SQL: Doc and test adjustments

This commit is contained in:
Yorhel 2025-02-21 10:22:33 +01:00
parent 145d086bea
commit 8036b8f0bf
2 changed files with 34 additions and 10 deletions

View file

@ -180,8 +180,7 @@ B<String literals> are interpreted as raw SQL fragments.
=item 2.
Objects returned by other functions listed below can be included as
SQL fragments.
Objects returned by other functions listed below are included as SQL fragments.
=item 3.
@ -200,8 +199,8 @@ These rules allow for flexible SQL construction:
There is some magic going on in order to differentiate between a I<string
literal> and other arguments. The rule is that anything that is
C<builtin::created_as_string()> and read-only (as per
C<Internals::SvREADONLY()>) is considered raw SQL. Variables are by definition
writable:
C<Internals::SvREADONLY()>) is considered raw SQL. Regular variables, array
elements and hash values are always writable:
my $x = 'SELECT 1';
SQL $x; # BAD: $x is used as bind parameter instead
@ -210,8 +209,16 @@ writable:
my $x = SQL 'SELECT 1';
SQL $x;
In most cases, though, this is the behavior you want. In the few cases where it
isn't, you can always use C<P()> or C<RAW()> to force an argument as bind
Constants created with C<use constant> are considered string literals, and
there are probably plenty of other creative ways to end up with variables that
may be considered a "string literal" by this module. L<Hash::Util>,
L<Scalar::Readonly>, other modules and tied hashes or arrays all have the
potential to create read-only strings, but I don't expect that these are
commonly applied on untrusted user input.
In most cases, this heuristic should work out well. In the few cases where it
doesn't, or when you're not entirely sure what kind of value you're dealing
with, you can always use C<P()> or C<RAW()> to force an argument as bind
parameter or SQL string.
=item P($val)
@ -235,7 +242,7 @@ Force the given C<$sql> string to be included as SQL. For example:
SQL 'WHERE * FROM', RAW $tables[1];
# 'SELECT * FROM b'
Absolutely do not use this function with untrusted input.
Never use this function with untrusted input.
=item PARENS(@args)
@ -271,7 +278,7 @@ C<'1=1'> (i.e. true) if C<@conditions> is an empty list.
=item AND($hashref)
A special form of C<AND()> that tests the given column for equality instead.
A special form of C<AND()> that tests the given columns for equality instead.
The keys of the hashref are interpreted as raw SQL and the values as bind
parameters.
@ -335,8 +342,7 @@ convenient insert-or-update:
SQL 'INSERT INTO table', VALUES($data),
'ON CONFLICT (name) DO UPDATE', SET($data);
(The bind parameters are duplicated though, there's no duplicate detection yet.
Not sure if that's even worth it)
(The bind parameters are duplicated though)
=item IN($arrayref)

18
t/sql.t
View file

@ -68,4 +68,22 @@ t IN([]), '= ANY($1)', [[]], in_style => 'pg', placeholder_style => 'pg';
t WHERE({ id => IN([1,2]) }), 'WHERE ( id IN(?,?) )', [1,2];
sub somefunc { 'not actually const' }
t SQL(somefunc), '?', [somefunc];
use constant constval => 'this is a constant';
t SQL(constval), constval, [];
sub otherfunc { constval }
t SQL(otherfunc), '?', [otherfunc];
sub passthrough { $_[0] }
t SQL(passthrough 'hi'), '?', ['hi'];
use Hash::Util;
my %hash = (v => 'value');
Hash::Util::lock_keys(%hash);
Hash::Util::lock_value(%hash, 'v');
t SQL($hash{v}), 'value', [];
done_testing;