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]. |