I read your post 3 times and I couldn't find a question about package vars. neither "our" nor "package"
Using the fact that I forgot to ask a question to not answer it, that's low :P.
Package vars are actually easier to handle than lexicals, that's why it's not included in the first demo.
OK, package vars not a problem then. Honestly my main concern was with the explicit use of closures, I didn't think this through so I didn't imagine what this could look like with the (&) prototype. I think it isn't as bad with the prototype version. At least I agree that there's no issue with package vars now that you've answered the question I didn't ask.
I wrote this:
my $username = 'LanX';
my $req1 = $db->xprepare(ph { "SELECT from users WHERE nick = '$userna
+me'" });
$username = 'Eily';
my $req2 = $db->xprepare(ph { "SELECT from users WHERE nick = '$userna
+me'" });
$req1->xexecute();
$req2->xexecute();
because in your example the variables where interpolated directly in the string fed to prepare. So unless that's with some version of perl with an extra layer of magic, the prepare is completly useless, which means probably not understood. So someone who wrote code with all the values in the prepare call might have written code like this:
my $username = 'LanX';
my $req1 = $db->prepare("SELECT from users WHERE nick = '$username'");
$username = 'Eily';
my $req2 = $db->prepare("SELECT from users WHERE nick = '$username'");
$req1->execute();
$req2->execute();
In which case you will get two different results. Someone who thinks your version is pretty much the same but is "optimized somehow" might fall into that trap. And that's counter intuitive anyway, people expect that when you use a variable in a statement, changing the variable later doesn't change the result of the previous statement. So by "as intended" I mostly meant by "as intended by a newbie". Notice that I called xprepare twice though, not once before changing the values like in your proposal. While it may be understood that calling xprepare once and changing the values will execute a different request each time, you could expect that calling xprepare again overwrites the biding, so the previously prepared queries are left unchanged. Maybe calling xprepare twice without calling xexecute should be forbidden (
die), because according to your interpretation (twice the same result), the second call to xprepare is useless. If someone writes useless code on purpose, they probably don't understand what it does, in this case, they probably interpreted it like me (two different results).
prepare statements are meant to speed up repeated execution
They do speed up execution most of the time, but they can also do that across sessions, if the optimizer sees the exact same request prepared again, it can reuse the execution plan from the previous call and skip the evaluation phase. But the main advantage of bind variables is security, because they are protected against injection. So even if you had to call xprepare again each time before a xexecute, replacing the interpolated variables by bound variables would still be a huge benefit, that's why I didn't realize you were concentrating on the "one prepare, several executes" case.
BTW, since xprepare is supposed to be called instead of prepare, calling it several times in a row could change the values sent to execute without calling prepare again if the query string is the same. That way your colleagues could even write (with xdo to mean xprepare + xexecute):
$first_node = 1210590;
$first = $dbh->xdo(ph { "SELECT * WHERE node_id = $first_node" }); #
+Calls prepare and execute
$second_node = 1210586;
$second = $dbh->xdo(ph { "SELECT * WHERE node_id = $second_node" }); #
+ Same query, skips prepare, calls execute
$third_node = 1210578;
$third = $dbh->xdo(ph { "SELECT * WHERE node_id = $third_node " }); #
+ Prepare still not called
Although the DB manager might already do that for you (ignore a prepare that it already processed). And since I'm putting myself in the place of someone who doesn't understand everything or even doesn't want to understand (because the starting point is people who are reluctant to rewrite the code with placeholders), I didn't do the smart thing and select all three at once.
> my %vars = (rank => 'Pope', xp => 4);
well I could also allow that bound variables are overriden if xexecute gets arguments. (not sure if that's what you mean)
No I didn't think about that, but that's a good idea, I guess you could imagine something like that:
my %default = (rank => "Pope", xp => 4);
my $req = $dbh->xprepare(ph { "SELECT * FROM users WHERE rank = $_{ran
+k} AND xp > $_{xp} AND username != $_{username}" } %default);
$req->execute(rank => "Prior", username => "Eily");
Where xp uses the value bound during xprepare, rank is overwritten, and username is required because it doesn't have a default. At least that way if you want to replace some, or all values you don't have something that looks like you change a value
after it was used. And when you reuse several times the same query in a row, some of the values might be left unchanged each time. And for this version to work you don't need to look into variables a closure closed over (ie accessing lexicals outside of their scope), prototypes are enough, and maybe
tie if you want to
die when a required parameter is missing (ie, when trying to read a value for a key that doesn't exist in %_)
FYI, probably more than half of that conversation happened in my head, because I realized things as I typed, and corrected myself and rewrote a lot. That's why my posts may lack questions I intended to ask, and be confusing. And I meant "you" as in "someone" BTW :D