Skip to content

⭐ Improve parameterized query support - fixes #793#794

Merged
AzisK merged 5 commits into
kayak:masterfrom
mvanderlee:mvanderlee/parametized_query
Oct 21, 2024
Merged

⭐ Improve parameterized query support - fixes #793#794
AzisK merged 5 commits into
kayak:masterfrom
mvanderlee:mvanderlee/parametized_query

Conversation

@mvanderlee

@mvanderlee mvanderlee commented Feb 22, 2024

Copy link
Copy Markdown
Contributor

Fixes #793
The implemented tests speak for themselves, but here is an extract:

subquery = (
    Query.from_(self.table_efg)
    .select(self.table_efg.fiz, self.table_efg.buz)
    .where(self.table_efg.buz == 'buz')
)

q = (
    Query.from_(self.table_abc)
    .join(subquery)
    .on(self.table_abc.bar == subquery.buz)
    .select(self.table_abc.foo, subquery.fiz)
    .where(self.table_abc.bar == 'bar')
)

parameter = NamedParameter()
sql = q.get_sql(parameter=parameter)
self.assertEqual(
    'SELECT "abc"."foo","sq0"."fiz" FROM "abc" JOIN (SELECT "fiz","buz" FROM "efg" WHERE "buz"=:param1)'
    ' "sq0" ON "abc"."bar"="sq0"."buz" WHERE "abc"."bar"=:param2',
    sql,
)
self.assertEqual({'param1': 'buz', 'param2': 'bar'}, parameter.get_parameters())

This would allow the user to easily plug into SQLAlchemy for example:

parameter = NamedParameter()
session.execute(text(q.get_sql(parameter=parameter)), **parameter.get_parameters())

@mvanderlee mvanderlee requested a review from a team as a code owner February 22, 2024 00:13
@mvanderlee mvanderlee mentioned this pull request Feb 22, 2024
Comment thread pypika/terms.py Outdated
@property
def placeholder(self):
if callable(self._placeholder):
return self._placeholder(None)

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.

how does this work on None?

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.

callable Returns False
image

But I presume you mean how will the self_placeholder(None) work with None.
Good point, it wouldn't atm with the default generators.

I will fix it now.

@williambdean

Copy link
Copy Markdown
Contributor

Thanks for the PR! Will take a deeper look later

@coveralls

coveralls commented Feb 28, 2024

Copy link
Copy Markdown

Coverage Status

coverage: 98.392% (-0.006%) from 98.398%
when pulling 9ba1241 on mvanderlee:mvanderlee/parametized_query
into 53a77eb on kayak:master.

@mvanderlee

Copy link
Copy Markdown
Contributor Author

Thanks for the PR! Will take a deeper look later

@wd60622 Do you think you'll have time this week?

@williambdean williambdean 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.

Looks really good.
Just some comments on type hints, raising errors, and combining logic

Comment thread pypika/terms.py Outdated
Comment thread pypika/terms.py Outdated
Comment thread pypika/terms.py
class Parameter(Term):
is_aggregate = None

def __init__(self, placeholder: Union[str, int]) -> None:

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.

Type hint here doesn't include callable? Is that correct still?

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.

Correct, only ListParameter and DictParameter (and classes that inherit from them) support callable.
The base parameter does not.

Comment thread pypika/terms.py
def __init__(self, placeholder: Union[str, int]) -> None:
super().__init__()
self.placeholder = placeholder
self._placeholder = placeholder

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.

Might be good to use a setter here. With that, implementing a check for the correct type can happen on initialization if put in the setter

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'm not sure I follow how to do this pythonically? Could you share some pseudo code to show what you mean?

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.

@mvanderlee I believe @wd60622 was referring to the following:

@property
def placeholder(self):
      return self._placeholder

@placeholder.setter
def placeholder(self, placeholder):
     # can add checks here if needed
     self._placeholder = placeholder

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.

Right, which adds no value and isn't pythonic. This isn't Java/C#.

