const failable get()

in components/sqldb/pgsql.hpp [409:533]


const failable<value> get(const value& key, const PGSql& pgsql) {
    debug(key, "pgsql::get::key");
    debug(pgsql.conninfo, "pgsql::get::conninfo");
    debug(pgsql.table, "pgsql::get::table");
    setup(pgsql);

    // Get item and id and get parameters from the key
    const bool lk = isList(key);
    const list<value> kparams = lk? keyparams(key) : nilListValue;
    const list<value> regex = assoc<value>("regex", kparams);
    const list<value> like = assoc<value>("like", kparams);
    const list<value> textsearch = assoc<value>("textsearch", kparams);
    const list<value> limit = assoc<value>("limit", kparams);
    const list<value> offset = assoc<value>("offset", kparams);
    const list<value> rank = assoc<value>("rank", kparams);
    const list<value> id = lk? keyid(key) : nilListValue;
    const list<value> atable = assoc<value>("table", kparams);
    const string table = isNull(atable)? pgsql.table : (string)cadr(atable);
    const list<value> akname = assoc<value>("kcolumn", kparams);
    const string kname = isNull(akname)? pgsql.kname : (string)cadr(akname);
    const list<value> avname = assoc<value>("vcolumn", kparams);
    const string vname = isNull(avname)? pgsql.vname : (string)cadr(avname);

    // Build the SQL query
    const char* sqlparams[6];
    int p = 0;
    int w = 0;
    int rk = 0;
    ostringstream sqlos;
    sqlos << "select data." << kname << ", data." << vname;
    if (!isNull(textsearch)) {
        // Text search, setup text result ranking
        sqlos << ", ts_rank_cd(to_tsvector(data." << vname << "), tsquery, 32) as tsrank";
        rk++;
    }
    if (!isNull(rank)) {
        // Ranking, setup rank expression
        const string rs = (string)cadr(rank);
        sqlos << ", " << rs << " as rank";
        rk++;
    }
    sqlos << " from " << table << " data";
    if (!isNull(textsearch)) {
        // Text search, define the query
        const string ts = tstranslate((string)cadr(textsearch));
        debug(ts, "pgsql::get::sqlparam");
        sqlparams[p++] = c_str(ts);
        sqlos << ", plainto_tsquery($" << p << ") tsquery";
    }
    if (!lk || !isNull(id)) {
        // Query of the form key = id
        const string ks = write(content(scheme::writeValue(lk? (value)id : key)));
        debug(ks, "pgsql::get::sqlparam");
        sqlparams[p++] = c_str(ks);
        sqlos << (w == 0? " where" : " and");
        sqlos << " data." << kname << " = $" << p;
        w++;
    }
    if (!isNull(regex)) {
        // Query of the form key ~ param
        const string rs = cadr(regex);
        debug(rs, "pgsql::get::sqlparam");
        sqlparams[p++] = c_str(rs);
        sqlos << (w == 0? " where" : " and");
        sqlos << " data." << kname << " ~ $" << p;
        w++;
    }
    if (!isNull(like)) {
        // Query of the form key like param
        const string ls = cadr(like);
        debug(ls, "pgsql::get::sqlparam");
        sqlparams[p++] = c_str(ls);
        sqlos << (w == 0? " where" : " and");
        sqlos << " data." << kname << " like $" << p;
        w++;
    }
    if (!isNull(textsearch)) {
        // Text search, apply the query
        sqlos << (w == 0? " where" : " and");
        sqlos << " tsquery @@ to_tsvector(data." << vname << ")";
        w++;
    }
    if (!isNull(rank) || !isNull(textsearch)) {
        // Result ordering
        sqlos << " order by" << (isNull(rank)? "" : " rank desc") << ((isNull(rank) || isNull(textsearch))? "" : ",") << (isNull(textsearch)? "" : " tsrank desc");
    }
    if (!isNull(offset)) {
        // Result pagination offset
        sqlos << " offset " << atoi(c_str((string)cadr(offset)));
    }
    // Result limit count
    const int l = isNull(limit)? 1 : atoi(c_str((string)cadr(limit)));
    sqlos << " limit " << l << ";";
    
    // Execute the query
    const string sqls = str(sqlos);
    debug(sqls, "pgsql::get::sqls");
    PGresult* r = PQexecParams(pgsql.conn, c_str(sqls), p, NULL, sqlparams, NULL, NULL, 0);
    if (PQresultStatus(r) != PGRES_TUPLES_OK) {
        const string rs = string("Couldn't execute select postgresql SQL statement: ") + pgfailure(r, pgsql.conn);
        PQclear(r);
        return mkfailure<value>(rs);
    }
    const int n = PQntuples(r);
    if (n < 1) {
        PQclear(r);
        ostringstream os;
        os << "Couldn't get postgresql entry: " << key;
        return mkfailure<value>(str(os), 404, false);
    }

    // Return a collection of items
    if (l != 1) {
        const list<value> lval = getitems(r, 0, n, rk);
        PQclear(r);
        debug(lval, "pgsql::get::result");
        return (value)lval;
    }

    // Return a single item
    const value val = getitem(r, 0, rk);
    PQclear(r);
    debug(val, "pgsql::get::result");
    return val;
}