diff --git a/db/db_op.c b/db/db_op.c new file mode 100644 index 00000000000..a94cc7fafdc --- /dev/null +++ b/db/db_op.c @@ -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"; diff --git a/db/db_op.h b/db/db_op.h index 8938dbda17b..2f93dc2fc85 100644 --- a/db/db_op.h +++ b/db/db_op.h @@ -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. diff --git a/db/db_ut.c b/db/db_ut.c index 674808295a6..4b3e6cf34c2 100644 --- a/db/db_ut.c +++ b/db/db_ut.c @@ -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;