File size: 1,360 Bytes
f55c62a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * Tagged template helper for composing parameterized SQL.
 *
 * Usage:
 *   sql`SELECT * FROM users WHERE uuid = ${uuid}`
 *
 * Values are pushed as positional `?` placeholders, in order. Nested
 * `SqlFragment` values are inlined verbatim and their params merged.
 * Use `raw` for trusted strings (identifiers, keywords) that must not
 * be parameterized.
 */
export class SqlFragment {
  constructor(
    readonly text: string,
    readonly params: readonly unknown[]
  ) {}
}

export function sql(
  strings: TemplateStringsArray,
  ...values: unknown[]
): SqlFragment {
  let text = '';
  const params: unknown[] = [];
  for (let i = 0; i < strings.length; i++) {
    text += strings[i];
    if (i < values.length) {
      const v = values[i];
      if (v instanceof SqlFragment) {
        text += v.text;
        params.push(...v.params);
      } else {
        text += '?';
        params.push(v);
      }
    }
  }
  return new SqlFragment(text, params);
}

export function raw(s: string): SqlFragment {
  return new SqlFragment(s, []);
}

export function join(
  fragments: readonly SqlFragment[],
  separator = ', '
): SqlFragment {
  let text = '';
  const params: unknown[] = [];
  fragments.forEach((f, i) => {
    if (i > 0) text += separator;
    text += f.text;
    params.push(...f.params);
  });
  return new SqlFragment(text, params);
}