Пример #1
0
static void test_project()
{
    char *names[] = {"b", "c"};

    Rel *prj = rel_project(load("project_1"), names, 2);
    if (!equal(prj, "project_1_res"))
        fail();

    prj = rel_project(load("project_2"), names, 2);
    if (!equal(prj, "project_2_res"))
        fail();
}
Пример #2
0
static void test_compound()
{
    char *rn_from[] = {"a2"};
    char *rn_to[] = {"a"};
    char *pn[] = {"a2", "c"};

    Rel *tmp = load("compound_1");

    int a;
    Type ta;
    head_attr(tmp->head, rn_to[0], &a, &ta);

    Expr *e[] = { expr_mul(expr_int(10), expr_attr(a, Int)) };
    tmp = rel_extend(tmp, rn_from, e, 1);
    tmp = rel_project(tmp, pn, 2);
    tmp = rel_rename(tmp, rn_from, rn_to, 1);
    tmp = rel_union(tmp, load("compound_1"));

    if (!equal(tmp, "compound_1_res"))
        fail();

    /* compound with a parameter */
    char *names[] = {"car", "date", "email", "name", "phone", "pos", "when"};
    Type types[] = {String, String, String, String, String, Int, Int};
    char union_2_l[1024];
    str_cpy(union_2_l,
            "car,date,email,name,phone,pos,when\n"
            "myCar,28-Jun-2010,myEmail,myName,myPhone,2,14");

    if (!equal(rel_union(pack(union_2_l, names, types, 7),
                         load("union_2_r")),
               "union_2_res"))
        fail();

    str_cpy(union_2_l,
            "car,date,email,name,phone,pos,when\n"
            "myCar,28-Jun-2010,myEmail,myName,myPhone,2,14");
    if (!equal(rel_union(load("union_2_r"),
                         pack(union_2_l, names, types, 7)),
               "union_2_res"))
        fail();
}
Пример #3
0
/* return val;
 */
