• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rogerpadilla / nukak / #526

29 Dec 2025 06:29PM UTC coverage: 86.953% (+0.4%) from 86.536%
#526

push

rogerpadilla
v1.8.0

683 of 922 branches covered (74.08%)

Branch coverage included in aggregate %.

2376 of 2596 relevant lines covered (91.53%)

149.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.0
/packages/postgres/src/postgresDialect.ts
1
import { AbstractSqlDialect } from 'nukak/dialect';
2✔
2
import { getMeta } from 'nukak/entity';
2✔
3
import {
4
  type FieldKey,
5
  type FieldOptions,
6
  type NamingStrategy,
7
  type QueryComparisonOptions,
8
  type QueryConflictPaths,
9
  type QueryContext,
10
  type QueryOptions,
11
  QueryRaw,
12
  type QueryTextSearchOptions,
13
  type QueryWhereFieldOperatorMap,
14
  type QueryWhereMap,
15
  type Type,
16
} from 'nukak/type';
2✔
17
import sqlstring from 'sqlstring-sqlite';
2✔
18

19
export class PostgresDialect extends AbstractSqlDialect {
2✔
20
  constructor(namingStrategy?: NamingStrategy) {
21
    super(namingStrategy, '"', 'BEGIN TRANSACTION');
2✔
22
  }
23

24
  override addValue(values: unknown[], value: unknown): string {
25
    values.push(value);
66✔
26
    return this.placeholder(values.length);
66✔
27
  }
28

29
  override placeholder(index: number): string {
30
    return `$${index}`;
66✔
31
  }
32

33
  override insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void {
34
    super.insert(ctx, entity, payload, opts);
5✔
35
    ctx.append(' ' + this.returningId(entity));
5✔
36
  }
37

38
  override upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E): void {
39
    const meta = getMeta(entity);
8✔
40
    const update = this.getUpsertUpdateAssignments(ctx, meta, conflictPaths, payload, (name) => `EXCLUDED.${name}`);
11✔
41
    const keysStr = this.getUpsertConflictPathsStr(meta, conflictPaths);
8✔
42
    const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
8✔
43
    super.insert(ctx, entity, payload);
8✔
44
    ctx.append(` ON CONFLICT (${keysStr}) ${onConflict} ${this.returningId(entity)}`);
8✔
45
  }
46

47
  override compare<E, K extends keyof QueryWhereMap<E>>(
48
    ctx: QueryContext,
49
    entity: Type<E>,
50
    key: K,
51
    val: QueryWhereMap<E>[K],
52
    opts: QueryComparisonOptions = {},
×
53
  ): void {
54
    if (key === '$text') {
16✔
55
      const meta = getMeta(entity);
2✔
56
      const search = val as QueryTextSearchOptions<E>;
2✔
57
      const fields = search.$fields
2✔
58
        .map((fKey) => {
59
          const field = meta.fields[fKey];
3✔
60
          const columnName = this.resolveColumnName(fKey as string, field);
3✔
61
          return this.escapeId(columnName);
3✔
62
        })
63
        .join(` || ' ' || `);
64
      ctx.append(`to_tsvector(${fields}) @@ to_tsquery(`);
2✔
65
      ctx.addValue(search.$value);
2✔
66
      ctx.append(')');
2✔
67
      return;
2✔
68
    }
69
    super.compare(ctx, entity, key, val, opts);
14✔
70
  }
71

72
  override compareFieldOperator<E, K extends keyof QueryWhereFieldOperatorMap<E>>(
73
    ctx: QueryContext,
74
    entity: Type<E>,
75
    key: FieldKey<E>,
76
    op: K,
77
    val: QueryWhereFieldOperatorMap<E>[K],
78
    opts: QueryOptions = {},
×
79
  ): void {
80
    switch (op) {
18!
81
      case '$istartsWith':
82
        this.getComparisonKey(ctx, entity, key, opts);
2✔
83
        ctx.append(' ILIKE ');
2✔
84
        ctx.addValue(`${val}%`);
2✔
85
        break;
2✔
86
      case '$iendsWith':
87
        this.getComparisonKey(ctx, entity, key, opts);
2✔
88
        ctx.append(' ILIKE ');
2✔
89
        ctx.addValue(`%${val}`);
2✔
90
        break;
2✔
91
      case '$iincludes':
92
        this.getComparisonKey(ctx, entity, key, opts);
2✔
93
        ctx.append(' ILIKE ');
2✔
94
        ctx.addValue(`%${val}%`);
2✔
95
        break;
2✔
96
      case '$ilike':
97
        this.getComparisonKey(ctx, entity, key, opts);
2✔
98
        ctx.append(' ILIKE ');
2✔
99
        ctx.addValue(val);
2✔
100
        break;
2✔
101
      case '$in':
102
        this.getComparisonKey(ctx, entity, key, opts);
×
103
        ctx.append(' = ANY(');
×
104
        ctx.addValue(val);
×
105
        ctx.append(')');
×
106
        break;
×
107
      case '$nin':
108
        this.getComparisonKey(ctx, entity, key, opts);
×
109
        ctx.append(' <> ALL(');
×
110
        ctx.addValue(val);
×
111
        ctx.append(')');
×
112
        break;
×
113
      case '$regex':
114
        this.getComparisonKey(ctx, entity, key, opts);
1✔
115
        ctx.append(' ~ ');
1✔
116
        ctx.addValue(val);
1✔
117
        break;
1✔
118
      default:
119
        super.compareFieldOperator(ctx, entity, key, op, val, opts);
9✔
120
    }
121
  }
122

123
  protected override formatPersistableValue<E>(ctx: QueryContext, field: FieldOptions, value: unknown): void {
124
    if (value instanceof QueryRaw) {
47✔
125
      super.formatPersistableValue(ctx, field, value);
1✔
126
      return;
1✔
127
    }
128
    if (field.type === 'json' || field.type === 'jsonb') {
46✔
129
      ctx.addValue(value ? JSON.stringify(value) : null);
1!
130
      ctx.append(`::${field.type}`);
1✔
131
      return;
1✔
132
    }
133
    if (field.type === 'vector' && Array.isArray(value)) {
45!
134
      ctx.addValue(`[${value.join(',')}]`);
×
135
      ctx.append('::vector');
×
136
      return;
×
137
    }
138
    super.formatPersistableValue(ctx, field, value);
45✔
139
  }
140

141
  override escape(value: unknown): string {
142
    return sqlstring.escape(value);
×
143
  }
144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc