Thread: Minor issue
Hi all This is very minor, but I thought I would mention it. I have a function that returns a complex SQL query and a tuple of parameters. The query is stored inside the function as a triple-quoted string, and the parameters are derived depending on the input arguments. Sometimes while testing I will comment out some of the SQL using '--'. If those lines happen to contain a parameter placeholder ('%s') I expected to remove the parameter from the tuple as well. pyodbc and sqlite3 both work this way, but psycopg2 raises the exception 'tuple index out of range'. I can live with it, but it means that I have to adjust the parameter tuple differently depending on which database I am testing with. If it can be fixed, that would be nice. If it can't, no problem. Frank Millman
On 26/05/20, Frank Millman (frank@chagford.com) wrote: > I have a function that returns a complex SQL query and a tuple of > parameters. The query is stored inside the function as a triple-quoted > string, and the parameters are derived depending on the input arguments. > > Sometimes while testing I will comment out some of the SQL using '--'. If > those lines happen to contain a parameter placeholder ('%s') I expected to > remove the parameter from the tuple as well. Could you provide an example?
On 2020-05-26 1:45 PM, Rory Campbell-Lange wrote: > On 26/05/20, Frank Millman (frank@chagford.com) wrote: >> I have a function that returns a complex SQL query and a tuple of >> parameters. The query is stored inside the function as a triple-quoted >> string, and the parameters are derived depending on the input arguments. >> >> Sometimes while testing I will comment out some of the SQL using '--'. If >> those lines happen to contain a parameter placeholder ('%s') I expected to >> remove the parameter from the tuple as well. > > Could you provide an example? > Sure. Here is my function - """ def get_sql(company, conn, bal_date): sql = (""" SELECT {1} AS "[DATE]" , c.gl_code , SUM(COALESCE(b.tran_tot, 0)) AS "[REAL2]" FROM ( -- SELECT ( -- SELECT c.row_id FROM {0}.gl_totals c -- WHERE c.tran_date <= {1} -- AND c.gl_code_id = d.row_id -- AND c.location_row_id = e.row_id -- AND c.function_row_id = f.row_id -- AND c.tran_type_id = g.row_id -- AND c.deleted_id = 0 -- ORDER BY c.tran_date DESC LIMIT 1 -- ) AS cl_row_id -- FROM {0}.gl_codes d, {0}.adm_locations e, {0}.adm_functions f, {0}.gl_tran_types g -- WHERE e.location_type != 'group' -- AND f.function_type != 'group' SELECT a.row_id FROM {0}.gl_codes b CROSS JOIN {0}.adm_locations c CROSS JOIN {0}.adm_functions d CROSS JOIN {0}.gl_tran_types e LEFT OUTER JOIN ( SELECT gl_code_id,location_row_id,function_row_id,tran_type_id,row_id,tran_date, ROW_NUMBER() OVER (PARTITION BY gl_code_id,location_row_id,function_row_id ORDER BY tran_date DESC) row_num FROM {0}.gl_totals WHERE deleted_id = 0 AND tran_date <= {1} ) a ON a.gl_code_id = b.row_id AND a.location_row_id = c.row_id AND a.function_row_id = d.row_id AND a.tran_type_id = e.row_id AND a.row_num = 1 ) AS a LEFT JOIN {0}.gl_totals b on b.row_id = a.row_id JOIN {0}.gl_codes c ON c.row_id = b.gl_code_id GROUP BY b.gl_code_id, c.gl_code HAVING b.gl_code_id IS NOT NULL ORDER BY b.gl_code_id """.format(company, conn.constants.param_style) ) params = (bal_date, bal_date) fmt = '{:%d-%m-%Y} : {:<12}{:>12}' return sql, params, fmt """ You will see 3 occurrences of '{1}'. This is replaced at runtime by the appropriate placeholder, namely '?' for pyodbc and sqlite3, '%s' for psycopg2. One of them is in a line that starts with '--'. The tuple of parameters has 2 items. It works with pyodbc and sqlite3. Using psyocpg2, it only works if I add a third item. Frank
No, we don't want to add any intelligence in trying to figure out what is into a query. If you are comfortable that you will be using always the same pattern for comments you can easily clean the string yourself before passing it to psycopg. A better approach for you I guess would be to use named placeholders, so that an a missing placeholder wouldn't require you to change the arguments to execute. -- Daniele On Tue, 26 May 2020 at 23:43, Frank Millman <frank@chagford.com> wrote: > > Hi all > > This is very minor, but I thought I would mention it. > > I have a function that returns a complex SQL query and a tuple of > parameters. The query is stored inside the function as a triple-quoted > string, and the parameters are derived depending on the input arguments. > > Sometimes while testing I will comment out some of the SQL using '--'. > If those lines happen to contain a parameter placeholder ('%s') I > expected to remove the parameter from the tuple as well. > > pyodbc and sqlite3 both work this way, but psycopg2 raises the exception > 'tuple index out of range'. > > I can live with it, but it means that I have to adjust the parameter > tuple differently depending on which database I am testing with. > > If it can be fixed, that would be nice. If it can't, no problem. > > Frank Millman > >
Ok, thanks. Frank On 2020-05-26 2:11 PM, Daniele Varrazzo wrote: > No, we don't want to add any intelligence in trying to figure out what > is into a query. If you are comfortable that you will be using always > the same pattern for comments you can easily clean the string yourself > before passing it to psycopg. > > A better approach for you I guess would be to use named placeholders, > so that an a missing placeholder wouldn't require you to change the > arguments to execute. > > -- Daniele > > On Tue, 26 May 2020 at 23:43, Frank Millman <frank@chagford.com> wrote: >> >> Hi all >> >> This is very minor, but I thought I would mention it. >> >> I have a function that returns a complex SQL query and a tuple of >> parameters. The query is stored inside the function as a triple-quoted >> string, and the parameters are derived depending on the input arguments. >> >> Sometimes while testing I will comment out some of the SQL using '--'. >> If those lines happen to contain a parameter placeholder ('%s') I >> expected to remove the parameter from the tuple as well. >> >> pyodbc and sqlite3 both work this way, but psycopg2 raises the exception >> 'tuple index out of range'. >> >> I can live with it, but it means that I have to adjust the parameter >> tuple differently depending on which database I am testing with. >> >> If it can be fixed, that would be nice. If it can't, no problem. >> >> Frank Millman >> >>
On 26/05/20, Frank Millman (frank@chagford.com) wrote: > On 2020-05-26 2:11 PM, Daniele Varrazzo wrote: > > No, we don't want to add any intelligence in trying to figure out what > > is into a query. If you are comfortable that you will be using always > > the same pattern for comments you can easily clean the string yourself > > before passing it to psycopg. > > > > A better approach for you I guess would be to use named placeholders, > > so that an a missing placeholder wouldn't require you to change the > > arguments to execute. > Ok, thanks. > > Frank I must be missing something, because this works for me: In [28]: d.query(""" ...: select ...: 1 as a ...: /* ...: ,2 as b ...: */ ...: -- ,'hi' as c ...: ,%s as d ...: """, ("a string", )).results Out[28]: [Record(a=1, d='a string')] (d.query is a wrapper around cursor_create, execute and fetchall). Does using %s instead of {}.format help solve the issue?
On 2020-05-26 3:08 PM, Rory Campbell-Lange wrote: > On 26/05/20, Frank Millman (frank@chagford.com) wrote: >> On 2020-05-26 2:11 PM, Daniele Varrazzo wrote: >>> No, we don't want to add any intelligence in trying to figure out what >>> is into a query. If you are comfortable that you will be using always >>> the same pattern for comments you can easily clean the string yourself >>> before passing it to psycopg. >>> >>> A better approach for you I guess would be to use named placeholders, >>> so that an a missing placeholder wouldn't require you to change the >>> arguments to execute. > >> Ok, thanks. >> >> Frank > > I must be missing something, because this works for me: > > In [28]: d.query(""" > ...: select > ...: 1 as a > ...: /* > ...: ,2 as b > ...: */ > ...: -- ,'hi' as c > ...: ,%s as d > ...: """, ("a string", )).results > Out[28]: [Record(a=1, d='a string')] > > (d.query is a wrapper around cursor_create, execute and fetchall). > > Does using %s instead of {}.format help solve the issue? > To reproduce my situation, you should place the '--' at the beginning of the following line (',%s as d'). As no parameters are now being substituted, I would expect to supply an empty tuple. In fact, the parameter is still required. Frank
On 26/05/20, Frank Millman (frank@chagford.com) wrote: > > > On 2020-05-26 3:08 PM, Rory Campbell-Lange wrote: > > On 26/05/20, Frank Millman (frank@chagford.com) wrote: > > Does using %s instead of {}.format help solve the issue? > > To reproduce my situation, you should place the '--' at the beginning of the > following line (',%s as d'). > > As no parameters are now being substituted, I would expect to supply an > empty tuple. In fact, the parameter is still required. That makes sense, apologies. However, this works as you suggest: In [33]: d.query(""" ...: select ...: 1 as a ...: /* ...: ,2 as b ...: */ ...: -- ,'hi' as c ...: -- ,%s as d ...: """, ("a string", )).results Out[33]: [Record(a=1)] This doesn't work (as you suggest): In [35]: d.query(""" ...: select ...: 1 as a ...: /* ...: ,2 as b ...: */ ...: -- ,'hi' as c ...: -- ,%s as d ...: """, ()).results ERROR:root:An unexpected error occurred while tokenizing input The following traceback may be corrupted or invalid The error message is: ('EOF in multi-line string', (1, 0)) But this works: In [34]: d.query(""" ...: select ...: 1 as a ...: /* ...: ,2 as b ...: */ ...: -- ,'hi' as c ...: -- ,%s as d ...: """).results Out[34]: [Record(a=1)] So perhaps simply don't provide the tuple in this case? This will also probably work for your other backends. Rory