static list * 
rel_psm_return( mvc *sql, sql_subtype *restype, list *restypelist, symbol *return_sym )
{
	exp_kind ek = {type_value, card_value, FALSE};
	sql_exp *res;
	sql_rel *rel = NULL;
	int is_last = 0;
	list *l = sa_list(sql->sa);

	if (restypelist)
		ek.card = card_relation;
	res = rel_value_exp2(sql, &rel, return_sym, sql_sel, ek, &is_last);
	if (!res)
		return NULL;
	if (ek.card != card_relation && (!res || 
           	(res = rel_check_type(sql, restype, res, type_equal)) == NULL))
		return NULL;
	else if (ek.card == card_relation && !rel)
		return NULL;
	
	if (rel && ek.card != card_relation)
		append(l, exp_rel(sql, rel));
	else if (rel && !is_ddl(rel->op)) {
		list *exps = sa_list(sql->sa);
		node *n, *m;
		int isproject = (rel->op == op_project);
		list *oexps = rel->exps;
		sql_rel *l = rel->l;

		if (is_topn(rel->op))
			oexps = l->exps;
		for (n = oexps->h, m = restypelist->h; n && m; n = n->next, m = m->next) {
			sql_exp *e = n->data;
			sql_arg *ce = m->data;
			char *cname = exp_name(e);
			char name[16];

			if (!cname)
				cname = sa_strdup(sql->sa, number2name(name, 16, ++sql->label));
			if (!isproject) 
				e = exp_column(sql->sa, exp_relname(e), cname, exp_subtype(e), exp_card(e), has_nil(e), is_intern(e));
			e = rel_check_type(sql, &ce->type, e, type_equal);
			if (!e)
				return NULL;
			append(exps, e);
		}
		if (isproject)
			rel -> exps = exps;
		else
			rel = rel_project(sql->sa, rel, exps);
		res = exp_rel(sql, rel);
	} else if (rel && restypelist){ /* handle return table-var */
		list *exps = sa_list(sql->sa);
		sql_table *t = rel_ddl_table_get(rel);
		node *n, *m;
		char *tname = t->base.name;

		if (cs_size(&t->columns) != list_length(restypelist))
			return sql_error(sql, 02, "RETURN: number of columns do not match");
		for (n = t->columns.set->h, m = restypelist->h; n && m; n = n->next, m = m->next) {
			sql_column *c = n->data;
			sql_arg *ce = m->data;
			sql_exp *e = exp_alias(sql->sa, tname, c->base.name, tname, c->base.name, &c->type, CARD_MULTI, c->null, 0);

			e = rel_check_type(sql, &ce->type, e, type_equal);
			if (!e)
				return NULL;
			append(exps, e);
		}
		rel = rel_project(sql->sa, rel, exps);
		res = exp_rel(sql, rel);
	}
	append(l, exp_return(sql->sa, res, stack_nr_of_declared_tables(sql)));
	return l;
}
Пример #4
0
static void processor(const char *tx_addr, int port)
{
    sys_init(1);
    sys_log('E', "started port=%d, tx=%s\n", port, tx_addr);

    /* connect to the control thread */
    char addr[MAX_ADDR];
    sys_address(addr, port);
    IO *io = sys_connect(addr, IO_CHUNK);

    tx_attach(tx_addr);

    /* get env code from the tx */
    char *code = tx_program();
    char *res = mem_alloc(MAX_BLOCK);

    while (!io->stop) {
        sys_iready(io, -1);

        int status = -1;
        long long sid = 0LL, time = sys_millis();

        Env *env = NULL;
        Arg *arg = NULL;
        Vars *v = vars_new(0), *r = NULL, *w = NULL;

        Http_Req *req = http_parse_req(io);
        if (io->stop)
            goto exit;

        if (req == NULL) {
            status = http_400(io);
            goto exit;
        }

        if (req->method == OPTIONS) {
            status = http_opts(io);
            goto exit;
        }

        env = env_new("net", code);

        if (str_idx(req->path, "/fn") == 0) {
            int idx = (req->path[3] == '/') ? 4 : 3;
            int i = 0, len = 1, cnt = 0;
            Func **fns = env_funcs(env, req->path + idx, &cnt);

            status = http_200(io);
            while (status == 200 && len) {
                len = pack_fn2csv(fns, cnt, res, MAX_BLOCK, &i);
                status = http_chunk(io, res, len);
            }

            mem_free(fns);
            goto exit;
        }

        /* compare the request with the function defintion */
        Func *fn = env_func(env, req->path + 1);
        if (fn == NULL) {
            Error *err = error_new("unknown function '%s'", req->path + 1);
            status = http_404(io, err->msg);
            mem_free(err);
            goto exit;
        }

        if (fn->rp.name != NULL && req->method != POST) {
            status = http_405(io, POST);
            goto exit;
        }

        if (fn->rp.name == NULL && req->method == POST) {
            status = http_405(io, GET);
            goto exit;
        }

        /* TODO: think what to do with duplicate parameter values */
        for (int i = 0; i < req->args->len; ++i) {
            char *name = req->args->names[i];
            if (array_freq(req->args->names, req->args->len, name) > 1) {
                Error *err = error_new("duplicate parameter '%s' "
                                       "(not supported)",
                                       name);
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }
        }

        if (fn->pp.len != req->args->len) {
            Error *err = error_new("expected %d primitive parameters, got %d",
                                   fn->pp.len, req->args->len);
            status = http_404(io, err->msg);
            mem_free(err);
            goto exit;
        }

        arg = mem_alloc(sizeof(Arg));
        for (int i = 0; i < fn->pp.len; ++i) {
            char *name = fn->pp.names[i];
            Type t = fn->pp.types[i];

            int idx = array_scan(req->args->names, req->args->len, name);
            if (idx < 0) {
                Error *err = error_new("unknown parameter '%s'", name);
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }

            char *val = req->args->vals[idx];
            int error = 0;
            if (t == Int) {
                arg->vals[i].v_int = str_int(val, &error);
            } else if (t == Real)
                arg->vals[i].v_real = str_real(val, &error);
            else if (t == Long)
                arg->vals[i].v_long = str_long(val, &error);
            else if (t == String) {
                error = str_len(val) > MAX_STRING;
                if (!error)
                    str_cpy(arg->vals[i].v_str, val);
            }

            if (error) {
                Error *err = error_new("value '%s' (parameter '%s') "
                                       "is not of type '%s'",
                                       val, name, type_to_str(t));
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }
        }

        if (fn->rp.name != NULL) {
            TBuf *body = NULL;
            if (req->len > 0) {
                Error *err = pack_csv2rel(req->body, fn->rp.head, &body);
                if (err != NULL) {
                    status = http_404(io, err->msg);
                    mem_free(err);
                    goto exit;
                }
            } else {
                body = tbuf_new();
            }

            vars_add(v, fn->rp.name, 0, body);

            /* project the parameter */
            Rel *param = rel_project(rel_load(fn->rp.head, fn->rp.name),
                                     fn->rp.head->names,
                                     fn->rp.head->len);

            rel_eval(param, v, arg);

            /* clean the previous version */
            tbuf_clean(body);
            tbuf_free(body);

            /* replace with the new body */
            int vpos = array_scan(v->names, v->len, fn->rp.name);
            v->vals[vpos] = param->body;

            param->body = NULL;
            rel_free(param);
        }

        /* start a transaction */
        r = vars_new(fn->r.len);
        w = vars_new(fn->w.len);
        for (int i = 0; i < fn->r.len; ++i)
            vars_add(r, fn->r.names[i], 0, NULL);
        for (int i = 0; i < fn->w.len; ++i)
            vars_add(w, fn->w.names[i], 0, NULL);

        sid = tx_enter(addr, r, w);

        /* prepare variables */
        for (int i = 0; i < r->len; ++i) {
            TBuf *body = vol_read(r->vols[i], r->names[i], r->vers[i]);
            vars_add(v, r->names[i], 0, body);
        }
        for (int i = 0; i < w->len; ++i) {
            int pos = array_scan(v->names, v->len, w->names[i]);
            if (pos < 0)
                vars_add(v, w->names[i], 0, NULL);
        }
        for (int i = 0; i < fn->t.len; ++i)
            vars_add(v, fn->t.names[i], 0, NULL);

        /* evaluate the function body */
        for (int i = 0; i < fn->slen; ++i)
            rel_eval(fn->stmts[i], v, arg);

        /* prepare the return value. note, the resulting relation
           is just a container for the body, so it is not freed */
        Rel *ret = NULL;
        if (fn->ret != NULL)
            ret = fn->stmts[fn->slen - 1];

        /* persist the global variables */
        for (int i = 0; i < w->len; ++i) {
            int idx = array_scan(v->names, v->len, w->names[i]);
            if (idx < 0) {
                status = http_500(io);
                goto exit;
            }

            vol_write(w->vols[i], v->vals[idx], w->names[i], w->vers[i]);
            tbuf_free(v->vals[idx]);
            v->vals[idx] = NULL;
        }

        /* confirm a success and send the result back */
        status = http_200(io);
        if (status != 200)
            goto exit;

        tx_commit(sid);

        /* N.B. there is no explicit revert as the transaction manager handles
           nested tx_enter and a connectivity failure as a rollback */

        int len = 1, i = 0;
        while (status == 200 && len) {
            len = pack_rel2csv(ret, res, MAX_BLOCK, i++);
            status = http_chunk(io, res, len);
        }
exit:
        if (status != -1)
            sys_log('E', "%016llX method %c, path %s, time %lldms - %3d\n",
                         sid,
                         (req == NULL) ? '?' : req->method,
                         (req == NULL) ? "malformed" : req->path,
                         sys_millis() - time,
                         status);


        if (r != NULL)
            vars_free(r);
        if (w != NULL)
            vars_free(w);
        if (arg != NULL)
            mem_free(arg);
        if (req != NULL)
            http_free_req(req);
        if (env != NULL)
            env_free(env);
        for (int i = 0; i < v->len; ++i)
            if (v->vals[i] != NULL) {
                tbuf_clean(v->vals[i]);
                tbuf_free(v->vals[i]);
            }
        vars_free(v);

        sys_term(io);
    }

    mem_free(code);
    mem_free(res);
    tx_detach();
    sys_close(io);
}