File size: 2,772 Bytes
aa89cb5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
59
60
61
62
63
64
65
66
67
68
import { debugSQL } from '../src/sql/core';
import { Values } from '../src/core/value';
import { SQLiteDriver } from '../src/sql/sqlite';

describe('debugSQL', () => {
  test('renders positional SQL that can be copied into SQLite or MySQL', () => {
    expect(debugSQL(
      "SELECT * FROM school WHERE name = ? AND active = ? AND phone IS ? AND note = '?'",
      ["O'Brien School", true, null],
    )).toBe(
      "SELECT * FROM school WHERE name = 'O''Brien School' AND active = TRUE AND phone IS NULL AND note = '?'",
    );
  });

  test('renders numbered placeholders repeatedly and ignores SQL string literals', () => {
    expect(debugSQL("SELECT $2, $1, $1, '$2'", ["O'Brien", 7]))
      .toBe("SELECT 7, 'O''Brien', 'O''Brien', '$2'");
    expect(debugSQL('SELECT @p2, @p1', ['first', 'second']))
      .toBe("SELECT 'second', 'first'");
  });

  test('preserves comments and renders SQLite temporal storage literals', () => {
    expect(debugSQL(
      "-- line ? $1\nSELECT '?', \"identifier?\", ?, ? /* block ? */",
      [Values.Date('2024-02-29'), Values.Timestamp(1787110200123)],
    )).toBe(
      "-- line ? $1\nSELECT '?', \"identifier?\", '2024-02-29', 1787110200123 /* block ? */",
    );
  });

  test('temporal debug SQL matches prepared SQLite storage', async () => {
    const driver = new SQLiteDriver(':memory:');
    try {
      await driver.query('CREATE TABLE temporal_fixture(id INTEGER, d TEXT, t INTEGER)');
      await driver.query(
        'INSERT INTO temporal_fixture VALUES (?, ?, ?) /* ignored ? */',
        [1, '2024-02-29', -123],
      );
      const literal = debugSQL(
        '/* teaql source=temporal ? */ INSERT INTO temporal_fixture VALUES (?, ?, ?)',
        [Values.I64(2), Values.Date('2024-02-29'), Values.Timestamp(-123)],
      );
      await driver.query(literal);
      const result = await driver.query(
        'SELECT d, t, typeof(d) AS dt, typeof(t) AS tt FROM temporal_fixture ORDER BY id',
      );
      expect(result.rows).toEqual([
        { d: '2024-02-29', t: -123, dt: 'text', tt: 'integer' },
        { d: '2024-02-29', t: -123, dt: 'text', tt: 'integer' },
      ]);
    } finally {
      await driver.close();
    }
  });

  test('renders PostgreSQL and MySQL typed temporal literals', () => {
    const values = [Values.Date('2024-02-29'), Values.Timestamp(-315521754322)];
    expect(debugSQL(
      '-- ignored $1\nSELECT $1, $2 /* ignored $2 */', values, 'postgresql',
    )).toBe(
      "-- ignored $1\nSELECT DATE '2024-02-29', TIMESTAMPTZ '1960-01-02T03:04:05.678Z' /* ignored $2 */",
    );
    expect(debugSQL('SELECT ?, ? /* ignored ? */', values, 'mysql')).toBe(
      "SELECT CAST('2024-02-29' AS DATE), CAST('1960-01-02 03:04:05.678' AS DATETIME(3)) /* ignored ? */",
    );
  });
});