Skip to content

fix: substitute placeholders near non-comment -- and executable comments#31

Open
spokodev wants to merge 2 commits into
mysqljs:mainfrom
spokodev:fix/comment-detection-placeholders
Open

fix: substitute placeholders near non-comment -- and executable comments#31
spokodev wants to merge 2 commits into
mysqljs:mainfrom
spokodev:fix/comment-detection-placeholders

Conversation

@spokodev

Copy link
Copy Markdown

Bug

format() drops or shifts ? placeholders in two cases that MySQL parses differently from how skipSqlContext treats them:

format('SELECT 1--2, ?', ['VAL']);
// actual:   "SELECT 1--2, ?"            (placeholder dropped)
// expected: "SELECT 1--2, 'VAL'"

format('SELECT /*!40101 ? */ , ?', ['A', 'B']);
// actual:   "SELECT /*!40101 ? */ , 'A'"   (value shifted into the wrong slot)
// expected: "SELECT /*!40101 'A' */ , 'B'"

sqlstring returns the expected results for both. This is a correctness / data-integrity issue, not a quoting/injection one: the values stay quoted, but they land in the wrong slot or get silently dropped.

Why MySQL parses these as live placeholders

Per the MySQL reference manual, §9.7 Comments:

  • -- begins a comment only when the two dashes are followed by a whitespace (or control) character, or end of input. So 1--2 is not a comment — it is 1 minus -2.
  • /*! ... */ is an executable comment: MySQL runs the code inside it (used for version-gated SQL). /*+ ... */ is an optimizer hint, also evaluated. A ? inside either is a real placeholder.

Root cause

skipSqlContext (src/index.ts) detects:

  • -- with no whitespace-suffix check, so it skips from 1--2 to end of line and swallows the trailing placeholder.
  • /* */ without excluding /*! and /*+, so it skips over placeholders inside executable comments / optimizer hints.

This comes from the comment-aware skipping layer added in #22 (which correctly fixed -- and /* */ sequences inside backtick identifiers). This PR narrows that skip to actual comments.

Fix

  • -- branch: only treat as a comment when the character after the two dashes is whitespace/control or end of input; otherwise it is an operator sequence and is not skipped.
  • /* */ branch: return early (do not skip) when the marker is /*! or /*+, so the placeholder inside is substituted normally.

Regular -- (with trailing whitespace) and regular /* */ comments are still skipped, so the behavior from #22 is preserved.

Tests

Added three cases to test/delimiters.test.ts under Query comments for debugging. They fail on main and pass with the fix; the full suite stays green across the 9 test files.

input before after
SELECT 1--2, ? SELECT 1--2, ? SELECT 1--2, 'VAL'
SELECT /*!40101 ? */ , ? SELECT /*!40101 ? */ , 'A' SELECT /*!40101 'A' */ , 'B'
SELECT /*+ MAX_EXECUTION_TIME(?) */ id FROM t skipped SELECT /*+ MAX_EXECUTION_TIME(1000) */ id FROM t

…ments

`skipSqlContext` treated every `--` as a line comment and every `/* */` as a
regular comment, so placeholders were dropped or shifted in two cases MySQL
parses differently:

- `--` only starts a comment when followed by whitespace/control/EOF, so
  `format("SELECT 1--2, ?", ["VAL"])` returned `"SELECT 1--2, ?"` (placeholder
  dropped) instead of `"SELECT 1--2, 'VAL'"`.
- `/*! ... */` and `/*+ ... */` are executable comments / optimizer hints that
  MySQL evaluates, so a `?` inside them is a live placeholder. `format("SELECT
  /*!40101 ? */ , ?", ["A", "B"])` returned `"SELECT /*!40101 ? */ , 'A'"` (value
  shifted to the wrong slot) instead of `"SELECT /*!40101 'A' */ , 'B'"`.

Require a whitespace/control/EOF suffix before skipping `--`, and exclude
`/*!`/`/*+` from the block-comment skip. Regular `--` and `/* */` comments are
still skipped, matching the comment-aware behavior added in mysqljs#22.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Jun 25, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (2f8c077) to head (8ae49df).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #31   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines          401       410    +9     
=========================================
+ Hits           401       410    +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/index.ts
// MySQL starts a `--` comment only when the dashes are followed by a
// whitespace/control character (or end of input); `1--2` is an operator
// sequence, not a comment, so it must not swallow following placeholders.
const afterDash = sql.charCodeAt(position + 2);

@wellwelwel wellwelwel Jun 25, 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.

Thanks, @spokodev! I believe we can remove 100% of the need for any comments in the entire implementation, since the logic and variable names are already clear for long-term maintenance 🙋🏻‍♂️

Comment thread test/delimiters.test.ts Outdated
});

test('double dash without trailing whitespace is not a comment', () => {
// MySQL only treats `--` as a comment when followed by whitespace/control;

@wellwelwel wellwelwel Jun 25, 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.

The same idea applies to tests, where instead of loose comments, we can use test, describe, it, and the assertions themselves to maintain clarity of what is being tested.

The variable names and test descriptions already convey intent, so the inline
comments in the implementation and tests are redundant. Logic and assertions
are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@spokodev

Copy link
Copy Markdown
Author

Done, dropped the comments in both the implementation and the tests. Logic and assertions are unchanged, suite stays green.

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.

3 participants