Skip to content

split set_type_context: strict + update#438

Open
pyshx wants to merge 4 commits intovillagesql:mainfrom
pyshx:pyshx/update-type-context
Open

split set_type_context: strict + update#438
pyshx wants to merge 4 commits intovillagesql:mainfrom
pyshx:pyshx/update-type-context

Conversation

@pyshx
Copy link
Copy Markdown
Contributor

@pyshx pyshx commented May 6, 2026

Description

  • Add update_type_context() to Field (sql/field.h) and Item (sql/item.h) for sites that deliberately overwrite an existing TypeContext.
  • set_type_context() now asserts custom_type == nullptr via should_assert_if_false (villagesql/include/error.h): debug aborts, release silently overwrites — the safe-fallback shape from [Server]: Introduce update_type_context to supplement set_type_context #409.
  • Migrate 10 re-set call sites to update_type_context(). No SQL-level behavior change.

Related Issue

Closes #409 (approach 2 from the issue thread). Bodies of set_type_context() are now out-of-line in sql/field.cc / sql/item.cc to keep error.h (transitively pulls component log_builtins) out of sql/field.h / sql/item.h.

How was this tested?

Checklist

  • I have signed the CLA
  • I have read CONTRIBUTING.md
  • I have added or updated tests as appropriate
  • My changes maintain compatibility with upstream MySQL 8.4

@pyshx
Copy link
Copy Markdown
Contributor Author

pyshx commented May 6, 2026

Re-set sites

3 from the call-site analysis on #409:

  • villagesql/types/util.cc:1352 — TD1 unknown→known
  • sql/item_func.cc:7024Item_func_get_user_var::propagate_type
  • sql/item.h:6980Item_cache::setup

7 surfaced via the debug-build assert pass (they're post-factory sync points):

Site Pre-setter
sql/item.cc:3078 Item_field::set_field fix_fields re-runs (prepared-stmt)
sql/sp_head.cc:1989 sp_head::create_result_field make_field()
sql/sql_tmp_table.cc:298 Item_sum::make_string_field
sql/sql_tmp_table.cc:441 Field::new_field (clone)
villagesql/types/util.cc:1185 insert_tmp_metadata_for_thd make_field() upstream
villagesql/types/util.cc:1604, 1615 InjectCustomSpParams SP frame setup

pyshx added 4 commits May 6, 2026 18:28
Companion to set_type_context() for sites that deliberately overwrite
an existing TypeContext (e.g. the TD1 unknown -> known resolution path).
Identical body for now; set_type_context() will gain a strict-null
precondition assert in a follow-up commit, which is what makes the two
names meaningful.

Refs villagesql#409.
Three sites that deliberately overwrite an existing TypeContext now
call update_type_context() instead of set_type_context(), expressing
intent in the call:

- villagesql/types/util.cc:1352 - TD1 unknown -> known resolution
- sql/item_func.cc:7024 - Item_func_get_user_var::propagate_type
  (type propagation iterates; TC may be set from a prior pass)
- sql/item.h:6980 - Item_cache::setup (Item_cache is reused in plan-
  reset paths in MySQL core)

Behavior unchanged - both setters have identical bodies until
set_type_context() gains a strict-null precondition in a follow-up.

Refs villagesql#409.
Audit pass identified four more sites that re-set an existing
TypeContext rather than first-setting on a fresh Field/Item:

- sql/item.cc:3078 - Item_field::set_field, called from fix_fields
  which re-runs across prepared-statement re-executions
- sql/sp_head.cc:1989 - sp_head::create_result_field, after make_field()
  has already pre-set TC from m_return_field_def
- sql/sql_tmp_table.cc:298, 441 - post-factory TC sync sites; the
  factory paths (Item_sum::make_string_field, Field::new_field clone)
  pre-set TC, so these re-set rather than first-set
- villagesql/types/util.cc:1185 (insert_tmp_metadata_for_thd),
  villagesql/types/util.cc:1604, 1615 (InjectCustomSpParams) - sync
  the shared_ptr-owned TC ref into Fields/Items that already hold the
  raw-pointer version from upstream setup

Behavior unchanged - both setters still have identical bodies. The
strict-null precondition lands in the next commit.

Refs villagesql#409.
set_type_context now asserts custom_type == nullptr via
should_assert_if_false (villagesql/include/error.h): debug builds
abort on violation, release builds silently overwrite as the safe
fallback per the maintainer's framing in villagesql#409.

Moved the bodies out-of-line into sql/field.cc and sql/item.cc to
keep the heavyweight error.h include (which pulls the component
log_builtins) out of sql/field.h and sql/item.h.

The assert reflects the call-site-intent split between set_ and
update_type_context: callers that have already migrated to update_
preserve their existing-TC overwrite semantics; the remaining
set_ callers are the first-set sites identified by the call-site
audit posted on villagesql#409.

Closes villagesql#409.
@pyshx pyshx force-pushed the pyshx/update-type-context branch from 8251c5b to b29a323 Compare May 6, 2026 13:17
@villagestevers villagestevers self-requested a review May 6, 2026 13:57
Comment thread sql/field.cc
void Field::add_to_cost(CostOfItem *cost) const { cost->AddFieldCost(); }

void Field::set_type_context(const villagesql::TypeContext *tc) {
(void)should_assert_if_false(custom_type == nullptr);
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.

It's fine to make this assert(custom_type) because the fall through is the behavior we want for release mode. Same here - let's make this assert. This should also still be in the header file. It should effectively be inline.

Comment thread sql/item.cc
}

void Item::set_type_context(const villagesql::TypeContext *tc) {
(void)should_assert_if_false(custom_type == nullptr);
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.

Same here

Comment thread sql/item.h
// VillageSQL: Copy custom type context for proper formatting of custom
// types
set_type_context(item->get_type_context());
update_type_context(item->get_type_context());
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 do we know this is the right call here? Are we expecting it to have a value already, or are we unsure and thus hedging?

Comment thread sql/item_func.cc
collation.set(var_entry->collation);

set_type_context(var_entry->type_context());
update_type_context(var_entry->type_context());
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.

Same here

Comment thread sql/item.h
}
void set_type_context(const villagesql::TypeContext *tc) { custom_type = tc; }
void set_type_context(const villagesql::TypeContext *tc);
void update_type_context(const villagesql::TypeContext *tc) {
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.

Do we intend update to mean custom_type already had a value? If so, should we assert that?

How would someone deal with knowing whether to call one or the other without checking first? If update doesn't assert, then why ever call set_ ?

I wonder if adding such an assert would result in test failures after this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Server]: Introduce update_type_context to supplement set_type_context

2 participants