Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Case sensitive parameters 18861 #26287

Closed

Conversation

KevRitchie
Copy link
Contributor

@KevRitchie KevRitchie commented Oct 8, 2021

  • I've read the guidelines for contributing and seen the walkthrough

  • I've posted a comment on an issue with a detailed description of how I am planning to contribute and got approval from a member of the team

  • The code builds and tests pass locally (also verified by our automated build checks)

  • Commit messages follow this format:

      Summary of the changes
    
      - Check SqliteParameterCollection for parameters that already exist ignoring casing
      - Update Bind to check for parameters regardless of casing
      - Add test for case-insensitive parameter binding
    

Fixes #18861

  • Tests for the changes have been added (for bug fixes / features)
  • Code follows the same patterns and style as existing code in this repo

@dnfadmin
Copy link

dnfadmin commented Oct 8, 2021

CLA assistant check
All CLA requirements met.

@smitpatel smitpatel changed the base branch from release/6.0 to main October 8, 2021 21:04
@smitpatel smitpatel requested a review from dougbu as a code owner October 8, 2021 21:04
@smitpatel
Copy link
Contributor

release/6.0 branch is closed for commits unless critical ship blockers. Please rebase your branch on top main branch.

Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

@KevRitchie I've posted a general comment on the perf consequences of this change (apologies, I should have posted that earlier).

@KevRitchie
Copy link
Contributor Author

@roji makes perfect sense. It would be best to keep everything moving in the same direction.

