sqlbind icon indicating copy to clipboard operation
sqlbind copied to clipboard

Function Decorator API

Open DavidBuchanan314 opened this issue 1 year ago • 1 comments

Hi, I have an idea for an alternate API which I think might be cleaner and less error-prone.

Here's a quick mockup:

class Binder:
	def __init__(self, params: list, value) -> None:
		self.params = params
		self.value = value
	
	def __str__(self):
		self.params.append(self.value)
		return "?"

def bound(fn):
	def wrapper(*args, **kwargs):
		params = []
		res = fn(
			*[Binder(params, arg) for arg in args],
			**{k: Binder(params, v) for k, v in kwargs.items()}
		)
		return res, params
	return wrapper

@bound
def make_me_a_query(foo: str, bar: int):
	return f"SELECT * FROM table WHERE field1 = {foo} AND field2 = {bar} OR field3 < {bar}"

print(make_me_a_query("abc", bar=123))
# ('SELECT * FROM table WHERE field1 = ? AND field2 = ? OR field3 < ?', ['abc', 123, 123])

The idea is to use a function decorator that replaces the args with magic "Binder" class instances, which record their values into the params list when the fstring tries to convert them into a string (which means the order is correct, etc.)

I haven't got my head around sqlbind's more advanced features yet, but I'll have a think about how they could be adapted to this sort of API.

To elaborate on "less error-prone" part, I believe it makes it harder to, for example forget the q/ prefix on a parameter and end up with sql injection.

DavidBuchanan314 avatar Feb 26 '24 20:02 DavidBuchanan314

You are so right about injection. The other thing it could easily pass tests. It bothers me a lot. I think about simple linter to check no parameters go into strings without some sort of binding. May be it could be an extension for bandit. Your idea is also viable approach. I need some time to toy with it and try to apply to existing code.

baverman avatar Feb 26 '24 23:02 baverman

Closing for now. A general solution requires https://peps.python.org/pep-0675/ to be widely adopted by type checkers. Or writing a typing plugin.

baverman avatar Dec 04 '24 07:12 baverman

By the way, I just came across this PEP, that might also be useful in the future: https://peps.python.org/pep-0750/

DavidBuchanan314 avatar Feb 07 '25 00:02 DavidBuchanan314

@DavidBuchanan314 yes, its a great PEP along side with a LiteralString. I'm waiting it's support for mypy and at least typing_extensions. It would allow to restrict accidental injections.

baverman avatar Feb 13 '25 18:02 baverman