./ct_report/coverage/rdbms_queries.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : rdbms_queries.erl
3 %%% Author : Mickael Remond <mremond@process-one.net>
4 %%% Purpose : RDBMS queries dependind on back-end
5 %%% Created : by Mickael Remond <mremond@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(rdbms_queries).
27 -author("mremond@process-one.net").
28
29 -export([get_db_type/0,
30 begin_trans/0,
31 get_db_specific_limits/0,
32 get_db_specific_limits/1,
33 get_db_specific_limits_binaries/0,
34 get_db_specific_limits_binaries/1,
35 get_db_specific_offset/2,
36 add_limit_arg/2,
37 limit_offset_sql/0,
38 limit_offset_args/2,
39 sql_transaction/2,
40 count_records_where/3,
41 create_bulk_insert_query/3]).
42
43 -export([join/2,
44 prepare_upsert/6,
45 prepare_upsert/7,
46 prepare_upsert_many/7,
47 execute_upsert/5, execute_upsert/6,
48 execute_upsert_many/4, execute_upsert_many/5,
49 request_upsert/5]).
50
51 -ignore_xref([
52 execute_upsert/6, count_records_where/3,
53 execute_upsert_many/5,
54 get_db_specific_limits/1, get_db_specific_offset/2, get_db_type/0
55 ]).
56
57 %% We have only two compile time options for db queries:
58 %%-define(generic, true).
59 %%-define(mssql, true).
60 -ifndef(mssql).
61 -undef(generic).
62 -endif.
63
64 -define(RDBMS_TYPE, (mongoose_rdbms:db_type())).
65
66 -include("mongoose.hrl").
67
68 -ifdef(gen_server_request_id).
69 -type request_id() :: gen_server:request_id().
70 -else.
71 -type request_id() :: term().
72 -endif.
73
74 %
75 %% -----------------
76 %% Common functions
77
78 join([], _Sep) ->
79
:-(
[];
80 join([H|T], Sep) ->
81
:-(
[H, [[Sep, X] || X <- T]].
82
83 %% -----------------
84 %% Generic queries
85
86 get_db_type() ->
87
:-(
?RDBMS_TYPE.
88
89 -spec execute_upsert(HostType :: mongooseim:host_type_or_global(),
90 Name :: atom(),
91 InsertParams :: [any()],
92 UpdateParams :: [any()],
93 UniqueKeyValues :: [any()]) -> mongoose_rdbms:query_result().
94 execute_upsert(HostType, Name, InsertParams, UpdateParams, UniqueKeyValues) ->
95
:-(
execute_upsert(HostType, default, Name, InsertParams, UpdateParams, UniqueKeyValues).
96
97 -spec execute_upsert(HostType :: mongooseim:host_type_or_global(),
98 PoolTag :: mongoose_wpool:tag(),
99 Name :: atom(),
100 InsertParams :: [any()],
101 UpdateParams :: [any()],
102 UniqueKeyValues :: [any()]) -> mongoose_rdbms:query_result().
103 execute_upsert(HostType, PoolTag, Name, InsertParams, UpdateParams, UniqueKeyValues) ->
104
:-(
case {mongoose_rdbms:db_engine(HostType), mongoose_rdbms:db_type()} of
105 {mysql, _} ->
106
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, InsertParams ++ UpdateParams);
107 {pgsql, _} ->
108
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, InsertParams ++ UpdateParams);
109 {odbc, mssql} ->
110
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, UniqueKeyValues ++ InsertParams ++ UpdateParams);
111
:-(
NotSupported -> erlang:error({rdbms_not_supported, NotSupported})
112 end.
113
114 -spec execute_upsert_many(HostType :: mongooseim:host_type_or_global(),
115 Name :: atom(),
116 InsertParams :: [any()],
117 UpdateParams :: [any()]) -> mongoose_rdbms:query_result().
118 execute_upsert_many(HostType, Name, InsertParams, UpdateParams) ->
119
:-(
execute_upsert_many(HostType, default, Name, InsertParams, UpdateParams).
120
121 -spec execute_upsert_many(HostType :: mongooseim:host_type_or_global(),
122 PoolTag :: mongoose_wpool:tag(),
123 Name :: atom(),
124 InsertParams :: [any()],
125 UpdateParams :: [any()]) -> mongoose_rdbms:query_result().
126 execute_upsert_many(HostType, PoolTag, Name, InsertParams, UpdateParams) ->
127
:-(
case {mongoose_rdbms:db_engine(HostType), mongoose_rdbms:db_type()} of
128 {mysql, _} ->
129
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, InsertParams);
130 {pgsql, _} ->
131
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, InsertParams ++ UpdateParams);
132 {odbc, mssql} ->
133
:-(
mongoose_rdbms:execute(HostType, PoolTag, Name, InsertParams);
134
:-(
NotSupported -> erlang:error({rdbms_not_supported, NotSupported})
135 end.
136
137 -spec request_upsert(HostType :: mongooseim:host_type_or_global(),
138 Name :: atom(),
139 InsertParams :: [any()],
140 UpdateParams :: [any()],
141 UniqueKeyValues :: [any()]) -> request_id().
142 request_upsert(HostType, Name, InsertParams, UpdateParams, UniqueKeyValues) ->
143
:-(
case {mongoose_rdbms:db_engine(HostType), mongoose_rdbms:db_type()} of
144 {mysql, _} ->
145
:-(
mongoose_rdbms:execute_request(HostType, Name, InsertParams ++ UpdateParams);
146 {pgsql, _} ->
147
:-(
mongoose_rdbms:execute_request(HostType, Name, InsertParams ++ UpdateParams);
148 {odbc, mssql} ->
149
:-(
mongoose_rdbms:execute_request(HostType, Name, UniqueKeyValues ++ InsertParams ++ UpdateParams);
150
:-(
NotSupported -> erlang:error({rdbms_not_supported, NotSupported})
151 end.
152
153 %% @doc
154 %% This functions prepares query for inserting a row or updating it if already exists
155 %% Updates can be either fields or literal expressions like "column = tab.column + 1"
156 -spec prepare_upsert(HostType :: mongooseim:host_type_or_global(),
157 QueryName :: mongoose_rdbms:query_name(),
158 TableName :: atom(),
159 InsertFields :: [binary()],
160 Updates :: [binary() | {assignment | expression, binary(), binary()}],
161 UniqueKeyFields :: [binary()]) ->
162 {ok, QueryName :: mongoose_rdbms:query_name()} | {error, already_exists}.
163 prepare_upsert(HostType, Name, Table, InsertFields, Updates, UniqueKeyFields) ->
164
:-(
prepare_upsert(HostType, Name, Table, InsertFields, Updates, UniqueKeyFields, none).
165
166 -spec prepare_upsert(HostType :: mongooseim:host_type_or_global(),
167 QueryName :: mongoose_rdbms:query_name(),
168 TableName :: atom(),
169 InsertFields :: [ColumnName :: binary()],
170 Updates :: [binary() | {assignment | expression, binary(), binary()}],
171 UniqueKeyFields :: [binary()],
172 IncrementalField :: none | binary()) ->
173 {ok, QueryName :: mongoose_rdbms:query_name()} | {error, already_exists}.
174 prepare_upsert(HostType, Name, Table, InsertFields, Updates, UniqueKeyFields, IncrementalField) ->
175
:-(
SQL = upsert_query(HostType, Table, InsertFields, Updates, UniqueKeyFields, IncrementalField),
176
:-(
Query = iolist_to_binary(SQL),
177
:-(
?LOG_DEBUG(#{what => rdbms_upsert_query, name => Name, query => Query}),
178
:-(
Fields = prepared_upsert_fields(InsertFields, Updates, UniqueKeyFields),
179
:-(
mongoose_rdbms:prepare(Name, Table, Fields, Query).
180
181 prepared_upsert_fields(InsertFields, Updates, UniqueKeyFields) ->
182
:-(
UpdateFields = lists:filtermap(fun get_field_name/1, Updates),
183
:-(
case mongoose_rdbms:db_type() of
184 mssql ->
185
:-(
UniqueKeyFields ++ InsertFields ++ UpdateFields;
186 _ ->
187
:-(
InsertFields ++ UpdateFields
188 end.
189
190 prepared_upsert_many_fields(RecordCount, InsertFields, Updates, _UniqueKeyFields) ->
191
:-(
InsertFieldsMany = lists:append(lists:duplicate(RecordCount, InsertFields)),
192
:-(
UpdateFields = lists:filtermap(fun get_field_name/1, Updates),
193
:-(
case mongoose_rdbms:db_type() of
194 mssql ->
195
:-(
InsertFieldsMany;
196 _ ->
197
:-(
InsertFieldsMany ++ UpdateFields
198 end.
199
200 -spec prepare_upsert_many(HostType :: mongooseim:host_type_or_global(),
201 RecordCount :: pos_integer(),
202 QueryName :: mongoose_rdbms:query_name(),
203 TableName :: atom(),
204 InsertFields :: [ColumnName :: binary()],
205 Updates :: [binary() | {assignment | expression, binary(), binary()}],
206 UniqueKeyFields :: [binary()]) ->
207 {ok, QueryName :: mongoose_rdbms:query_name()} | {error, already_exists}.
208 prepare_upsert_many(HostType, RecordCount, Name, Table, InsertFields, Updates, UniqueKeyFields) ->
209
:-(
SQL = upsert_query_many(HostType, RecordCount, Table, InsertFields, Updates, UniqueKeyFields),
210
:-(
Query = iolist_to_binary(SQL),
211
:-(
?LOG_DEBUG(#{what => rdbms_upsert_query, name => Name, query => Query}),
212
:-(
Fields = prepared_upsert_many_fields(RecordCount, InsertFields, Updates, UniqueKeyFields),
213
:-(
mongoose_rdbms:prepare(Name, Table, Fields, Query).
214
215 upsert_query(HostType, Table, InsertFields, Updates, UniqueKeyFields, IncrementalField) ->
216
:-(
case {mongoose_rdbms:db_engine(HostType), mongoose_rdbms:db_type()} of
217 {mysql, _} ->
218
:-(
upsert_mysql_query(Table, InsertFields, Updates, UniqueKeyFields, IncrementalField);
219 {pgsql, _} ->
220
:-(
upsert_pgsql_query(Table, InsertFields, Updates, UniqueKeyFields, IncrementalField);
221 {odbc, mssql} ->
222
:-(
upsert_mssql_query(Table, InsertFields, Updates, UniqueKeyFields);
223
:-(
NotSupported -> erlang:error({rdbms_not_supported, NotSupported})
224 end.
225
226 upsert_query_many(HostType, RecordCount, Table, InsertFields, Updates, UniqueKeyFields) ->
227
:-(
case {mongoose_rdbms:db_engine(HostType), mongoose_rdbms:db_type()} of
228 {mysql, _} ->
229
:-(
upsert_many_mysql_query(RecordCount, Table, InsertFields);
230 {pgsql, _} ->
231
:-(
upsert_many_pgsql_query(RecordCount, Table, InsertFields, Updates, UniqueKeyFields);
232 {odbc, mssql} ->
233
:-(
upsert_many_mssql_query(RecordCount, Table, InsertFields, Updates, UniqueKeyFields);
234
:-(
NotSupported -> erlang:error({rdbms_not_supported, NotSupported})
235 end.
236
237 mysql_and_pgsql_insert_many(RecordCount, Table, Columns) ->
238
:-(
JoinedFields = join(Columns, <<", ">>),
239
:-(
Placeholders = lists:duplicate(length(Columns), $?),
240
:-(
Values = ["(", join(Placeholders, ", "), ")"],
241
:-(
ManyValues = join(lists:duplicate(RecordCount, Values), ", "),
242
:-(
["INSERT INTO ", atom_to_binary(Table, utf8), " (",
243 JoinedFields, ") VALUES ", ManyValues].
244
245 upsert_mysql_query(Table, InsertFields, Updates, [Key | _], IncrementalField) ->
246
:-(
Insert = mysql_and_pgsql_insert_many(1, Table, InsertFields),
247
:-(
OnConflict = mysql_on_conflict(Table, Updates, Key, IncrementalField),
248
:-(
[Insert, OnConflict].
249
250 upsert_pgsql_query(Table, InsertFields, Updates, UniqueKeyFields, IncrementalField) ->
251
:-(
Insert = mysql_and_pgsql_insert_many(1, Table, InsertFields),
252
:-(
OnConflict = pgsql_on_conflict(Updates, UniqueKeyFields),
253
:-(
WhereIncrements = pgsql_ensure_increments(Table, IncrementalField),
254
:-(
[Insert, OnConflict, WhereIncrements].
255
256 upsert_many_mysql_query(RecordCount, Table, InsertFields) ->
257
:-(
TableName = atom_to_list(Table),
258
:-(
Columns = join(InsertFields, ", "),
259
:-(
Placeholders = lists:duplicate(length(InsertFields), $?),
260
:-(
Values = ["(", join(Placeholders, ", "), ")"],
261
:-(
ManyValues = join(lists:duplicate(RecordCount, Values), ", "),
262
:-(
["REPLACE INTO ", TableName, " (", Columns, ") ",
263 " VALUES ", ManyValues].
264
265 upsert_many_pgsql_query(RecordCount, Table, InsertFields, Updates, UniqueKeyFields) ->
266
:-(
Insert = mysql_and_pgsql_insert_many(RecordCount, Table, InsertFields),
267
:-(
OnConflict = pgsql_on_conflict(Updates, UniqueKeyFields),
268
:-(
[Insert, OnConflict].
269
270 mysql_on_conflict(_Table, [], Key, _) ->
271 %% Update field to itself (no-op), there is no 'DO NOTHING' in MySQL
272
:-(
[" ON DUPLICATE KEY UPDATE ", Key, " = ", Key];
273 mysql_on_conflict(_Table, UpdateFields, _, none) ->
274
:-(
[" ON DUPLICATE KEY UPDATE ",
275 update_fields_on_conflict(UpdateFields)];
276 mysql_on_conflict(Table, UpdateFields, _, IncrementalField) ->
277
:-(
TableName = atom_to_list(Table),
278
:-(
FieldsWithPlaceHolders = [mysql_fields_with_placeholders(TableName, Update, IncrementalField)
279
:-(
|| Update <- UpdateFields],
280
:-(
IncrUpdates = join(FieldsWithPlaceHolders, ", "),
281
:-(
[" AS alias ON DUPLICATE KEY UPDATE ", IncrUpdates].
282
283 mysql_fields_with_placeholders(TableName, UpdateField, IncrementalField) ->
284
:-(
Alternatives = case UpdateField of
285 {Op, Column, Expression} when Op =:= assignment; Op =:= expression ->
286
:-(
[Expression, ", ", TableName, ".", Column, ")"];
287 Column ->
288
:-(
["? , ", TableName, ".", Column, ")"]
289 end,
290
:-(
[ Column, " = IF(", TableName, ".", IncrementalField, " < alias.", IncrementalField, ", "
291 | Alternatives].
292
293 pgsql_on_conflict([], UniqueKeyFields) ->
294
:-(
JoinedKeys = join(UniqueKeyFields, ", "),
295
:-(
[" ON CONFLICT (", JoinedKeys, ") DO NOTHING"];
296 pgsql_on_conflict(UpdateFields, UniqueKeyFields) ->
297
:-(
JoinedKeys = join(UniqueKeyFields, ", "),
298
:-(
[" ON CONFLICT (", JoinedKeys, ")"
299 " DO UPDATE SET ",
300 update_fields_on_conflict(UpdateFields)].
301
302 update_fields_on_conflict(Updates) ->
303
:-(
FieldsWithPlaceHolders = [get_field_expression(Update) || Update <- Updates],
304
:-(
join(FieldsWithPlaceHolders, ", ").
305
306 pgsql_ensure_increments(_Table, none) ->
307
:-(
[];
308 pgsql_ensure_increments(Table, IncrementalField) ->
309
:-(
TableName = atom_to_list(Table),
310
:-(
[" WHERE ", TableName, ".", IncrementalField, " < EXCLUDED.", IncrementalField].
311
312 upsert_mssql_query(Table, InsertFields, Updates, UniqueKeyFields) ->
313
:-(
UniqueKeysInSelect = [[" ? AS ", Key] || Key <- UniqueKeyFields],
314
:-(
BinTab = atom_to_binary(Table, utf8),
315
:-(
UniqueConstraint = [[BinTab, ".", Key, " = source.", Key] || Key <- UniqueKeyFields],
316
:-(
JoinedInsertFields = join(InsertFields, ", "),
317
:-(
Placeholders = lists:duplicate(length(InsertFields), $?),
318
:-(
["MERGE INTO ", BinTab, " WITH (SERIALIZABLE)"
319 " USING (SELECT ", join(UniqueKeysInSelect, ", "), ")"
320 " AS source (", join(UniqueKeyFields, ", "), ")"
321 " ON (", join(UniqueConstraint, " AND "), ")"
322 " WHEN NOT MATCHED THEN INSERT"
323 " (", JoinedInsertFields, ")"
324 " VALUES (", join(Placeholders, ", "), ")" | mssql_on_conflict(Updates)].
325
326 %% see prepared_upsert_many_fields
327 upsert_many_mssql_query(RecordCount, Table, InsertFields, Updates, UniqueKeyFields) ->
328
:-(
BinTab = atom_to_binary(Table, utf8),
329
:-(
UniqueConstraint = [["tgt.", Key, " = new.", Key] || Key <- UniqueKeyFields],
330
:-(
NewJoinedInsertFields = join([["new.", Column] || Column <- InsertFields], ", "),
331
:-(
JoinedInsertFields = join(InsertFields, ", "),
332
:-(
Placeholders = lists:duplicate(length(InsertFields), $?),
333
:-(
Values = ["(", join(Placeholders, ", "), ")"],
334
:-(
ManyValues = join(lists:duplicate(RecordCount, Values), ", "),
335
:-(
["MERGE ", BinTab, " AS tgt",
336 " USING (VALUES", ManyValues, ")"
337 " AS new (", JoinedInsertFields, ")"
338 " ON (", join(UniqueConstraint, " AND "), ")"
339 " WHEN MATCHED THEN UPDATE SET ", tgt_equals_new(Updates),
340 " WHEN NOT MATCHED THEN INSERT (", JoinedInsertFields, ")",
341 " VALUES(", NewJoinedInsertFields, ");"].
342
343 tgt_equals_new(Updates) ->
344
:-(
join([["tgt.", ColumnName, "=new.", ColumnName] || ColumnName <- Updates], ", ").
345
346
:-(
mssql_on_conflict([]) -> ";";
347 mssql_on_conflict(Updates) ->
348
:-(
[" WHEN MATCHED THEN UPDATE SET ", update_fields_on_conflict(Updates), ";"].
349
350 get_field_expression({Op, ColumnName, Expr}) when Op =:= assignment; Op =:= expression ->
351
:-(
[ColumnName, " = ", Expr];
352 get_field_expression(Field) when is_binary(Field) ->
353
:-(
[Field, " = ?"].
354
355 get_field_name({assignment, Field, _}) when is_binary(Field) ->
356
:-(
false;
357 get_field_name({expression, Field, _}) when is_binary(Field) ->
358
:-(
{true, Field};
359 get_field_name(Field) when is_binary(Field) ->
360
:-(
true.
361
362 %% F can be either a fun or a list of queries
363 %% TODO: We should probably move the list of queries transaction
364 %% wrapper from the mongoose_rdbms module to this one (rdbms_queries)
365 sql_transaction(LServer, F) ->
366
:-(
mongoose_rdbms:sql_transaction(LServer, F).
367
368 begin_trans() ->
369
:-(
begin_trans(?RDBMS_TYPE).
370
371 begin_trans(mssql) ->
372
:-(
rdbms_queries_mssql:begin_trans();
373 begin_trans(_) ->
374
:-(
[<<"BEGIN;">>].
375
376 %% Count number of records in a table given a where clause
377 count_records_where(LServer, Table, WhereClause) ->
378
:-(
mongoose_rdbms:sql_query(
379 LServer,
380 [<<"select count(*) from ">>, Table, " ", WhereClause, ";"]).
381
382 -spec create_bulk_insert_query(Table :: iodata() | atom(), Fields :: [iodata() | atom()],
383 RowsNum :: pos_integer()) ->
384 {iodata(), [binary()]}.
385 create_bulk_insert_query(Table, Fields, RowsNum) when is_atom(Table) ->
386
:-(
create_bulk_insert_query(atom_to_binary(Table, utf8), Fields, RowsNum);
387 create_bulk_insert_query(Table, [Field | _] = Fields, RowsNum) when is_atom(Field) ->
388
:-(
create_bulk_insert_query(Table, [atom_to_binary(F, utf8) || F <- Fields], RowsNum);
389 create_bulk_insert_query(Table, Fields, RowsNum) when RowsNum > 0 ->
390
:-(
JoinedFields = join(Fields, <<", ">>),
391
:-(
Placeholders = lists:duplicate(length(Fields), <<"?">>),
392
:-(
PlaceholderSet = [<<"(">>, join(Placeholders, <<", ">>), <<")">>],
393
:-(
PlaceholderSets = lists:duplicate(RowsNum, PlaceholderSet),
394
:-(
JoinedPlaceholderSets = join(PlaceholderSets, <<", ">>),
395
:-(
Sql = [<<"INSERT INTO ">>, Table, <<" (">>, JoinedFields, <<") "
396 "VALUES ">>, JoinedPlaceholderSets, <<";">>],
397
:-(
Fields2 = lists:append(lists:duplicate(RowsNum, Fields)),
398
:-(
{Sql, Fields2}.
399
400 get_db_specific_limits() ->
401
:-(
do_get_db_specific_limits(?RDBMS_TYPE, "?", true).
402
403 get_db_specific_limits_binaries() ->
404
:-(
{LimitSQL, LimitMSSQL} = get_db_specific_limits(),
405
:-(
{list_to_binary(LimitSQL), list_to_binary(LimitMSSQL)}.
406
407 -spec get_db_specific_limits(integer())
408 -> {SQL :: nonempty_string(), []} | {[], MSSQL::nonempty_string()}.
409 get_db_specific_limits(Limit) ->
410
:-(
LimitStr = integer_to_list(Limit),
411
:-(
do_get_db_specific_limits(?RDBMS_TYPE, LimitStr, false).
412
413 -spec get_db_specific_offset(integer(), integer()) -> iolist().
414 get_db_specific_offset(Offset, Limit) ->
415
:-(
do_get_db_specific_offset(?RDBMS_TYPE, integer_to_list(Offset), integer_to_list(Limit)).
416
417
418 %% Arguments:
419 %% - Type (atom) - database type
420 %% - LimitStr (string) - a field value
421 %% - Wrap (boolean) - add parentheses around a field for MSSQL
422 do_get_db_specific_limits(mssql, LimitStr, _Wrap = false) ->
423
:-(
{"", "TOP " ++ LimitStr};
424 do_get_db_specific_limits(mssql, LimitStr, _Wrap = true) ->
425
:-(
{"", "TOP (" ++ LimitStr ++ ")"};
426 do_get_db_specific_limits(_, LimitStr, _Wrap) ->
427
:-(
{"LIMIT " ++ LimitStr, ""}.
428
429 do_get_db_specific_offset(mssql, Offset, Limit) ->
430
:-(
[" OFFSET ", Offset, " ROWS"
431 " FETCH NEXT ", Limit, " ROWS ONLY"];
432 do_get_db_specific_offset(_, Offset, _Limit) ->
433
:-(
[" OFFSET ", Offset].
434
435 add_limit_arg(Limit, Args) ->
436
:-(
add_limit_arg(?RDBMS_TYPE, Limit, Args).
437
438 add_limit_arg(mssql, Limit, Args) ->
439
:-(
[Limit|Args];
440 add_limit_arg(_, Limit, Args) ->
441
:-(
Args ++ [Limit].
442
443 get_db_specific_limits_binaries(Limit) ->
444
:-(
{LimitSQL, LimitMSSQL} = get_db_specific_limits(Limit),
445
:-(
{list_to_binary(LimitSQL), list_to_binary(LimitMSSQL)}.
446
447 limit_offset_sql() ->
448
:-(
limit_offset_sql(?RDBMS_TYPE).
449
450 limit_offset_sql(mssql) ->
451
:-(
<<" OFFSET (?) ROWS FETCH NEXT (?) ROWS ONLY">>;
452 limit_offset_sql(_) ->
453
:-(
<<" LIMIT ? OFFSET ?">>.
454
455 limit_offset_args(Limit, Offset) ->
456
:-(
limit_offset_args(?RDBMS_TYPE, Limit, Offset).
457
458
:-(
limit_offset_args(mssql, Limit, Offset) -> [Offset, Limit];
459
:-(
limit_offset_args(_, Limit, Offset) -> [Limit, Offset].
Line Hits Source