The fun aspects of this are that:
(a) I could actually imagine trailbase.js contents that would make it not SQL injection: you could have parsePath(…).query.get(…) return objects with a toString() that escaped SQL. This would raise even more questions, and I was sure it wouldn’t be the case, but it’s possible.
(b) You could make it work safely, converting interpolation into parameters, by using a tagged template string. This could require only a tiny change:
return await query(
sql`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, '[${+aroma}, ${+flavor}, ${+acid}, ${+sweet}]')
LIMIT 100`
);
(You could even make it query`…`, but I think query(sql`…`) is probably wiser. As for the plusses I put in there, that’s to convert from strings to numbers.)This is a concept that’s definitely been done seriously. The first search result I found: https://github.com/blakeembrey/sql-template-tag.
probably something like this would work then too (didn't test):
return await query(
`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, '[?,?,?,?]')
LIMIT 100`,
[+aroma, +flavor, +acid, +sweet],
);
it is nice because the query string is constant then and a prepared query could be cached..And actually on second consideration, you probably can’t use binding parameters here: you’re trying to slot numbers in as JSON values inside a string! Maybe you’d have to write vec_distance_L2(embedding, ?), with parameter JSON.stringify([+aroma, +flavor, +acid, +sweet]).
—⁂—
const [data, setData] = useState<Array<Array<object>> | undefined>();
That type stinks. Kill the undefined bit by giving it an initial value, then you can skip the `?? []` later too; and replace Array<object> with the actual row type, probably naming it, maybe like this: type Record = [string, number, number, number, number];
const [data, setData] = useState<Record[]>([]);
—⁂— const params = Object.entries({ aroma, flavor, acidity, sweetness })
.map(([k, v]) => `${k}=${v}`)
.join("&");
No need to construct the query string manually: const params = new URLSearchParams({ aroma, flavor, acidity, sweetness });
I confess I’m puzzled about this one too, as trailbase.js suggests you know about URLSearchParams, which is what you should use for all typical query string manipulation, parsing and generation.Mucking about with SQL strings, to me, is akin to writing your own crypto: Don't. Unless you must (because, say, it doesn't exist yet.) Doing this is asking for security problems later. Trust that the SQLite team (or any other SQL engine) have more experience and have provided a correct interface. And if that interface isn't correct, don't just run off an make your own- contribute a fix so we can keep all the lessons learned together.
Is the string interpolation straight into the sql from the query string in your getting started docs (https://trailbase.io/getting-started/first-ui-app) safe? It smells a bit.
From a quick look (on mobile) I can see that the function takes parameters, but you're not using that.