Skip to content

Commit 430d4c8

Browse files
committed
db: Add IS NULL and IS NOT NULL unary operators
If you try to (mysql) prepare a statement with "... column IS ?" you'd get an error. For mysql, you could fix this by using the "... column <=> ?" special operator ('IS NOT DISTINCT FROM'), but that's not standard SQL. Instead, you can now do: update_cols[0] = &some_column; update_ops[0] = OP_IS_NULL; update_vals[0].nul = 1; You still need to set .nul=1 because prepared statements have to consume a single value.
1 parent 7af3886 commit 430d4c8

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

db/db_op.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (C) 2021 OpenSIPS
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version
10+
*
11+
* opensips is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
#include "db_op.h"
22+
23+
24+
const char OP_IS_NULL[] = " IS NULL";
25+
const char OP_IS_NOT_NULL[] = " IS NOT NULL";

db/db_op.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@
4040
/** operator negation */
4141
#define OP_NEQ "!="
4242

43+
/* Special unary operators here, because mysql prepared statements do
44+
* not cope with 'column IS ?' (they would cope with 'column <=> ?' but
45+
* that's not standard SQL).
46+
* (Declared as char array instead of define, so they can be pointer
47+
* compared in code.) */
48+
49+
/** unary operator: IS NULL */
50+
extern const char OP_IS_NULL[]; /* " IS NULL" */
51+
/** unary operator: IS NOT NULL */
52+
extern const char OP_IS_NOT_NULL[]; /* " IS NOT NULL" */
53+
4354

4455
/**
4556
* This type represents an expression operator uses for SQL queries.

db/db_ut.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,16 +336,33 @@ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _
336336

337337
for(i = 0; i < _n; i++) {
338338
if (_o) {
339-
ret = snprintf(_b + len, _l - len, "%.*s%s",
340-
_k[i]->len, _k[i]->s, _o[i]);
341-
if (ret < 0 || ret >= (_l - len)) goto error;
342-
len += ret;
339+
/* Special case: if there is unary operator and
340+
* we use a prepared statement, we must still
341+
* consume one value, even though we're not
342+
* using it. Otherwise the number of values will
343+
* be incorrect. */
344+
if ((_o[i] == OP_IS_NULL || _o[i] == OP_IS_NOT_NULL) &&
345+
CON_HAS_PS(_c)) {
346+
/* ?=NULL will never be true; so we're
347+
* safely consuming a single argument */
348+
ret = snprintf(_b + len, _l - len, "(%.*s%s OR ?=NULL)",
349+
_k[i]->len, _k[i]->s, _o[i]);
350+
if (ret < 0 || ret >= (_l - len)) goto error;
351+
len += ret;
352+
} else {
353+
ret = snprintf(_b + len, _l - len, "%.*s%s",
354+
_k[i]->len, _k[i]->s, _o[i]);
355+
if (ret < 0 || ret >= (_l - len)) goto error;
356+
len += ret;
357+
}
343358
} else {
344359
ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s);
345360
if (ret < 0 || ret >= (_l - len)) goto error;
346361
len += ret;
347362
}
348-
if (CON_HAS_PS(_c)) {
363+
if (_o && (_o[i] == OP_IS_NULL || _o[i] == OP_IS_NOT_NULL)) {
364+
;
365+
} else if (CON_HAS_PS(_c)) {
349366
*(_b+len++) = '?';
350367
} else {
351368
l = _l - len;

0 commit comments

Comments
 (0)