Interpolated strings and SQL

Nickolay Bukreyev buknik95 at ya.ru
Tue Jan 9 21:35:28 UTC 2024


On Tuesday, 9 January 2024 at 20:01:34 UTC, Walter Bright wrote:
> With the istring, there are 4 calls to struct member functions 
> that just return null.
> This can't be good for performance or program size.

A valid point, thanks. Could you test if that fixes the issue?

```d
import core.interpolation;
import std.meta: AliasSeq, staticMap;
import std.stdio;

template filterOutEmpty(alias arg) {
     alias T = typeof(arg);

     static if (is(T == InterpolatedLiteral!s, string s))
         static if (s.length)
             alias filterOutEmpty = s;
         else
             alias filterOutEmpty = AliasSeq!();
     else static if (
         is(T == InterpolationHeader) ||
         is(T == InterpolatedExpression!code, string code) ||
         is(T == InterpolationFooter)
     )
         alias filterOutEmpty = AliasSeq!();
     else
         alias filterOutEmpty = arg;
}

pragma(inline, true) // This pragma is necessary unless you 
compile with `-inline`.
void log(Args...)(InterpolationHeader, Args args, 
InterpolationFooter) {
     writeln(staticMap!(filterOutEmpty, args));
}

void main() {
     int baz = 3;
     log(i"$(baz + 4)");
     writeln(baz + 5);
}
```

> Adam's implementation of execi() also runs at run time, not 
> compile time.

We are probably talking about different things. Adam’s 
implementation constructs a format string at compile time thanks 
to `enum` storage class [in line 
36](https://github.com/adamdruppe/interpolation-examples/blob/a8a5d4d4ee37ee9ae3942c4f4e8489011c3c4673/lib/sql.d#L36). Constructing it at compile time is essential so that we can validate the generated SQL and abort compilation, as Paolo [demonstrated](https://forum.dlang.org/post/qbtbyxcglwijjbeygtvi@forum.dlang.org).

> execi could be extended to reject arguments that contain %s 
>sequences.

I disagree. Storing a string that contains `%s` in a database 
should be allowed (storing any string should obviously be 
allowed, regardless of its contents). But `execi` is unable to 
differentiate between a string that happens to contain `%s` and a 
nested format string:

```
// DIP1027
example(i"prefix1 $(i"prefix2 $(x) suffix2") suffix1");
// Gets rewritten as:
example("prefix1 %s suffix1", "prefix2 %s suffix2", x);
```

I might be wrong, but it appears to me that DIP1027 is not able 
to deal with nested format strings, in a general case. DIP1036 
has no such limitation (demonstrated in point 2 
[here](https://forum.dlang.org/post/lizjwxdgsnmgykaoczyf@forum.dlang.org)).

> Nor does it need the extra two arguments, which aren't free of 
> cost.

I explained 
[here](https://forum.dlang.org/post/qkvxnbqjefnvjyytfana@forum.dlang.org) why these two arguments are valuable. Aren’t free of cost—correct unless you enable inlining. `execi` may require some changes (like `filterOutEmpty` I showed above) to make them free of cost, but it is doable.

> What happens when ?3 is included in a DIP1036 istring? 
> `i"string ?3 ($betty)"` ? I didn't see any check for that. Of 
> course, one could add such a check to the 1036 execi.

You are right, it doesn’t. Timon’s point (expressed as “This does 
not work”) is that DIP1036 is able to do validation at compile 
time while DIP1027 is only able to do it at runtime, when this 
function actually gets invoked.


More information about the Digitalmars-d mailing list