[{"data":1,"prerenderedAt":291},["ShallowReactive",2],{"project-44004827":3},{"id":4,"name":5,"line":6,"line_source":7,"title":8,"url":9,"domain":10,"maker":11,"posted_at":12,"year":13,"points":14,"comments":15,"rank_in_year":16,"shown_in_year":17,"top":18,"tier":19,"category":20,"subcategory":21,"format":22,"keywords":23,"reception":28,"sentiment":29,"stance_mix":30,"scored_comments":35,"aspects":36,"strip":43,"post_text":46,"what":47,"said":61,"maker_in_thread":158,"shape":169,"grid":174,"attempts":254,"same_week":255,"maker_shows":31,"maker_hidden":283,"reader_version":284,"card":285},"44004827","SQL-tString","SQL-tString a t-string SQL builder in Python","title","Show HN: SQL-tString a t-string SQL builder in Python","https:\u002F\u002Fgithub.com\u002Fpgjones\u002Fsql-tstring","github.com","pgjones",1747399702,2025,85,35,844,3837,22,"notable","data","sql query builder","library",[24,25,26,27],"python t-strings","sql builder","sql injection","parameterized queries","curious",-0.064,{"positive":31,"neutral":32,"mixed":33,"critical":34},2,6,0,4,12,{"design":37,"idea":39,"execution":40,"docs":41,"privacy":42},{"positive":38,"critical":31},1,{"positive":38,"critical":33},{"positive":33,"critical":38},{"positive":33,"critical":33},{"positive":33,"critical":38},[44,33,44,45,33,33,33,33,33,45,33,38,33,45,33,45,38,33,45,33,38,38,33,45,33,33,33,33,33,33,33,33,33,33,33],5,3,"SQL-tString is a SQL builder that utilises the recently accepted PEP-750, https:\u002F\u002Fpeps.python.org\u002Fpep-0750\u002F, t-strings to build SQL queries, for example,\n\n\n```\n    from sql_tstring import sql          val = 2     query, values = sql(t\"SELECT x FROM y WHERE x = {val}\")     assert query == \"SELECT x FROM y WHERE x = ?\"     assert values == [2]     db.execute(query, values)  # Most DB engines support this \n```\n The placeholder ? protects against SQL injection, but cannot be used everywhere. For example, a column name cannot be a placeholder. If you try this SQL-tString will raise an error,\n\n\n```\n",{"problem":48,"audience":54,"pricing":54,"motivation":54,"time_to_build":54,"stack":55,"openness":60,"stage":60,"team_size":60},{"value":49,"source":50,"evidence":51},"Building SQL queries safely with optional clauses and placeholders without injection risk","maker",[52],{"id":53},"post",null,[56],{"value":57,"source":50,"evidence":58},"Python",[59],{"id":53},"unknown",{"praise":62,"objections":71,"requests":99,"questions":114,"alternatives":127},[63],{"value":64,"source":65,"evidence":66},"Dynamic query building with Absent is a better approach than if-statement WHERE clauses","commenter",[67],{"id":68,"by":69,"text":70},"44006804","hombre_fatal","I thought this was just going to be the same ol \"where id = {id}\" interpolation but dang, those are some crazy examples. I can imagine the behavior takes some trial and error to…",[72,79,89],{"value":73,"source":65,"evidence":74},"Absent removing clauses could silently drop filters and expose whole tables",[75],{"id":76,"by":77,"text":78},"44008236","bencyoung","Looks very nice but the Absent functionality seems like a potential foot gun to me, easy to remove an entire WHERE clause and blow up a whole table! If I have a filter in sql…",{"value":80,"source":65,"evidence":81},"Parsing non-standard SQL and expression removal is risky for complex queries",[82,86],{"id":83,"by":84,"text":85},"44005869","jitl","How does the SQL parsing work for the rewrites like removing expressions? I have a project using some non-standard SQL features and we have quite complex queries going on, so the…",{"id":87,"by":84,"text":88},"44008238","How do you know what the expression is though? Don’t you need to be parsing the SQL? If I have non standard SQL somewhere upstream in the text how does the parser cope?",{"value":90,"source":65,"evidence":91},"Template-based SQL looks like string concatenation and may mislead developers",[92,96],{"id":93,"by":94,"text":95},"44008355","williamdclt","No I think your point is valid, and is valid in JavaScript too. Designing the “right” approach to look like the “wrong” approach (string concatenation) is a bad idea, however…",{"id":97,"by":94,"text":98},"44009001","I might have misunderstood your point, but scoping isn’t related to what I’m trying to say. What I’m saying is that, regardless of how it works, I don’t think string templating…",[100,107],{"value":101,"source":65,"evidence":102},"Support composing queries from fragments while keeping placeholder safety",[103],{"id":104,"by":105,"text":106},"44009669","badmonster","I had a question about dynamic query fragments: is there a recommended way to compose larger queries from smaller pieces while preserving placeholder safety and avoiding manual…",{"value":108,"source":65,"evidence":109},"Use a formal lexer and parser based on the SQL grammar with conformance testing",[110],{"id":111,"by":112,"text":113},"44005951","schultzer","Just took a quick look, and it seams like the parser is hand written which is great, but you probably want to build a lexer and parser based on the BNF grammar take a look at how…",[115,120],{"value":116,"source":65,"evidence":117},"How does the parser determine expression boundaries for removal",[118,119],{"id":83,"by":84,"text":85},{"id":87,"by":84,"text":88},{"value":121,"source":65,"evidence":122},"Can different column allowlists be used for different parts of a query",[123],{"id":124,"by":125,"text":126},"44014673","throwaway127482","The columns feature seems somewhat dangerous at first glance. What if I wanted different sets of allowed columns for different parts of the query? Would I have to provide the…",[128,137,144,151],{"value":129,"source":65,"evidence":130},"elixir-dbvisor\u002Fsql",[131,134],{"id":132,"by":112,"text":133},"44005593","Not really sure what a t string is or if it’s a macro, but feel similar to https:\u002F\u002Fgithub.com\u002Felixir-dbvisor\u002Fsql but less elegant and ergonomic.",{"id":135,"by":112,"text":136},"44006657","If your language has macros you can get by as well https:\u002F\u002Fgithub.com\u002Felixir-dbvisor\u002Fsql",{"value":138,"source":65,"evidence":139},"sqlbind",[140],{"id":141,"by":142,"text":143},"44012634","bvrmn","I'm author of sqlbind[1] and since t-strings announce have been thinking really hard how to incorporate it. Like obvious cases `t\"select * from table where id = {id}\"` are…",{"value":145,"source":65,"evidence":146},"sqlglot",[147],{"id":148,"by":149,"text":150},"44007906","westurner","Ibis has sqlglot for parsing and rewriting SQL query graphs; and there's sql-to-ibis: https:\u002F\u002Fgithub.com\u002Fibis-project\u002Fibis\u002Fissues\u002F9529 sqlglot: https:\u002F\u002Fgithub.com\u002Ftobymao\u002Fsqlglot…",{"value":152,"source":65,"evidence":153},"manifold-sql",[154],{"id":155,"by":156,"text":157},"44006578","owlstuffing","Languages should strive to type safely inline SQL and other structured data. With Java the manifold project achieves this via compiler plugin. The manifold-sql[1] module provides…",{"comments":32,"answered":45,"key":159},[160,163,166],{"id":161,"text":162},"44006014","Thanks, do you have a reference for SQL grammar - I've had no success finding an official source.",{"id":164,"text":165},"44006044","Thank you! My Google foo did not find this.",{"id":167,"text":168},"44006039","The presence of Absent removes the entire expression, and if that removal results in an empty clause (or group) it will remove that as well. For example if `a = Absent` `WHERE a…",{"commenters":170,"max_depth":171,"maker_replies":32,"first_comment_mins":172,"lifespan_hours":173},17,8,68,38,[175,177,180,182,184,186,189,191,194,195,196,198,200,203,207,209,210,212,215,220,222,224,226,229,233,235,236,240,242,244,246,247,249,250,252],[68,33,44,176,69,33],"project\u002Fpositive\u002Fdesign",[178,33,33,179,94,33],"44008289","commenter\u002Fneutral\u002Fdesign",[141,38,44,181,142,33],"project\u002Fpositive\u002Fidea",[111,31,45,183,112,33],"project\u002Fneutral\u002Fexecution",[161,31,33,185,11,38],"maker\u002Fneutral\u002Fdocs",[187,31,33,188,112,33],"44006035","other\u002Fneutral\u002Fdocs",[164,31,33,190,11,38],"other\u002Fneutral\u002Fnone",[192,31,33,190,193,33],"44015912","UltraSane",[148,31,33,190,149,33],[83,45,45,183,84,33],[167,45,33,197,11,38],"maker\u002Fneutral\u002Fdesign",[87,45,38,199,84,33],"project\u002Fcritical\u002Fexecution",[201,45,33,202,11,38],"44008856","maker\u002Fneutral\u002Fexecution",[204,34,45,205,206,33],"44007257","project\u002Fneutral\u002Fdocs","caturopath",[208,34,33,185,11,38],"44007704",[104,44,45,197,105,33],[124,32,38,211,125,33],"project\u002Fcritical\u002Fprivacy",[213,32,33,214,11,38],"44016725","maker\u002Fneutral\u002Fprivacy",[216,217,45,218,219,33],"44008812",7,"project\u002Fneutral\u002Fdesign","ilyagr",[221,217,33,190,219,33],"44008977",[76,171,38,223,77,33],"project\u002Fcritical\u002Fdesign",[132,225,38,223,112,33],9,[227,225,33,190,228,33],"44006827","1_08iu",[230,231,45,218,232,33],"44005627",10,"sirfz",[155,234,33,190,156,33],11,[135,234,33,190,112,33],[237,35,33,238,239,33],"44005841","other\u002Fcritical\u002Fdesign","90s_dev",[241,35,33,238,84,33],"44005924",[243,35,33,179,239,33],"44006095",[245,35,33,179,239,33],"44007947",[93,35,33,179,94,33],[248,35,33,179,239,33],"44008734",[97,35,33,179,94,33],[251,35,33,179,239,33],"44009134",[253,35,33,179,84,33],"44018743",[],[256,261,266,270,275,279],{"id":257,"name":258,"points":259,"category":260},"44022353","Voynich NLP analysis",381,"learning_science",{"id":262,"name":263,"points":264,"category":265},"44042371","90s.dev",352,"dev_tools",{"id":267,"name":268,"points":269,"category":265},"44042490","Jwno",297,{"id":271,"name":272,"points":273,"category":274},"44009321","KVSplit",272,"ai",{"id":276,"name":277,"points":278,"category":265},"44006231","Erlang-RED",243,{"id":280,"name":281,"points":282,"category":265},"44023144","Vaev",233,false,"v2",{"id":286,"name":5,"line":6,"ai":283,"cat":20,"year":13,"points":14,"comments":15,"rec":28,"s":287,"top":18,"kw":288,"maker":11,"domain":10,"tier":19,"attempts":33,"strip":289,"url":9,"mix":290,"praise":64,"objection":73,"sub":21},44004827,-0.06,[24,25,26,27],"50530000030103031030110300000000000",[31,32,33,34],1794036674908]