Skip to content

Commit 4942f35

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 7f7118b commit 4942f35

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

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
@@ -334,16 +334,33 @@ int db_print_where(const db_con_t* _c, char* _b, const int _l, const db_key_t* _
334334

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

0 commit comments

Comments
 (0)