It would also break parameters. After a parameter has been created and used, any changes to the placeholder will break queries. Which is why I've made it private with a getter only.

I haven't changed the type, so any type checks would have to be part of a separate PR.

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.

I would disagree with you on it not being Pythonic, as it’s a native language feature.

Also, I was trying to be helpful, but given your receptiveness to discussion and tone, I won’t proceed.

Good luck.

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.

Sorry for my tone @danielenricocahall it'd been a rough week and I lashed out.

As for pythonic. This video does a good job at explaining language feature vs pythonic.
Raymond Hettinger - Beyond PEP 8 -- Best practices for beautiful intelligible code - https://www.youtube.com/watch?v=wf-BqAjZb8M

@williambdean

Copy link
Copy Markdown
Contributor

Do you have any thoughts as well? @AzisK

@mvanderlee

Copy link
Copy Markdown
Contributor Author

@wd60622 @AzisK any update on this?
It should go hand in hand with #792

@AzisK

AzisK commented Apr 26, 2024

Copy link
Copy Markdown
Contributor

Could we also have this functionality described in pypika docs?

@AzisK

AzisK commented Apr 26, 2024

Copy link
Copy Markdown
Contributor

The tests look really nice. What about adding PyformatParameter in the tests as well?

@reasv

reasv commented Sep 7, 2024

Copy link
Copy Markdown

I just wanted to say that I'm going to fork your branch because this feature is really useful, and I'm using it in my own project to help me build a custom high level query language: https://github.com/reasv/panoptikon/blob/master/src/panoptikon/db/pql/build_query.py

I'm resorting to making my own fork of PyPika and merging a bunch of these PRs because it doesn't seem PyPika is getting much merged lately and there are open bugs that otherwise block development: d7a4054

@larsschwegmann

Copy link
Copy Markdown
Contributor

Any progress on getting this merged? :)

@AzisK

AzisK commented Sep 27, 2024

Copy link
Copy Markdown
Contributor

Hello, I asked @mvanderlee for a test for PyformatParameter and I believe it would be good to have it. Otherwise I could merge this PR

@larsschwegmann

larsschwegmann commented Sep 27, 2024

Copy link
Copy Markdown
Contributor

I forked @mvanderlee 's fork and added a test for PyformatParameter: Commit

@AzisK

AzisK commented Sep 27, 2024

Copy link
Copy Markdown
Contributor

@larsschwegmann could you make a PR? Is it better for me to push this change to this commit? Probably

@larsschwegmann

Copy link
Copy Markdown
Contributor

If it's possible, I guess it would be best if you could just add the commit to this PR, just for the sake of documentation. Then you'll still have all the discussion in the PR that was merged 😄
If not, I can create a PR. Let me know what works best for you.

@mvanderlee

Copy link
Copy Markdown
Contributor Author

Thank you @larsschwegmann I've added it to the branch!

@AzisK

AzisK commented Oct 10, 2024

Copy link
Copy Markdown
Contributor

I could merge but linting fails. It's not a big deal but it would be good to lint before. Also unit tests for other Python versions fail but it's probably a bug of Github actions which is super annoying. It seems that they fail due to a race condition

@mvanderlee

Copy link
Copy Markdown
Contributor Author

@AzisK I'll run the linter, but it's an issue in the master branch. The linter is failing on lines that were not altered by this PR. I see the same errors with other PRs.

@mvanderlee mvanderlee force-pushed the mvanderlee/parametized_query branch from 2ae30df to 9ba1241 Compare October 11, 2024 08:15
@mvanderlee

Copy link
Copy Markdown
Contributor Author

The affected code wasn't in this fork yet, so I rebased in order to fix it.

@AzisK

AzisK commented Oct 21, 2024

Copy link
Copy Markdown
Contributor

All seems good, I can merge it. Thanks for the work

@AzisK AzisK merged commit 4072bfb into kayak:master Oct 21, 2024
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.

Improved parameter support

7 participants