Repository navigation
Quote Update SET columns as identifiers and bind them under valid placeholder names - #238
Open
simon-mundy wants to merge 1 commit into
Open
simon-mundy wants to merge 1 commit into
simon-mundy wants to merge 1 commit into
Conversation
…ceholder names Update rendered each SET column through quoteIdentifierInFragment(), which splits on every non-word character, so a column such as x-y rendered as "x""-""y". SET targets are now quoted as an identifier chain split only on the table-qualifier dot. Update and Insert bound scalar values under a parameter named after the column, producing invalid placeholders such as :film.title for non-PDO drivers. Non-word characters are now replaced with underscores and the name is suffixed when it collides with another column's name. Plain names and PDO's c_N names are unchanged.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Updaterenders SET column names as free-form SQL fragments instead of identifiers. That causes two problems:set(['x-y' => 1])renders"x""-""y" = …,set(['a b' => 1])renders"a" "b" = …, andset(['c"d' => 1])renders"c""'""d" = ….film.titlebinds as:film.title. Any other non-word character has the same effect. Those are not valid named placeholders on most drivers.Inserthas the same parameter-name problem in its VALUES list (:film.title,:x-y). It does not have the quoting problem.Cause
Update::processSet()resolves each SET column throughresolveColumnValue(['isIdentifier' => true]). That ends inquoteIdentifierInFragment(), which splits on every non-word character and quotes each piece.Update::processSet()andInsert::processInsert()both use the raw column name as the parameter name for non-PDO drivers.Fix
Updatenow quotes the SET target as an identifier chain. It splits only on the table-qualifier., quotes each part with the platform'squoteIdentifier(), and joins the parts withgetIdentifierSeparator(). This keeps the platform's own quote characters, its embedded-quote escaping and itsquoteIdentifiersswitch.AbstractSql::resolveParameterNames()maps each column to a valid placeholder name:[a-zA-Z0-9_]are kept as they are._._1,_2, …) is added when the result would collide with another column's name. Plain names are reserved first, so they never change, whatever the column order.c_Nnames are unchanged.Behaviour change (non-plain names only)
Updateset(['x-y' => …])"x""-""y""x-y"Updateset(['a b' => …])"a" "b""a b"Updateset(['c"d' => …])"c""'""d""c'd"(the SQL-92 platform's ownquoteIdentifier()escaping)film.title/x-y/a b(non-PDO):film.title/:x-y/:a b:film_title/:x_y/:a_bset(['a.b' => …, 'a_b' => …]):a_b_1and:a_bPlain and dotted SET columns render exactly as before (
"title","film"."title"). Insert column quoting is unchanged.Tests
test/unit/Sql/UpdateColumnNameTest.phpandtest/unit/Sql/InsertColumnNameTest.php, both#[Group('unit')]. Data providers cover plain, dotted, hyphen, space, quote-character and colliding names, with assertions on:getNamedArray();c_Nnaming staying the same.The tests were written first and failed 18 cases before the fix; the plain cases already passed. Changed lines have 100% line coverage. The full unit suite,
mago format --check,mago lintandmago analyzeare clean.Follow-ups (not in this PR)
Insertquotes each column as a single identifier, sofilm.titlerenders"film.title", not"film"."title". Standard SQL doesn't allow qualified INSERT columns (MySQL does), so this is left alone here.quoteIdentifierTois'inAbstractPlatform(SQL-92) and in phpdb-sqlite'sAdapterPlatform. An embedded"therefore becomes', not the standard"". phpdb-pgsql ("") and phpdb-mysql (backtick, doubled) escape correctly.quoteIdentifierChain().AbstractPlatform::quoteIdentifierChain()hard-codes"with\"escaping and ignoresquoteIdentifiers. phpdb-mysql and phpdb-pgsql override it. phpdb-sqlite does not.quoteIdentifierFragmentPatternalready treats-as a word character, sox-yrendered correctly there before this change. Spaces and quotes did not.processSet()orprocessInsert(). mysqli (?) and pgsql ($N) ignore the parameter name in the SQL; only the ParameterContainer key changes. Their PDO drivers keepc_N.