Skip to content

Commit

Permalink
db: Add IS NULL and IS NOT NULL unary operators
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wdoekes committed Jul 26, 2021
1 parent 7af3886 commit 430d4c8
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
25 changes: 25 additions & 0 deletions db/db_op.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2021 OpenSIPS
*
* This file is part of opensips, a free SIP server.
*
* opensips is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* opensips is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "db_op.h"


const char OP_IS_NULL[] = " IS NULL";
const char OP_IS_NOT_NULL[] = " IS NOT NULL";
11 changes: 11 additions & 0 deletions db/db_op.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@
/** operator negation */
#define OP_NEQ "!="

/* Special unary operators here, because mysql prepared statements do
* not cope with 'column IS ?' (they would cope with 'column <=> ?' but
* that's not standard SQL).
* (Declared as char array instead of define, so they can be pointer
* compared in code.) */

/** unary operator: IS NULL */
extern const char OP_IS_NULL[]; /* " IS NULL" */
/** unary operator: IS NOT NULL */
extern const char OP_IS_NOT_NULL[]; /* " IS NOT NULL" */


/**
* This type represents an expression operator uses for SQL queries.
Expand Down
27 changes: 22 additions & 5 deletions db/db_ut.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,33 @@ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _

for(i = 0; i < _n; i++) {
if (_o) {
ret = snprintf(_b + len, _l - len, "%.*s%s",
_k[i]->len, _k[i]->s, _o[i]);
if (ret < 0 || ret >= (_l - len)) goto error;
len += ret;
/* Special case: if there is unary operator and
* we use a prepared statement, we must still
* consume one value, even though we're not
* using it. Otherwise the number of values will
* be incorrect. */
if ((_o[i] == OP_IS_NULL || _o[i] == OP_IS_NOT_NULL) &&
CON_HAS_PS(_c)) {
/* ?=NULL will never be true; so we're
* safely consuming a single argument */
ret = snprintf(_b + len, _l - len, "(%.*s%s OR ?=NULL)",
_k[i]->len, _k[i]->s, _o[i]);
if (ret < 0 || ret >= (_l - len)) goto error;
len += ret;
} else {
ret = snprintf(_b + len, _l - len, "%.*s%s",
_k[i]->len, _k[i]->s, _o[i]);
if (ret < 0 || ret >= (_l - len)) goto error;
len += ret;
}
} else {
ret = snprintf(_b + len, _l - len, "%.*s=", _k[i]->len, _k[i]->s);
if (ret < 0 || ret >= (_l - len)) goto error;
len += ret;
}
if (CON_HAS_PS(_c)) {
if (_o && (_o[i] == OP_IS_NULL || _o[i] == OP_IS_NOT_NULL)) {
;
} else if (CON_HAS_PS(_c)) {
*(_b+len++) = '?';
} else {
l = _l - len;
Expand Down

0 comments on commit 430d4c8

Please sign in to comment.