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

egobrain / equery / 8

23 Jul 2024 06:47AM UTC coverage: 98.756%. Remained the same
8

push

github

web-flow
Support erlang 26+ (#7)

* Move to github actions

7 of 7 new or added lines in 3 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

397 of 402 relevant lines covered (98.76%)

181.41 hits per line

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

98.61
/src/pg_sql.erl
1
-module(pg_sql).
2

3
-include("query.hrl").
4
-include("cth.hrl").
5

6
-export([
7
         'andalso'/2,
8
         'orelse'/2,
9

10
         '=:='/2,
11
         '=/='/2,
12
         '>'/2,
13
         '>='/2,
14
         '<'/2,
15
         '=<'/2,
16
         'not'/1,
17
         'is'/2,
18
         is_null/1,
19

20
         '+'/2,
21
         '-'/2,
22
         '*'/2,
23
         '/'/2,
24
         'abs'/1
25
        ]).
26

27
-export([
28
         '~'/2,
29
         '~*'/2,
30
         like/2,
31
         ilike/2
32
        ]).
33

34
-export([
35
         call/3
36
        ]).
37

38
-export([
39
         sum/1,
40
         count/1,
41
         min/1,
42
         max/1,
43
         distinct/1,
44
         array_agg/1,
45
         trunc/2
46
        ]).
47

48
-export([
49
         min/2,
50
         max/2,
51
         row/1,
52
         row/2
53
        ]).
54

55
-export([
56
         coalesce/1,
57
         in/2,
58
         exists/1
59
        ]).
60

61
%% Array functions
62
-export([
63
         '@>'/2
64
        ]).
65

66
%% Type function
67
-export([
68
         as/2,
69
         set_type/2
70
        ]).
71

72
%% =============================================================================
73
%% Sql operations
74
%% =============================================================================
75

76
-type value() :: qast:ast_node() | any().
77

78
%% = Primitive =================================================================
79

80
%% @TODO wrap values in $value before validation and add $value match
81
-spec 'andalso'(V, V) -> V when V :: boolean() | qast:ast_node().
82
'andalso'(true, B) -> B;
4✔
83
'andalso'(A, true) -> A;
4✔
84

85
'andalso'(false, _) -> false;
4✔
86
'andalso'(_, false) -> false;
4✔
87

88
'andalso'(A, B) ->
89
    qast:exp([qast:raw("("), A, qast:raw(" and "), B, qast:raw(")")], #{type => boolean}).
32✔
90

91
-spec 'orelse'(V, V) -> V when V :: boolean() | qast:ast_node().
92
'orelse'(true, _) -> true;
4✔
93
'orelse'(_, true) -> true;
4✔
94
'orelse'(false, B) -> B;
4✔
95
'orelse'(A, false) -> A;
4✔
96
'orelse'(A, B) ->
97
    qast:exp([qast:raw("("), A, qast:raw(" or "), B, qast:raw(")")], #{type => boolean}).
36✔
98

99
-spec 'not'(V) -> V when V :: boolean() | qast:ast_node().
100
'not'(A) when is_boolean(A) -> not A;
8✔
101
'not'(A) ->
102
    qast:exp([qast:raw("not "), A], #{type => boolean}).
16✔
103

104
-spec '=:='(value(), value()) -> qast:ast_node().
105
'=:='(A, B) ->
106
    qast:exp([qast:raw("("), A, qast:raw(" = "), B, qast:raw(")")], #{type => boolean}).
132✔
107

108
-spec '=/='(value(), value()) -> qast:ast_node().
109
'=/='(A, B) -> 'not'('=:='(A,B)).
4✔
110

111
-spec '>'(value(), value()) -> qast:ast_node().
112
'>'(A, B) ->
113
    qast:exp([qast:raw("("), A, qast:raw(" > "), B, qast:raw(")")], #{type => boolean}).
16✔
114
-spec '>='(value(), value()) -> qast:ast_node().
115
'>='(A, B) ->
116
    qast:exp([qast:raw("("), A, qast:raw(" >= "), B, qast:raw(")")], #{type => boolean}).
4✔
117
-spec '<'(value(), value()) -> qast:ast_node().
118
'<'(A, B) ->
119
    qast:exp([qast:raw("("), A, qast:raw(" < "), B, qast:raw(")")], #{type => boolean}).
4✔
120
-spec '=<'(value(), value()) -> qast:ast_node().
121
'=<'(A, B) ->
122
    qast:exp([qast:raw("("), A, qast:raw(" <= "), B, qast:raw(")")], #{type => boolean}).
4✔
123

124
-spec 'is'(value(), value()) -> qast:ast_node().
125
is(A, B) ->
126
    qast:exp([A, qast:raw(" is "), B], #{type => boolean}).
4✔
127

128
-spec 'is_null'(value()) -> qast:ast_node().
129
is_null(A) ->
130
    is(A, qast:raw("null")).
4✔
131

132
%% @TODO type opts
133
-spec '+'(value(), value()) -> qast:ast_node().
134
'+'(A, B) ->
135
    qast:exp([qast:raw("("), A, qast:raw(" + "), B, qast:raw(")")]).
8✔
136
-spec '-'(value(), value()) -> qast:ast_node().
137
'-'(A, B) ->
138
    qast:exp([qast:raw("("), A, qast:raw(" - "), B, qast:raw(")")]).
4✔
139
-spec '*'(value(), value()) -> qast:ast_node().
140
'*'(A, B) ->
141
    qast:exp([qast:raw("("), A, qast:raw(" * "), B, qast:raw(")")]).
8✔
142
-spec '/'(value(), value()) -> qast:ast_node().
143
'/'(A, B) ->
144
    qast:exp([qast:raw("("), A, qast:raw(" / "), B, qast:raw(")")]).
4✔
145

146
-spec 'abs'(value()) -> qast:ast_node().
147
abs(A) ->
148
    qast:exp([qast:raw("abs("), A, qast:raw(")")], qast:opts(A)).
4✔
149

150
%% = LIKE ======================================================================
151

152
-spec '~'(value(), value()) -> qast:ast_node().
153
'~'(A, B) ->
154
    qast:exp([qast:raw("("), A, qast:raw(" ~ "), B, qast:raw(")")], #{type => boolean}).
4✔
155
-spec '~*'(value(), value()) -> qast:ast_node().
156
'~*'(A, B) ->
157
    qast:exp([qast:raw("("), A, qast:raw(" ~* "), B, qast:raw(")")], #{type => boolean}).
4✔
158
like(A, B) ->
159
    qast:exp([A, qast:raw(" like "), B], #{type => boolean}).
4✔
160
ilike(A, B) ->
161
    qast:exp([A, qast:raw(" ilike "), B], #{type => boolean}).
4✔
162

163
%% = Aggregators ===============================================================
164

165
-spec sum(qast:ast_node()) -> qast:ast_node().
166
sum(Ast) ->
167
    call("sum", [Ast], qast:opts(Ast)).
4✔
168

169
-spec count(qast:ast_node()) -> qast:ast_node().
170
count(Ast) ->
171
    call("count", [Ast], #{type => integer}).
12✔
172

173
-spec 'min'(value()) -> qast:ast_node().
174
min(Ast) ->
175
    call("min", [Ast], qast:opts(Ast)).
4✔
176

177
-spec 'max'(value()) -> qast:ast_node().
178
max(Ast) ->
179
    call("max", [Ast], qast:opts(Ast)).
8✔
180

181
-spec 'distinct'(value()) -> qast:ast_node().
182
distinct(Ast) ->
183
    call("distinct ", [Ast], qast:opts(Ast)).
4✔
184

185
-spec 'array_agg'(value()) -> qast:ast_node().
186
array_agg(Ast) ->
187
    Opts = qast:opts(Ast),
4✔
188
    Type = maps:get(type, Opts, undefined),
4✔
189
    NewOpts = Opts#{type => {array, Type}},
4✔
190
    call("array_agg", [Ast], NewOpts).
4✔
191

192
-spec 'trunc'(value(), qast:ast_node() | non_neg_integer()) -> qast:ast_node().
193
'trunc'(V, N) ->
194
    call("trunc", [V, N], qast:opts(V)).
8✔
195

196
%% = Math ======================================================================
197

198
-spec 'min'(value(), value()) -> qast:ast_node().
199
min(A, B) ->
200
    qast:exp([qast:raw("LEAST("), A, qast:raw(","), B, qast:raw(")")], qast:opts(A)).
4✔
201

202
-spec 'max'(value(), value()) -> qast:ast_node().
203
max(A, B) ->
204
    qast:exp([qast:raw("GREATEST("), A, qast:raw(","), B, qast:raw(")")], qast:opts(A)).
4✔
205

206
%% = Additional operations =====================================================
207

208
-spec row(#{atom() => qast:ast_node()}) -> qast:ast_node().
209
row(Fields) when is_map(Fields) ->
210
    row(undefined, Fields).
4✔
211

212
-spec row(Model :: module(), #{atom() => qast:ast_node()}) -> qast:ast_node().
213
row(Model, Fields) when is_map(Fields) ->
214
    FieldsList = ?MAPS_TO_LIST(Fields),
4✔
215
    Type = {record, {model, Model, [{F, qast:opts(Node)} || {F, Node} <- FieldsList]}},
4✔
216
    qast:exp([
4✔
217
        qast:raw("row("),
218
        qast:join([Node || {_F, Node} <- FieldsList], qast:raw(",")),
16✔
219
        qast:raw(")")
220
    ], #{type => Type}).
221

222
coalesce([H|_]=List) ->
223
    qast:exp([
4✔
224
        qast:raw("coalesce("),
225
        qast:join([Node || Node <- List], qast:raw(",")),
12✔
226
        qast:raw(")")
227
    ], maps:with([type], qast:opts(H))).
228

229
in(A, #query{}=Q) ->
230
    qast:exp([A, qast:raw(" in ("), qsql:select(Q), qast:raw(")")], #{type => boolean});
4✔
231
in(A, [Item]) ->
UNCOV
232
    '=:='(A, Item);
×
233
in(A, B) ->
234
    qast:exp([A, qast:raw(" = ANY("), B, qast:raw(")")], #{type => boolean}).
8✔
235

236
exists(#query{}=Q) ->
237
    qast:exp([qast:raw("exists ("), qsql:select(Q), qast:raw(")")], #{type => boolean}).
4✔
238

239
-spec call(iodata(), [value()], qast:opts()) -> qast:ast_node().
240
call(FunName, Args, Opts) ->
241
    qast:exp([
44✔
242
        qast:raw([FunName, "("]),
243
        qast:join(Args, qast:raw(",")),
244
        qast:raw(")")
245
    ], Opts).
246

247
%% = Array oprterations ========================================================
248

249
'@>'(A, B) ->
250
    qast:exp([A, qast:raw(" @> "), B], #{type => boolean}).
4✔
251

252
%% = Type functions ============================================================
253

254
as(Ast, Type) ->
255
    Opts = qast:opts(Ast),
4✔
256
    qast:exp([
4✔
257
        qast:raw("("), Ast, qast:raw(")::"),
258
        qast:raw(type_str(Type))
259
    ], Opts#{type => Type}).
260

261
set_type(Ast, Type) ->
262
    Opts = qast:opts(Ast),
4✔
263
    qast:set_opts(Ast, Opts#{type => Type}).
4✔
264

265
type_str(Atom) when is_atom(Atom) ->
266
    atom_to_binary(Atom, latin1);
12✔
267
type_str({array, Atom}) when is_atom(Atom) ->
268
    iolist_to_binary([type_str(Atom), "[]"]);
4✔
269
type_str({Type, Args}) when Type =/= array ->
270
    iolist_to_binary([
8✔
271
        to_iodata(Type),
272
        "(", join([to_iodata(A) || A <- Args], ","), ")"
12✔
273
    ]).
274

275
to_iodata(Atom) when is_atom(Atom) ->
276
    atom_to_list(Atom);
8✔
277
to_iodata(D) when is_list(D); is_binary(D) ->
278
    D;
4✔
279
to_iodata(Int) when is_integer(Int) ->
280
    integer_to_list(Int);
4✔
281
to_iodata(Float) when is_float(Float) ->
282
    io_lib:format("~p", [Float]).
4✔
283

284
join([], _) -> [];
4✔
285
join([H|T],Sep) -> [H|[[Sep,E]||E<-T]].
4✔
286

287
-ifdef(TEST).
288
-include_lib("eunit/include/eunit.hrl").
289

290
type_str_test() ->
291
    ?assertEqual(<<"bigint">>, type_str(bigint)),
4✔
292
    ?assertEqual(<<"int[]">>, type_str({array, int})),
4✔
293
    ?assertEqual(<<"custom()">>, type_str({custom, []})),
4✔
294
    ?assertEqual(<<"custom(a,1,2.0)">>, type_str({custom, [<<"a">>, 1, 2.0]})).
4✔
295

296
-endif.
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