Thanks Keith, I think you are right. I can enforce only using index-based or key-based placeholders and force the user to supply data as an array (indexed) or an object (keyed).
I think I was assuming I would allow treating index-based placeholders as keys {"?10": "data"}, which is where the "detect valid placeholders" need came from. Thanks for the help. On Mon, Jul 22, 2019 at 2:23 PM Keith Medcalf <kmedc...@dessus.com> wrote: > > I don't see what is so hard. APSW does it: > > >python > Python 2.7.16 (v2.7.16:413a49145e, Mar 4 2019, 01:30:55) [MSC v.1500 32 > bit (Intel)] on win32 > Type "help", "copyright", "credits" or "license" for more information. > >>> import apsw > >>> db = apsw.Connection('') > >>> db.execute('select ?, ?10;', ('one', 'ten')).fetchone() > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "C:\python\lib\site-packages\Local\newapsw.py", line 39, in execute > return self.cursor().execute(*args, **kwargs) > apsw.BindingsError: Incorrect number of bindings supplied. The current > statement uses 10 and there are 2 supplied. Current offset is 0 > >>> > > The statement required 10 parameters and only 2 were supplied. ERROR! > > Internally it uses sqlite3_bind_parameter_count to find out how many > parameters need binding and requires that the number of "parameters" passed > match the number of parameters "expected" when binding positionally. > > >>> db.execute('select :1, :10;', {'2': 'one', '10': 'ten'}).fetchone() > Row(_0=None, _1=u'ten') > >>> db.execute('select :1, :10;', {'1': 'one', '10': 'ten'}).fetchone() > Row(_0=u'one', _1=u'ten') > >>> > > When binding by name, it looks up the names in the provided dictionary and > binds those it finds. > > You can also bind named parameters to a positional list (in which case it > is the programmers job to keep track of what they are doing) since a named > parameter is merely syntactic sugar on top of positional parameters: > > >>> db.execute('select :1, :10;', ('one', 'two')).fetchone() > Row(_0=u'one', _1=u'two') > >>> > > > > It is not very difficult. You call sqlite3_bind_parameter_count. Then if > you are binding positionally you make sure there are sufficient positional > parameters provided to bind them all. It you are binding by name, you > cycle through the parameters, get the name, and then bind the given named > parameter to that parameter. apsw chooses to ignore missing items when > binding by name -- sounds like you simply want to ERROR instead ... > > The only issue I can see is that when requesting the name of an positional > parameter that has no name it returns null rather than the positional name, > however, this is pretty easy to work around in pretty much any programming > language ... that is if sqlite3_parameter_name(stmt, x) return null then > the name is a ? followed by x in decimal (sprintf("?%d", x) or thereabouts > having appropriate accomodations for the language syntax and buffer safety, > etc) > > However, you cannot bind to positional parameters by name: > > >>> db.execute('select ?1, ?10;', {'2': 'one', '10': 'ten'}).fetchone() > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "C:\python\lib\site-packages\Local\newapsw.py", line 39, in execute > return self.cursor().execute(*args, **kwargs) > apsw.BindingsError: Binding 1 has no name, but you supplied a dict (which > only has names). > > > *the builtin python sqlite3 wrapper does the same thing I expect (though I > have never actually looked) but it is somewhat braindead otherwise, so I > don't use it and haven't bothered to test what it does in these > circumstances. Though my expectation is that it would behave somewhat > similarly. > -- > The fact that there's a Highway to Hell but only a Stairway to Heaven says > a lot about anticipated traffic volume. > > > >-----Original Message----- > >From: sqlite-users [mailto:sqlite-users- > >boun...@mailinglists.sqlite.org] On Behalf Of test user > >Sent: Monday, 22 July, 2019 06:36 > >To: SQLite mailing list > >Subject: Re: [sqlite] [EXTERNAL] Determining valid statement > >placeholders. > > > >I understand the problem, but I want my library to be able to detect > >the > >problem programatically. > > > >Currently if a user mixes index-based and key-based placeholders, the > >only > >thing a library using SQLite can do us run the query with unbound > >placeholders set to null (the SQLite default). > > > >Id like the ability to instead throw an error in this case. > > > > > >I think SQLite internally knows how many placeholders are in the > >query at > >parse time. > > > >My question is how can I get the data via the API, or if it would be > >considered to add a function to get this data? > > > > > >On Mon, Jul 22, 2019 at 12:29 PM Keith Medcalf <kmedc...@dessus.com> > >wrote: > > > >> > >> On Monday, 22 July, 2019 04:34, Enzo <contact.enz...@gmail.com> > >wrote: > >> > >> >It is not the same information. > >> > >> >I want to be able to determine "has the user bound all placeholder > >> >values with data?". > >> > >> >The user provides this as input: > >> > >> >query="SELECT ?, ?10" > >> >data={"0": "data-a", "10": "data-b"} > >> > >> >Note: This IS valid, as they have provided all data for > >placeholders. > >> > >> >Using the APIs you mentioned: > >> > >> >count=10 > >> >name(10) = "?10" > >> > >> >So according to the API, the indexes [1, 2, 3, 4, 5, 6, 7, 8, 9, > >10] > >> >are placeholders in the query. > >> > >> >As you can see, only indexes [1, 10] are valid placeholders in the > >> >query. > >> > >> >So, as far as I can tell, it is not possible to get this from the > >> >API: > >> > >> >query="SELECT ?, ?10" > >> >valid_placeholder_indexes=[1, 10] > >> > >> >It is only possible to get this: > >> > >> >query="SELECT ?, ?10" > >> >maybe_placeholder_indexes=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] > >> > >> The fundamental issue is that you are confusing POSITIONAL > >parameters with > >> NAMED parameters. ?nnn where nnn is numeric describe POSITIONAL > >> parameters, so in the query SELECT ?, ?10 you are using positional > >> parameter 1 and positional parameter 10. That implies that there > >are 10 > >> positional parameters. The fact that you are only using those > >positional > >> parameters sparsely (only using positional parameter 1 and > >positional > >> parameter 10) does not mean that you are using two parameters, it > >means > >> that you are using 10, but only referencing 2 of them. > >> > >> Said another way, you are declaring a function that looks like > >this: > >> > >> int stmt(a, b, c, d, e, f, g, h, i, j) > >> return a + j > >> > >> and expecting to be able to call it as > >> > >> z = stmt(1, 6) > >> > >> expecting some magic to know that the second parameter is really > >parameter > >> 10. > >> > >> https://www.sqlite.org/lang_expr.html#varparam > >> > >> There may be many positional parameters (like 999 in the default > >build) > >> and sqlite3_bind_parameter_count returns the "index" of the > >greatest > >> parameter number used in the statement. Having created 10 > >parameters > >> sqlite has absolutely no clue that you happen to be using only > >parameter 1 > >> and parameter 10. If you only needed 2 parameters you should have > >only > >> created 2, not 10. That is what NAMED parameters are for. > >> > >> If you change from using positional (?) parameters to using named > >(:) or > >> (@) what happens? > >> > >> select :1, :10; should only create 2 parameters named :1 and :10 > >... > >> > >> -- > >> The fact that there's a Highway to Hell but only a Stairway to > >Heaven says > >> a lot about anticipated traffic volume. > >> > >> > >> > >> > >> _______________________________________________ > >> sqlite-users mailing list > >> sqlite-users@mailinglists.sqlite.org > >> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite- > >users > >> > >_______________________________________________ > >sqlite-users mailing list > >sqlite-users@mailinglists.sqlite.org > >http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users > > > > _______________________________________________ > sqlite-users mailing list > sqlite-users@mailinglists.sqlite.org > http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users > _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users