KevRitchie and others added 10 commits October 8, 2021 23:27
…1006.13 (dotnet#26267)

[release/6.0] Update dependencies from dotnet/runtime
…1006.17 (dotnet#26268)

[release/6.0] Update dependencies from dotnet/runtime
…1006.22 (dotnet#26270)

[release/6.0] Update dependencies from dotnet/runtime
…1007.5 (dotnet#26276)

[release/6.0] Update dependencies from dotnet/runtime
…1007.10 (dotnet#26278)

[release/6.0] Update dependencies from dotnet/runtime
…1007.16 (dotnet#26279)

[release/6.0] Update dependencies from dotnet/runtime
@bricelam
Copy link
Contributor

Finally circling back to this...

@roji your comment about perf doesn't apply in this situation. The .NET parameter collection object would remain case-sensitive. You need to check for a parameter using the exact case it was added in.

This just adds a fallback during execution where, if the parameter fails to bind to the SQL, we'll try a case-insensitive match instead. Perf isn't affected for parameters specified using the exact case. This just allows what is currently a negative scenario to succeed if the SQL happens to contain a different case. This is similar to the code added in #24588 for data readers where, only if we fail to find an exact match for the column, we'll fallback to a case-insensitive match.

I think we should move forward with this change.

foreach (var parameter in _parameters)
{
if (parameter.Bind(stmt))
Copy link
Contributor

Choose a reason for hiding this comment

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

This code shouldn't require any changes since parameter.Bind will return true if any match was found.

We do however have to update GetStatements.

Copy link
Contributor Author

@KevRitchie KevRitchie Jun 9, 2022

Choose a reason for hiding this comment

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

I've reverted the code back.

Is this the GetStatements method in SqliteCommand?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, for the error message that says which parameters weren't bound. Today, it just does a case-sensitive check:

var name = sqlite3_bind_parameter_name(stmt, i).utf8_to_string();
if (_parameters != null
&& !_parameters.Cast<SqliteParameter>().Any(p => p.ParameterName == name))

}
}
}

if (index == 0
&& (index = FindPrefixedParameter(stmt)) == 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also add a similar case-insensitive fallback for FindPrefixedParameter.

Comment on lines +442 to +443
command.CommandText = "SELECT @param";
command.Parameters.AddWithValue("@Param", "harvest");
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs more coverage:

SQL Parameter name(s) Expected Result
SELECT $param Param Adds prefix and binds using a case-insensitive match
SELECT $param, $Param $param and $Param Both parameters are bound using case-sensitive matches
SELECT $param, $Param $Param and then $param Both parameters are bound using case-sensitive matches. Order doesn't matter
SELECT $param, $PARAM $Param Throws ambiguous parameter exception
SELECT $param, $PARAM Param Throws ambiguous parameter exception

Copy link
Contributor

@bricelam bricelam Jan 25, 2022

Choose a reason for hiding this comment

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

Also make sure the new GetStatements code gets covered.

@roji
Copy link
Member

roji commented Jan 25, 2022

@bricelam if I'm understanding you correctly, you're saying that operations on SqliteParameterCollection would be case-sensitive only (so e.g. Contains for name foo would return false when there's a parameter with name FOO), but the matching of placeholders in the SQL (e.g. @FOO) would bind in a case-insensitive way (so @foo in the SQL would successfully bind to a parameter with FOO)?

If I've understood correctly, isn't this behavioral discrepancy problematic? I'd expect the same behavior to apply to both operations on SqliteParameterCollection and in placeholder binding (i.e. the name should either be case-sensitive or case-insensitive, but not sometimes one and sometime the other)?

@ajcvickers
Copy link
Contributor

@bricelam Agree this seems reasonable to move forward with.

@roji Not sure why those things need to behave the same. SqliteParameterCollection doesn't have a behavior that works when the generated SQL does. However, the generated SQL is not limited by a limitation in the specific ways SqliteParameterCollection can be used.

@roji
Copy link
Member

roji commented Jan 26, 2022

Not sure why those things need to behave the same.

Simply because both are about whether the parameter name is treated in a case-sensitive way or not? At the very least, it seems confusing to have two opposite behaviors around the same thing.

Put another way... Why exactly do we feel it's important to add the case-insensitive fallback for placeholder binding? I'm generally not a fan of lax/fuzzy/case-insensitive logic, but if do for some reason, why is that reason not equally valid for SqliteParameterCollection?

@bricelam
Copy link
Contributor

bricelam commented Jan 28, 2022

To use Dapper as an example, I think a lot of people could conceivably write this code and expect it to just work:

var post = connection
    .Query<Post>(
        "select * from posts where id = @id",
        new { Id = 1 })
    .FirstOrDefault();

They aren't really thinking about how the case of the C# anonymous type's property and the SQL parameter need to match up.

@roji
Copy link
Member

roji commented Jan 29, 2022

@bricelam yeah, I get that. I occasionally also get requests from people wanting to assign a string value to an NpgsqlDbType.Integer parameter, with Npgsql doing an implicit conversion - and tell them that I think it's a bad idea.

I guess I'm just a strictly-typed, case-sensitive kind of guy, just like C# :) I don't think it's a big deal for users to make their Dapper placeholders match the actual parameters; it also removes the ambiguity around what happens if there are two placeholders with the same case. I'd love for Npgsql to be in the same place as SQLite is now (fully case-sensitive), but an attempt to do that for 6.0 backfired pretty badly. I also specifically find it weird for the parameter collection to behave in one way, and for actual placeholder binding to behave in another way.

I've said my piece, if you guys still think it's important to do this (the issue only has two votes!), then that's fine by me...

@bricelam
Copy link
Contributor

bricelam commented Jun 7, 2022

@KevRitchie I'm still happy to take this change. Are you still interested in addressing the comments from my review?

@KevRitchie
Copy link
Contributor Author

@bricelam sorry for not picking this up sooner. Things have been a little hectic. I'm still interested. How soon do you need it turned around?

@bricelam
Copy link
Contributor

bricelam commented Jun 7, 2022

No rush. I was just checking in. We usually try to be feature-complete for a release around mid-August. But even if it's not done by then, it'll just go into the next release it's ready for.

@ajcvickers
Copy link
Contributor

Note from triage: closing this for now due to lack of activity. Happy to re-open if work resumes.

@ajcvickers ajcvickers closed this Mar 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Case-sensitive parameter names
7 participants