Skip to content

Spec: Add spec for expressions#16652

Merged
rdblue merged 7 commits into
apache:mainfrom
rdblue:add-expression-spec
Jun 29, 2026
Merged

Spec: Add spec for expressions#16652
rdblue merged 7 commits into
apache:mainfrom
rdblue:add-expression-spec

Conversation

@rdblue

@rdblue rdblue commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

This adds a spec for expressions, which was proposed in the Extending Iceberg Expressions design doc.

@github-actions github-actions Bot added the Specification Issues that may introduce spec changes. label Jun 2, 2026
@singhpk234 singhpk234 self-requested a review June 2, 2026 16:22

@singhpk234 singhpk234 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome ! Thanks @rdblue !

Comment thread format/expressions-spec.md
Comment on lines +147 to +148
| `IS NULL` | any | | true iff the value is null |
| `IS NOT NULL` | any | | true iff the value is not null |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: may be we should link supported iceberg data tyes in the spec

Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
A constant or literal is the simplest type of value expression that represents a specific typed value.


#### Field reference

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In looking around, other systems have two other reference types that I want to callout (though I don't necessarily think we want to include, but should consider):

  1. Positional References (for row-like references)
  2. Subscripted References (indexing into arrays)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For subscripted references, we can use a well-defined function, like get_item. I don't think that we want to have this in the spec since it fits cleanly into a function. And that would also requiring choosing either 1-based or 0-based indexing. With functions, we can model both: get_item_the_right_way for 0-based, get_item_sql for 1-based. 😄

I don't think that we want to include positional references.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread format/expressions-spec.md Outdated

A value expression's type is determined when it is bound to a specific input schema.

If types are incompatible at runtime, implementations binding or evaluating expressions may apply type promotion to align types for predicates and to resolve functions. Implementations may choose when to promote values to accomodate engines that differ in casting behavior. However, implementations must fail rather than insert "unsafe" casts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always implicitly handled by the engine? Do we consider CAST(...) something that would fall under a sql_functions.cast or should we leave it out entirely.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that sql_functions.cast can be used for explicit casts, but I want to avoid adding a cast definition in this spec for the expression structure.

Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md

This approach is intended to keep focus on the logical structure of expressions. Complexity is pushed to the functions that are called, which can be a limited set of well-defined and portable functions (like Iceberg partition transforms) or could be user-defined functions that can use the full range of SQL capabilities. Multi-dialect UDFs are responsible for any SQL constructs that are specific to an engine, rather than importing and duplicating dialects in Iceberg expressions.

This is consistent with Iceberg's conservative approach in other specs. Expressions and predicates are an important part of Iceberg implementation APIs, but have been deliberately limited in specifications. For example, sort orders and partition fields are strictly limited to a small set of transforms over well-defined inputs (source field IDs). This spec is widening what can be expressed, but depends on function calls for complex tasks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a "why this is written in the way it is" than a "what this spec is about". Just wondering if we need this paragraph in the text.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I can remove it if you think it isn't useful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal would be the Spec is just what would be needed to implement expressions correctly. So I would remove or maybe just start a whole new doc about Iceberg's core values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah not strongly opinionated here but I find this to just be extra stuff that isn't super valuable in the context of the spec itself. A separate core values or design principles would be valuable though.

Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated

Engines may document and use a catalog name to identify their built-in functions that are not part of the SQL spec, like `spark_builtin_functions.to_utc_timestamp`.

Producers are responsible for resolving catalog, namespace, and name if the environment is relevant. For example, if a SQL engine uses its current catalog and namespace to find a function, the resolved catalog and namespace must be used to produce an unambiguous function identifier.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this statement either. Is this just saying that a engine is allowed to resolve an identifier however it likes as long as it would be doing so unambigously?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified by adding:

Function identifiers are unambiguous and are not interpreted using session context.

What I am trying to say is that identifiers are fully-qualified and identify a single object. In a SQL query, I could use a function like parse_device_id, which is resolved using my session's current catalog and namespace to prod.website.parse_device_id. The identifier when referencing that function must be {"catalog": "prod", "namespace": ["website"], "name": "parse_device_id"}. The identifier is not dependent on session context.

I should also note that "catalog" could be omitted to use the catalog where the referencing object is stored.

Comment thread format/expressions-spec.md Outdated

A value expression's type is determined when it is bound to a specific input schema.

If types are incompatible at runtime, implementations binding or evaluating expressions may apply type promotion to align types for predicates and to resolve functions. Implementations may choose when to promote values to accomodate engines that differ in casting behavior. However, implementations must fail rather than insert "unsafe" casts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the intent here but adding "unsafe" feels ambiguous here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used unsafe in the UDF spec as well:

Require explicit casts for unsafe or non-obvious conversions.

I'll remove the quotes because that makes this more confusing. We do delegate to engines what casts they want to produce, but I think it's fairly well understood in most cases.

Comment thread format/expressions-spec.md Outdated

Predicates can be constants (true or false), comparisons or tests of value expressions, or logical combinations of predicates (AND, OR, NOT).

If value expression types in a predicate are incompatible, implementations should align types using type promotion. For instance, `int_col > 5.0` should promote int values to float. If the types cannot be aligned according to type promotion rules, the predicate must evaluate to false. For instance, `"goats" > -Infinity` should always be `false`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we are again dancing around what it is a "safe" type promotion and what is allowed. The above examples make sense to me but for me the dangerous ones are always things like

2141041920 > '05-04-19` or whatnot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm trying to say is that implementations should fail the dangerous cases.

I get the argument that this is not exact, but we are delegating this responsibility to engines so that their rules are applied consistently. I think that most engines have well-defined lines between safe and unsafe casts. But there are some situations like promoting decimals to wider scale that could differ.

Comment thread format/expressions-spec.md Outdated
* `fixed` and `binary` use unsigned byte-wise comparison
* `string` uses unsigned byte-wise comparison of the UTF-8 representation
* `uuid` uses unsigned byte-wise comparison of the UUID bytes
* `float` and `double` use IEEE 754 total order after normalizing NaN to the canonical NaN (sign bit 0, exponent bits all 1, matissa msb 1 followed by all 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering why we don't want to follow the IEE standard here and do -Nan < Nan?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use NaN counts in stats because IEEE total ordering can easily destroy the effectiveness of lower/upper bounds with just one NaN value. Since we treat all NaN values as the same thing in our stats, I wanted to be consistent here and also push people to use IS NAN and NOT NAN predicates instead of = NaN.

I think we have to support NaN here; the non-total-order IEEE comparison causes a ton of headaches because NaN != NaN. But I wanted to keep the intrusion minimal. Plus, "negative not a number" is a thing because of the bit representation. And I don't think we want to keep signaling NaN here either, which would be a whole class of cases where apparent NaN < NaN is true or false, or just confusing because you don't display the mantissa.

Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
@amogh-jahagirdar amogh-jahagirdar self-requested a review June 9, 2026 20:15
Comment thread format/expressions-spec.md Outdated
* `fixed` and `binary` use unsigned byte-wise comparison
* `string` uses unsigned byte-wise comparison of the UTF-8 representation; it is not the Unicode Collation Algorithm
* `uuid` uses unsigned byte-wise comparison of the UUID bytes
* `float` and `double` use IEEE 754 total order after normalizing NaN to the canonical NaN (sign bit 0, exponent bits all 1, mantissa msb 1 followed by all 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I added a comment here before but I was wondering why we want the cannonical nan here when we don't use it this way in the Table Format spec (-Nan < Nan )

@rdblue rdblue Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original answer is here: #16652 (comment)

I'd probably rephrase that to make the logic a bit more clear though. First, table stats don't allow NaN values for lower or upper bounds. NaN values are counted and the bounds reflect all non-NaN values. That is because NaN values can easily destroy the effectiveness of stats, since any NaN value is greater than (or -NaN is less than) all other floating point values. I think that the rules here for expressions are as consistent with this as we can be.

For expressions, we want to handle NaN like null: we don't want wacky comparison behavior and we want everyone to use IS NaN and IS NOT NaN to explicitly specify how to handle NaN values.

What do I mean by "wacky comparison behavior"? In the IEEE 754 non-total-order case, NaN = NaN is false and comparisons of values with NaN are always false. You get the bad behavior where the order of comparison matters (a < NaN is false and NaN < a is false, but a is not NaN) and so NaN values can show up in either upper or lower bounds (and sometimes both!). IEEE 754 total order isn't much better: NaN = NaN is true if they have the same bits, but NaN < NaN can also be true.

These quirks are not obvious, which is why we typically want people to use the IS NaN and IS NOT NaN forms. However, just like we have to handle null values produced by functions, we also have to handle NaN values when they show up as the results of functions (like divide(a, b) for a=0.0 and b=0.0).

Because we can't completely eliminate NaN comparisons, we have to define how they work. I think the most predictable and understandable way is using the two rules just below: NaN = NaN is true for any two NaN values, and all non-NaN values are less than NaN.

One additional choice I made was not to handle -NaN and NaN separately. I think -NaN isn't very useful so I chose the simpler option to normalize all NaN values.

@rdblue rdblue Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking about this more and talking offline with @RussellSpitzer, we decided to update the wording so that NaN behavior is the same as null handling. This consistency makes both nominally incomparable cases behave the same way.

This could cause situations like those mentioned above where aggregations with NaN values behave strangely based on the order of operands. I think consistency is more important and that NaN handling should always be explicit using IS NaN, just like IS NULL is the correct way to handle null values. In addition, the use cases contemplated by this spec (for instance CHECK constraints) are unlikely to hit these cases.

@stevenzwu stevenzwu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple minor comments


Functions are referenced using a catalog and a function identifier (list of strings).

* The function identifier consists of 0 or more namespace names followed by the function name. At least one part, the function name, is required.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prose rule ("at least one part, the function name, is required") is not enforced by the grammar in Appendix B, which uses [ NAME* ] for all three structured FUNC_REF forms:

FUNC_REF: NAME
    | [ NAME* ]
    | { "identifier": [ NAME* ] }
    | { "catalog": NAME, "identifier": [ NAME* ] }

Use [ NAME+ ] (one-or-more) so the grammar matches the prose:

FUNC_REF: NAME
    | [ NAME+ ]
    | { "identifier": [ NAME+ ] }
    | { "catalog": NAME, "identifier": [ NAME+ ] }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a problem for the grammar to be wider than what we actually allow, but I think we can tighten it up if this is important.

Comment thread format/expressions-spec.md Outdated
@szehon-ho

szehon-ho commented Jun 25, 2026

Copy link
Copy Markdown
Member

missed a lot of the discussion, but well thought out. Was reading the predicate/expression part, the 'Value expressions are not valid predicates' is a nice way to reduce the chance for null ambiguity:

if_else(f(x), "A", "B") //ambiguious if f(x) is null (not allowed)
if_else(f(x) = true, "A", "B")

### Backward compatibility

```
DEPRECATED_PREDICATE:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about { "type": "true" } and { "type": "false" }?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this currently allowed?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrueExpression:
type: object
required:
- type
properties:
type:
$ref: '#/components/schemas/ExpressionType'
const: "true"
FalseExpression:
type: object
required:
- type
properties:
type:
$ref: '#/components/schemas/ExpressionType'
const: "false"

@manuzhang manuzhang Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a folllow-up PR #17120. Please help review.


Function calls may produce different types when function definitions change, and type changes may change the definition that is resolved for a function name. For example, if the input field passed to `identity(int) -> int` is promoted from `int` to `long`, the resolved `identity` function can change to `identity(long) -> long` if it is defined.

If types are incompatible at runtime, implementations binding or evaluating expressions may apply type promotion to align types for predicates and to resolve functions. Implementations may choose when to promote values to accommodate engines that differ in casting behavior. However, implementations must fail rather than insert unsafe casts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is type promotion here identical to iceberg type promotion rule? I assume no.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not. Type promotion is delegated to implementations: "Implementations may choose when to promote values to accommodate engines that differ in casting behavior"

* `sql_functions` is used for functions defined by the SQL standard
* `iceberg_functions` is used for functions defined in this specification

Engines may document and use a catalog name to identify their built-in functions that are not part of the SQL spec, like `spark_builtin_functions.to_utc_timestamp`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have the risk that a non-interchangeable engine-specific expression used by a Iceberg table would ruin its interoperability?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expressions are not engine-specific, but functions may be.

That said, there are cases where users want to store engine-specific functions and I think we have to support it. We are planning to implement this using a property to enable non-portable functions that are outside of the SQL and Iceberg sets. And even then, we don't expect all implementations to be able to apply all of the functions from the SQL spec. The principle we're applying here is to nudge toward interoperability and compatibility, but not force it and limit the effectiveness of the features.

Comment thread format/expressions-spec.md Outdated
* `fixed` and `binary` use unsigned byte-wise comparison
* `string` uses unsigned byte-wise comparison of the UTF-8 representation; it is not the Unicode Collation Algorithm
* `uuid` uses unsigned byte-wise comparison of the UUID bytes
* `decimal` uses signed comparison independent of scale; this is equivalent to comparison of unscaled values because type alignment produces values with the same scale

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a native speaker but the phrase "independent of scale" seems to contradict "with the same scale". This might confuse developers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is saying that type alignment produces the same scale, so they are equivalent.

Comment thread format/expressions-spec.md
Comment thread format/expressions-spec.md
Comment thread format/expressions-spec.md Outdated
Functions are referenced using a catalog and a function identifier (list of strings).

* The function identifier consists of 0 or more namespace names followed by the function name. At least one part, the function name, is required.
* Catalog is optional and is assumed to be the catalog in which the referencing object is stored if it is not present or is null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to require ommitting the catalog in those cases instead of having readers also to consider the explicit null case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. I think we don't want to need to later define how to handle a null catalog. I would rather just specify that they are the same thing.


* `if_else(condition: predicate, when_true: T, when_false: T) -> T`: returns the value of `when_true` when `condition` is true and `when_false` otherwise

### Partition transforms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does having partition transforms here as well as in the main spec put us in a position where any changes to these specs have to make 2 updates? Or are we planning on removing it from the main page as well?

Also on that note, I think we're missing multi-argument transforms here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the corresponding functions. I think we will want to find a good way to keep these in sync, but I didn't want to remove things from the table spec as part of this change. I do think it would be reasonable to move some of those definitions here.

For multi-arg transforms, that is purely a table spec concept. These are functions, some of which take two args, but these do not define source-id or source-ids. I think we will want to define the functions here and then map the transform serialization into arguments to these functions.

Value expressions can be one of three types: a constant value, a field reference, or a function applied to zero or more value expressions.


#### Constant values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very minor, we may just want to consistently use one term throughout this. I think everyone understands constants and literals are the same but since grammars tend to be pedantic, may just be better off sticking to one term.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale is that "constant" is more clear for the purpose of the spec, but "literal" is used in expression libraries. They are basically synonyms so I thought that it was fine to use both. It is okay for implementations to continue using "literal" for constants.

I like using "constant" because the reader knows what it is and how it interacts with functions, without bringing in the questions like "what literal forms are allowed?"

EXPR: LITERAL | REFERENCE | APPLY

LITERAL: VALUE
| { "type": "literal", "value": VALUE }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the new grammar do we still want to allow untyped literals? I understand we may need to do it for compatibility reasons but I was under the impression here we'd deprecate producing the untyped one (so DEPRECATED_LITERAL, and then the new one with data-type. I'm mostly a bit concerned about avoiding interpretability issues of literals and producing an explicit Iceberg type doesn't seem like a significant burden? Unless there are cases where it's not reasonably known?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine to do this, since the values are still going to be bound to align types at runtime.

Comment thread format/expressions-spec.md

@singhpk234 singhpk234 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @rdblue !


Field references may be named references (unbound) or ID references (bound). ID references identify a field by field ID from a schema. Named references identify a field by name that must be resolved to an ID (bound to a schema) to access the field.

ID references are used for stored expressions, where the identity of the column is determined when the stored expression is created. For example, column constraints are tied to field IDs so that renaming a column does not invalidate the reference in its stored constraint.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[orthogonal] : it would be nice to have IDReference in the rest yaml too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we will add this when we update the yaml.

The context in which an expression is used determines the type of references that are valid. Iceberg specifications should document whether ID references, named references, or both are allowed.


#### Apply function

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional: should we add a restriction that if engines is unable resolve the function, lets say the function deleted or no longer exists in catalog, MUST fail execution ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is dependent on the context and should be specified. For example, we state in the table spec that unknown partition transforms should be ignored, rather than causing reads to fail. That was added specifically to create the ability to add new partition transforms without breaking forward-compatibility. Adding a blanket rule here would conflict with those decisions to I think these cases should be decided by the context where an expression is used or stored.

Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md Outdated
Comment thread format/expressions-spec.md
@rdblue

rdblue commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

I'm going to commit this since the vote passed and I've addressed the latest comments, which were mostly minor clarifications or updates.

This can continue to evolve, and in particular it sounds like we may want to formalize the JSON grammar. Right now it is descriptive, but not exact. For example, @stevenzwu points out that identifiers that are "one or more" names are represented with [ NAME* ] instead of [ NAME+ ].

@rdblue rdblue merged commit a2fb64b into apache:main Jun 29, 2026
8 checks passed
jakelong95 pushed a commit to jakelong95/iceberg that referenced this pull request Jun 29, 2026
@nssalian nssalian added this to the Iceberg 1.12.0 milestone Jul 6, 2026
Kurtiscwright pushed a commit to Kurtiscwright/iceberg that referenced this pull request Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Specification Issues that may introduce spec changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.