void w_query_delref(w_query *query) { uint32_t i; if (!w_refcnt_del(&query->refcnt)) { return; } for (i = 0; i < query->npaths; i++) { if (query->paths[i].name) { w_string_delref(query->paths[i].name); } } free(query->paths); if (query->since_spec) { w_clockspec_free(query->since_spec); } if (query->expr) { w_query_expr_delref(query->expr); } free(query); }
static void update_subscription_ticks(struct watchman_client_subscription *sub, w_query_res *res) { // create a new spec that will be used the next time if (sub->query->since_spec) { w_clockspec_free(sub->query->since_spec); } sub->query->since_spec = w_clockspec_new_clock(res->root_number, res->ticks); }
void w_assess_trigger(w_root_t *root, struct watchman_trigger_command *cmd) { w_query_res res; struct w_clockspec *since_spec = cmd->query->since_spec; if (since_spec && since_spec->tag == w_cs_clock) { w_log(W_LOG_DBG, "running trigger rules! since %" PRIu32 "\n", since_spec->clock.ticks); } else { w_log(W_LOG_DBG, "running trigger rules!\n"); } // Triggers never need to sync explicitly; we are only dispatched // at settle points which are by definition sync'd to the present time cmd->query->sync_timeout = 0; if (!w_query_execute(cmd->query, root, &res, trigger_generator, cmd)) { w_log(W_LOG_ERR, "error running trigger query: %s", res.errmsg); w_query_result_free(&res); return; } w_log(W_LOG_DBG, "trigger generated %" PRIu32 " results\n", res.num_results); // create a new spec that will be used the next time cmd->query->since_spec = w_clockspec_new_clock(res.root_number, res.ticks); if (res.num_results) { spawn_command(root, cmd, &res, since_spec); } if (since_spec) { w_clockspec_free(since_spec); since_spec = NULL; } w_query_result_free(&res); }
static json_t *build_subscription_results( struct watchman_client_subscription *sub, w_root_t *root) { w_query_res res; json_t *response; json_t *file_list; char clockbuf[128]; struct w_clockspec *since_spec = sub->query->since_spec; if (since_spec && since_spec->tag == w_cs_clock) { w_log(W_LOG_DBG, "running subscription rules! since %" PRIu32 "\n", since_spec->clock.ticks); } else { w_log(W_LOG_DBG, "running subscription rules!\n"); } // Subscriptions never need to sync explicitly; we are only dispatched // at settle points which are by definition sync'd to the present time sub->query->sync_timeout = 0; if (!w_query_execute(sub->query, root, &res, subscription_generator, sub)) { w_log(W_LOG_ERR, "error running subscription query: %s", res.errmsg); w_query_result_free(&res); return NULL; } w_log(W_LOG_DBG, "subscription generated %" PRIu32 " results\n", res.num_results); if (res.num_results == 0) { w_query_result_free(&res); return NULL; } file_list = w_query_results_to_json(&sub->field_list, res.num_results, res.results); w_query_result_free(&res); response = make_response(); // It is way too much of a hassle to try to recreate the clock value if it's // not a relative clock spec, and it's only going to happen on the first run // anyway, so just skip doing that entirely. if (since_spec && since_spec->tag == w_cs_clock && clock_id_string(since_spec->clock.root_number, since_spec->clock.ticks, clockbuf, sizeof(clockbuf))) { set_prop(response, "since", json_string_nocheck(clockbuf)); } if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) { set_prop(response, "clock", json_string_nocheck(clockbuf)); } // create a new spec that will be used the next time if (since_spec) { w_clockspec_free(since_spec); since_spec = NULL; } sub->query->since_spec = w_clockspec_new_clock(res.root_number, res.ticks); set_prop(response, "is_fresh_instance", json_boolean(res.is_fresh_instance)); set_prop(response, "files", file_list); set_prop(response, "root", json_string(root->root_path->buf)); set_prop(response, "subscription", json_string(sub->name->buf)); return response; }
w_query_expr *w_expr_since_parser(w_query *query, json_t *term) { json_t *jval; struct w_clockspec *spec; struct since_term *sterm; int selected_field = SINCE_OCLOCK; const char *fieldname = "oclock"; if (!json_is_array(term)) { query->errmsg = strdup("\"since\" term must be an array"); return NULL; } if (json_array_size(term) < 2 || json_array_size(term) > 3) { query->errmsg = strdup("\"since\" term has invalid number of parameters"); return NULL; } jval = json_array_get(term, 1); spec = w_clockspec_parse(jval); if (!spec) { query->errmsg = strdup("invalid clockspec for \"since\" term"); return NULL; } if (spec->tag == w_cs_named_cursor) { query->errmsg = strdup("named cursors are not allowed in \"since\" terms"); goto fail; } jval = json_array_get(term, 2); if (jval) { int i; bool valid = false; fieldname = json_string_value(jval); if (!fieldname) { query->errmsg = strdup("field name for \"since\" term must be a string"); goto fail; } for (i = 0; allowed_fields[i].label; i++) { if (!strcmp(allowed_fields[i].label, fieldname)) { selected_field = allowed_fields[i].value; valid = true; break; } } if (!valid) { ignore_result(asprintf(&query->errmsg, "invalid field name \"%s\" for \"since\" term", fieldname)); goto fail; } } switch (selected_field) { case SINCE_CTIME: case SINCE_MTIME: if (spec->tag != w_cs_timestamp) { ignore_result(asprintf(&query->errmsg, "field \"%s\" requires a timestamp value " "for comparison in \"since\" term", fieldname)); goto fail; } break; case SINCE_OCLOCK: case SINCE_CCLOCK: /* we'll work with clocks or timestamps */ break; } sterm = calloc(1, sizeof(*sterm)); if (!sterm) { query->errmsg = strdup("out of memory"); goto fail; } sterm->spec = spec; sterm->field = selected_field; return w_query_expr_new(eval_since, dispose_since, sterm); fail: w_clockspec_free(spec); return NULL; }
static void dispose_since(void *data) { struct since_term *term = data; w_clockspec_free(term->spec); free(data); }