ul_jsonwrt_object_close(json);
}
+static int cast_node(struct libscols_filter *fltr,
+ struct libscols_line *ln,
+ enum filter_data type,
+ struct filter_node *n,
+ struct filter_param **result)
+{
+ struct filter_node *pr;
+ int status = 0, rc;
+ bool x;
+
+ switch (n->type) {
+ case F_NODE_EXPR:
+ /* convert expression to a boolean param */
+ rc = filter_eval_expr(fltr, ln, (struct filter_expr *) n, &status);
+ if (rc)
+ return rc;
+ x = status != 0 ? true : false;
+ pr = filter_new_param(NULL, F_DATA_BOOLEAN, 0, (void *) &x);
+ if (!pr)
+ return -ENOMEM;
+ rc = filter_cast_param(fltr, ln, type, (struct filter_param *) pr, result);
+ filter_unref_node(pr);
+ break;
+ case F_NODE_PARAM:
+ rc = filter_cast_param(fltr, ln, type, (struct filter_param *) n, result);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static enum filter_data node_get_datatype(struct filter_node *n)
+{
+ switch (n->type) {
+ case F_NODE_EXPR:
+ return F_DATA_BOOLEAN;
+ case F_NODE_PARAM:
+ return ((struct filter_param *) n)->type;
+ }
+ return F_DATA_NONE;
+}
+
+static enum filter_data guess_expr_datatype(struct filter_expr *n)
+{
+ enum filter_data type;
+ enum filter_data l = node_get_datatype(n->left),
+ r = node_get_datatype(n->right);
+
+ if (l == r)
+ type = l;
+ else {
+ bool l_holder, r_holder;
+
+ /* for expression like "FOO > 5.5" preffer type defined by a real param
+ * rather than by holder (FOO) */
+ l_holder = is_filter_holder_param(n->left);
+ r_holder = is_filter_holder_param(n->right);
+
+ if (l_holder && !r_holder)
+ type = r;
+ else if (r_holder && !l_holder)
+ type = l;
+ else
+ type = l;
+ }
+
+ DBG(FPARAM, ul_debugobj(n, " expr datatype: %d", type));
+ return type;
+}
+
int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln,
struct filter_expr *n, int *status)
{
int rc = 0;
struct filter_param *l = NULL, *r = NULL;
enum filter_etype oper = n->type;
+ enum filter_data type;
/* logical operators */
switch (oper) {
break;
}
+ type = guess_expr_datatype(n);
+
/* compare data */
- l = (struct filter_param *) n->left;
- r = (struct filter_param *) n->right;
- rc = filter_compare_params(fltr, ln, oper, l, r, status);
+ rc = cast_node(fltr, ln, type, n->left, &l);
+ if (!rc)
+ rc = cast_node(fltr, ln, type, n->right, &r);
+ if (!rc)
+ rc = filter_compare_params(fltr, ln, oper, l, r, status);
+ filter_unref_node((struct filter_node *) l);
+ filter_unref_node((struct filter_node *) r);
return rc;
}
#include "smartcolsP.h"
+static int cast_param(enum filter_data type, struct filter_param *n);
+
+static inline const char *datatype2str(enum filter_data type)
+{
+ static const char *types[] = {
+ [F_DATA_NONE] = "none",
+ [F_DATA_STRING] = "string",
+ [F_DATA_NUMBER] = "number",
+ [F_DATA_FLOAT] = "float",
+ [F_DATA_BOOLEAN] = "boolean"
+ };
+ return types[type];
+}
static int param_set_data(struct filter_param *n, enum filter_data type, const void *data)
{
const char *p;
+ /*DBG(FPARAM, ul_debugobj(n, " set %s data", datatype2str(type)));*/
+
switch (type) {
case F_DATA_STRING:
p = data;
break;
}
- list_add_tail(&n->pr_params, &fltr->params);
+ if (fltr)
+ list_add_tail(&n->pr_params, &fltr->params);
return (struct filter_node *) n;
}
+static struct filter_param *copy_param(struct filter_param *n)
+{
+ return (struct filter_param *) filter_new_param(NULL,
+ n->type, F_HOLDER_NONE, (void *) &n->val);
+}
+
static void param_reset_data(struct filter_param *n)
{
if (n->type == F_DATA_STRING)
ul_jsonwrt_object_close(json);
}
-static int fetch_holder_data(struct libscols_filter *fltr,
+int filter_param_reset_holder(struct filter_param *n)
+{
+ if (!n->holder)
+ return 0;
+ if (!n->col)
+ return -EINVAL;
+
+ param_reset_data(n);
+
+ if (n->type != F_DATA_NONE)
+ return 0; /* already set */
+
+ switch (n->col->json_type) {
+ case SCOLS_JSON_NUMBER:
+ n->type = F_DATA_NUMBER;
+ break;
+ case SCOLS_JSON_BOOLEAN:
+ n->type = F_DATA_BOOLEAN;
+ break;
+ case SCOLS_JSON_FLOAT:
+ n->type = F_DATA_FLOAT;
+ break;
+ case SCOLS_JSON_STRING:
+ default:
+ n->type = F_DATA_STRING;
+ break;
+ }
+
+ DBG(FPARAM, ul_debugobj(n, "holder %s type: %s", n->holder_name, datatype2str(n->type)));
+ return 0;
+}
+
+static int fetch_holder_data(struct libscols_filter *fltr __attribute__((__unused__)),
struct filter_param *n, struct libscols_line *ln)
{
const char *data;
+ enum filter_data type = n->type;
int rc;
- if (n->holder != F_HOLDER_COLUMN)
+ if (n->has_value || n->holder != F_HOLDER_COLUMN)
return 0;
if (!n->col) {
- DBG(FLTR, ul_debugobj(fltr, "no column for %s holder", n->holder_name));
+ DBG(FPARAM, ul_debugobj(n, "no column for %s holder", n->holder_name));
return -EINVAL;
}
+ DBG(FPARAM, ul_debugobj(n, "fetching %s data", n->holder_name));
- DBG(FLTR, ul_debugobj(fltr, "fetching %s data", n->holder_name));
-
- param_reset_data(n);
-
+ /* read column data, use it as string */
data = scols_line_get_column_data(ln, n->col);
rc = param_set_data(n, F_DATA_STRING, data);
+ /* cast to the wanted type */
+ if (rc == 0 && type != F_DATA_NONE)
+ rc = cast_param(type, n);
return rc;
}
DBG(FLTR, ul_debugobj(fltr, "eval param"));
- if (!n->has_value && n->holder)
- rc = fetch_holder_data(fltr, n, ln);
-
+ rc = fetch_holder_data(fltr, n, ln);
if (!n->has_value || rc) {
*status = 0;
goto done;
return 0;
}
+/* call filter_cast_param() to be sure that param data are ready (fetched from
+ * holder, etc.) */
int filter_compare_params(struct libscols_filter *fltr __attribute__((__unused__)),
struct libscols_line *ln __attribute__((__unused__)),
enum filter_etype oper,
return rc;
}
+static int string_cast(enum filter_data type, struct filter_param *n)
+{
+ char *str = n->val.str;
+
+ if (type == F_DATA_STRING)
+ return 0;
+
+ n->val.str = NULL;
+
+ switch (type) {
+ case F_DATA_NUMBER:
+ {
+ uint64_t num;
+ int rc = ul_strtou64(str, &num, 10);
+ if (rc)
+ return rc;
+ n->val.num = num;
+ break;
+ }
+ case F_DATA_FLOAT:
+ {
+ long double num;
+ int rc = ul_strtold(str, &num);
+ if (rc)
+ return rc;
+ n->val.fnum = num;
+ break;
+ }
+ case F_DATA_BOOLEAN:
+ {
+ bool x = (!str || !*str
+ || strcasecmp(str, "false") == 0
+ || strcasecmp(str, "0") == 0) ? false : true;
+ n->val.boolean = x;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ free(str);
+ return 0;
+}
+
+static int number_cast(enum filter_data type, struct filter_param *n)
+{
+ unsigned long long num = n->val.num;
+
+ switch (type) {
+ case F_DATA_STRING:
+ n->val.str = NULL;
+ if (asprintf(&n->val.str, "%llu", num) <= 0)
+ return -ENOMEM;
+ break;
+ case F_DATA_NUMBER:
+ break;
+ case F_DATA_FLOAT:
+ n->val.fnum = num;
+ break;
+ case F_DATA_BOOLEAN:
+ n->val.boolean = num > 0 ? true : false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int float_cast(enum filter_data type, struct filter_param *n)
+{
+ long double fnum = n->val.fnum;
+
+ switch (type) {
+ case F_DATA_STRING:
+ n->val.str = NULL;
+ if (asprintf(&n->val.str, "%Lg", fnum) <= 0)
+ return -ENOMEM;
+ break;
+ case F_DATA_NUMBER:
+ n->val.num = fnum;
+ break;
+ case F_DATA_FLOAT:
+ break;;
+ case F_DATA_BOOLEAN:
+ n->val.boolean = fnum > 0.0 ? true : false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bool_cast(enum filter_data type, struct filter_param *n)
+{
+ bool x = n->val.boolean;
+
+ switch (type) {
+ case F_DATA_STRING:
+ n->val.str = NULL;
+ if (asprintf(&n->val.str, "%s", x ? "true" : "false") <= 0)
+ return -ENOMEM;
+ break;
+ case F_DATA_NUMBER:
+ n->val.num = x ? 1 : 0;
+ break;
+ case F_DATA_FLOAT:
+ n->val.fnum = x ? 1.0 : 0.0;
+ break;
+ case F_DATA_BOOLEAN:
+ break;;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cast_param(enum filter_data type, struct filter_param *n)
+{
+ int rc;
+ enum filter_data orgtype = n->type;
+
+ if (!n->has_value)
+ return -EINVAL;
+ if (type == orgtype)
+ return 0;
+
+ if (orgtype == F_DATA_STRING)
+ DBG(FPARAM, ul_debugobj(n, " casting \"%s\" to %s", n->val.str, datatype2str(type)));
+ else
+ DBG(FPARAM, ul_debugobj(n, " casting %s to %s", datatype2str(orgtype), datatype2str(type)));
+
+ switch (orgtype) {
+ case F_DATA_STRING:
+ rc = string_cast(type, n);
+ break;
+ case F_DATA_NUMBER:
+ rc = number_cast(type, n);
+ break;
+ case F_DATA_FLOAT:
+ rc = float_cast(type, n);
+ break;
+ case F_DATA_BOOLEAN:
+ rc = bool_cast(type, n);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc == 0)
+ n->type = type;
+
+ if (rc)
+ DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc));
+ return rc;
+}
+
+int filter_cast_param(struct libscols_filter *fltr,
+ struct libscols_line *ln,
+ enum filter_data type,
+ struct filter_param *n,
+ struct filter_param **result)
+{
+ int rc;
+ enum filter_data orgtype = n->type;
+
+ rc = fetch_holder_data(fltr, n, ln);
+ if (rc)
+ return rc;
+ if (!n->has_value)
+ return -EINVAL;
+
+ if (type == orgtype) {
+ filter_ref_node((struct filter_node *) n); /* caller wants to call filter_unref_node() for the result */
+ *result = n;
+ return 0;
+ }
+
+ *result = copy_param(n);
+ if (!*result)
+ return -ENOMEM;
+ rc = cast_param(type, *result);
+
+ DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc));
+ return rc;
+}
+
int filter_next_param(struct libscols_filter *fltr,
struct libscols_iter *itr, struct filter_param **prm)
{