Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge pull request #208 from bdrewery/pkg-rquery
Baptiste Daroussin committed 14 years ago
commit 08a371cff44633aea058dd14ffd922887f25ceaa
parent 9fbec3f
35 files changed +690 -286
modified libpkg/pkg.h
@@ -102,7 +102,11 @@ typedef enum {
	/**
	 * The argument is an extended regular expression.
	 */
-
	MATCH_EREGEX
+
	MATCH_EREGEX,
+
	/**
+
	 * The argument is a WHERE clause to use as condition
+
	 */
+
	MATCH_CONDITION
} match_t;

/**
@@ -640,8 +644,8 @@ int pkgdb_unregister_pkg(struct pkgdb *pkg, const char *origin);
 */
struct pkgdb_it * pkgdb_query(struct pkgdb *db, const char *pattern,
							  match_t type);
-
struct pkgdb_it * pkgdb_query_condition(struct pkgdb *db, const char *condition);
-
struct pkgdb_it * pkgdb_rquery(struct pkgdb *db, const char *pattern,
+
struct pkgdb_it * pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t type, const char *reponame);
+
struct pkgdb_it * pkgdb_search(struct pkgdb *db, const char *pattern,
		match_t type, unsigned int field, const char *reponame);

/**
modified libpkg/pkgdb.c
@@ -135,6 +135,41 @@ load_val(sqlite3 *db, struct pkg *pkg, const char *sql, int flags, int (*pkg_add
	return (EPKG_OK);
}

+
static const char *
+
pkgdb_get_reponame(struct pkgdb *db, const char *repo)
+
{
+
	const char *reponame = NULL;
+
	bool multirepos_enabled = false;
+

+
	assert(db->type == PKGDB_REMOTE);
+

+
	/* Working on multiple repositories */
+
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
+

+
	if (multirepos_enabled) {
+
		if (repo != NULL) {
+
			if (!is_attached(db->sqlite, repo)) {
+
				pkg_emit_error("repository '%s' does not exist.", repo);
+
				return (NULL);
+
			}
+

+
			reponame = repo;
+
		} else {
+
			/* default repository in multi-repos is 'default' */
+
			reponame = "default";
+
		}
+
	} else {
+
		if (repo != NULL) {
+
			pkg_emit_error("PKG_MULTIREPOS is not enabled. -r flag not supported.", repo);
+
			return (NULL);
+
		}
+
		/* default repository in single-repo is 'remote' */
+
		reponame = "remote";
+
	}
+

+
	return (reponame);
+
}
+

static void
populate_pkg(sqlite3_stmt *stmt, struct pkg *pkg) {
	int i, icol = 0;
@@ -775,19 +810,15 @@ pkgdb_it_free(struct pkgdb_it *it)
	free(it);
}

-
struct pkgdb_it *
-
pkgdb_query(struct pkgdb *db, const char *pattern, match_t match)
+
static const char *
+
pkgdb_get_pattern_query(const char *pattern, match_t match)
{
-
	char sql[BUFSIZ];
-
	sqlite3_stmt *stmt;
-
	const char *comp = NULL;
	char *checkorigin = NULL;
+
	const char *comp = NULL;

-
	assert(db != NULL);
-
	assert(match == MATCH_ALL || pattern != NULL);
-

-
	if (pattern != NULL)
+
	if (pattern != NULL) {
		checkorigin = strchr(pattern, '/');
+
	}

	switch (match) {
	case MATCH_ALL:
@@ -821,49 +852,72 @@ pkgdb_query(struct pkgdb *db, const char *pattern, match_t match)
		else
			comp = " WHERE EREGEXP(?1, origin)";
		break;
+
	case MATCH_CONDITION:
+
		comp = pattern;
+
		break;
	}

-
	snprintf(sql, sizeof(sql),
-
			"SELECT id, origin, name, version, comment, desc, "
-
				"message, arch, maintainer, www, "
-
				"prefix, flatsize, licenselogic, automatic, "
-
				"time, infos "
-
			"FROM packages AS p%s "
-
			"ORDER BY p.name;", comp);
+
	return (comp);
+
}

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite);
-
		return (NULL);
-
	}
+
static const char *
+
pkgdb_get_match_how(match_t match)
+
{
+
	const char *how = NULL;

-
	if (match != MATCH_ALL)
-
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
+
	switch (match) {
+
		case MATCH_ALL:
+
			how = NULL;
+
			break;
+
		case MATCH_EXACT:
+
			how = "%s = ?1";
+
			break;
+
		case MATCH_GLOB:
+
			how = "%s GLOB ?1";
+
			break;
+
		case MATCH_REGEX:
+
			how = "%s REGEXP ?1";
+
			break;
+
		case MATCH_EREGEX:
+
			how = "EREGEXP(?1, %s)";
+
			break;
+
		case MATCH_CONDITION:
+
			/* This case should not be called by pkgdb_get_match_how() */
+
			assert(0);
+
			break;
+
	}

-
	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
+
	return (how);
}

struct pkgdb_it *
-
pkgdb_query_condition(struct pkgdb *db, const char *condition)
+
pkgdb_query(struct pkgdb *db, const char *pattern, match_t match)
{
	char sql[BUFSIZ];
	sqlite3_stmt *stmt;
+
	const char *comp = NULL;

	assert(db != NULL);
-
	assert (condition != NULL);
+
	assert(match == MATCH_ALL || (pattern != NULL && pattern[0] != '\0'));
+

+
	comp = pkgdb_get_pattern_query(pattern, match);

	snprintf(sql, sizeof(sql),
-
	    "SELECT id, origin, name, version, comment, desc, "
-
	        "message, arch, maintainer, www, "
-
	        "prefix, flatsize, licenselogic, automatic "
-
		"time, infos "
-
	    "FROM packages AS p WHERE %s "
-
	    "ORDER BY p.name;", condition);
+
			"SELECT id, origin, name, version, comment, desc, "
+
				"message, arch, maintainer, www, "
+
				"prefix, flatsize, licenselogic, automatic, "
+
				"time, infos "
+
			"FROM packages AS p%s "
+
			"ORDER BY p.name;", comp);

	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (NULL);
	}

+
	if (match != MATCH_ALL && match != MATCH_CONDITION)
+
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
+

	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
}

@@ -966,16 +1020,16 @@ pkgdb_load_deps(struct pkgdb *db, struct pkg *pkg)

	assert(db != NULL && pkg != NULL);

-
	pkg_get(pkg, PKG_REPONAME, &reponame);
+
	if (pkg->flags & PKG_LOAD_DEPS)
+
		return (EPKG_OK);

-
	if (pkg->type == PKG_REMOTE)
+
	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
+
		pkg_get(pkg, PKG_REPONAME, &reponame);
		snprintf(sql, sizeof(sql), basesql, reponame);
-
	else
+
	} else
		snprintf(sql, sizeof(sql), basesql, "main");

-
	if (pkg->flags & PKG_LOAD_DEPS)
-
		return (EPKG_OK);
-

	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (EPKG_FATAL);
@@ -1005,18 +1059,26 @@ pkgdb_load_rdeps(struct pkgdb *db, struct pkg *pkg)
	sqlite3_stmt *stmt = NULL;
	int ret;
	const char *origin;
-
	const char sql[] = ""
+
	const char *reponame = NULL;
+
	char sql[BUFSIZ];
+
	const char *basesql = ""
		"SELECT p.name, p.origin, p.version "
-
		"FROM packages AS p, deps AS d "
+
		"FROM '%s'.packages AS p, '%s'.deps AS d "
		"WHERE p.id = d.package_id "
			"AND d.origin = ?1;";

	assert(db != NULL && pkg != NULL);
-
	assert(pkg->type == PKG_INSTALLED);

	if (pkg->flags & PKG_LOAD_RDEPS)
		return (EPKG_OK);

+
	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
+
		pkg_get(pkg, PKG_REPONAME, &reponame);
+
		snprintf(sql, sizeof(sql), basesql, reponame, reponame);
+
	} else
+
		snprintf(sql, sizeof(sql), basesql, "main", "main");
+

	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (EPKG_FATAL);
@@ -1093,6 +1155,7 @@ pkgdb_load_dirs(struct pkgdb *db, struct pkg *pkg)
	int ret;

	assert(db != NULL && pkg != NULL);
+
	assert(pkg->type == PKG_INSTALLED);

	if (pkg->flags & PKG_LOAD_DIRS)
		return (EPKG_OK);
@@ -1135,6 +1198,7 @@ pkgdb_load_license(struct pkgdb *db, struct pkg *pkg)
	assert(db != NULL && pkg != NULL);

	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
		snprintf(sql, sizeof(sql), basesql, reponame, reponame);
	} else
@@ -1158,6 +1222,7 @@ pkgdb_load_category(struct pkgdb *db, struct pkg *pkg)
	assert(db != NULL && pkg != NULL);

	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
		snprintf(sql, sizeof(sql), basesql, reponame, reponame);
	} else
@@ -1181,6 +1246,7 @@ pkgdb_load_user(struct pkgdb *db, struct pkg *pkg)
		"ORDER by name DESC";

	assert(db != NULL && pkg != NULL);
+
	assert(pkg->type == PKG_INSTALLED);

	ret = load_val(db->sqlite, pkg, sql, PKG_LOAD_USERS, pkg_adduser, PKG_USERS);

@@ -1210,6 +1276,7 @@ pkgdb_load_group(struct pkgdb *db, struct pkg *pkg)
		"ORDER by name DESC";

	assert(db != NULL && pkg != NULL);
+
	assert(pkg->type == PKG_INSTALLED);

	ret = load_val(db->sqlite, pkg, sql, PKG_LOAD_GROUPS, pkg_addgroup, PKG_GROUPS);

@@ -1238,6 +1305,7 @@ pkgdb_load_shlib(struct pkgdb *db, struct pkg *pkg)
	assert(db != NULL && pkg != NULL);

	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
		snprintf(sql, sizeof(sql), basesql, reponame, reponame);
	} else
@@ -1302,6 +1370,7 @@ pkgdb_load_options(struct pkgdb *db, struct pkg *pkg)
		return (EPKG_OK);

	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
		snprintf(sql, sizeof(sql), basesql, reponame);
	} else {
@@ -2161,7 +2230,6 @@ pkgdb_query_newpkgversion(struct pkgdb *db, const char *repo)
{
	struct sbuf *sql = sbuf_new_auto();
	const char *reponame = NULL;
-
	bool multirepos_enabled;
	sqlite3_stmt *stmt = NULL;

	const char finalsql[] = "SELECT pkgid AS id, origin, name, version, "
@@ -2178,32 +2246,12 @@ pkgdb_query_newpkgversion(struct pkgdb *db, const char *repo)
			"cksum, path, 0 FROM '%s'.packages WHERE origin='ports-mgmt/pkg';";

	assert(db != NULL);
+
	assert(db->type == PKGDB_REMOTE);

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
		return (NULL);
	}

-
	/* Working on multiple repositories */
-
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
-

-
	if (multirepos_enabled) {
-
		if (repo != NULL) {
-
			if (!is_attached(db->sqlite, repo)) {
-
				pkg_emit_error("repository '%s' does not exists", repo);
-
				return (NULL);
-
			}
-

-
			reponame = repo;
-
		} else {
-
			/* default repository in multi-repos is 'default' */
-
			reponame = "default";
-
		}
-
	} else {
-
		/* default repository in single-repo is 'remote' */
-
		reponame = "remote";
-
	}
-

	sbuf_printf(sql, main_sql, reponame);

	create_temporary_pkgjobs(db->sqlite);
@@ -2242,7 +2290,6 @@ pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, c
	struct sbuf *sql = sbuf_new_auto();
	const char *how = NULL;
	const char *reponame = NULL;
-
	bool multirepos_enabled = false;

	if ((it = pkgdb_query_newpkgversion(db, repo)) != NULL) {
		pkg_emit_newpkgversion();
@@ -2275,51 +2322,15 @@ pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, c
	const char weight_sql[] = "UPDATE pkgjobs set weight=(select count(*) from '%s'.deps as d where d.origin=pkgjobs.origin)";

	assert(db != NULL);
+
	assert(db->type == PKGDB_REMOTE);

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
		return (NULL);
	}

-
	/* Working on multiple repositories */
-
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
-

-
	if (multirepos_enabled) {
-
		if (repo != NULL) {
-
			if (!is_attached(db->sqlite, repo)) {
-
				pkg_emit_error("repository '%s' does not exists", repo);
-
				return (NULL);
-
			}
-

-
			reponame = repo;
-
		} else {
-
			/* default repository in multi-repos is 'default' */
-
			reponame = "default";
-
		}
-
	} else {
-
		/* default repository in single-repo is 'remote' */
-
		reponame = "remote";
-
	}
-

	sbuf_printf(sql, main_sql, reponame);

-
	switch (match) {
-
		case MATCH_ALL:
-
			how = NULL;
-
			break;
-
		case MATCH_EXACT:
-
			how = "%s = ?1";
-
			break;
-
		case MATCH_GLOB:
-
			how = "%s GLOB ?1";
-
			break;
-
		case MATCH_REGEX:
-
			how = "%s REGEXP ?1";
-
			break;
-
		case MATCH_EREGEX:
-
			how = "EREGEXP(?1, %s)";
-
			break;
-
	}
+
	how = pkgdb_get_match_how(match);

	create_temporary_pkgjobs(db->sqlite);

@@ -2399,7 +2410,6 @@ pkgdb_query_upgrades(struct pkgdb *db, const char *repo, bool all)
	sqlite3_stmt *stmt = NULL;
	struct sbuf *sql = sbuf_new_auto();
	const char *reponame = NULL;
-
	bool multirepos_enabled = false;
	struct pkgdb_it *it;

	if ((it = pkgdb_query_newpkgversion(db, repo)) != NULL) {
@@ -2408,11 +2418,7 @@ pkgdb_query_upgrades(struct pkgdb *db, const char *repo, bool all)
	}

	assert(db != NULL);
-

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
-
		return (NULL);
-
	}
+
	assert(db->type == PKGDB_REMOTE);

	const char finalsql[] = "select pkgid as id, origin, name, version, "
		"comment, desc, message, arch, maintainer, "
@@ -2459,24 +2465,8 @@ pkgdb_query_upgrades(struct pkgdb *db, const char *repo, bool all)

	const char weight_sql[] = "UPDATE pkgjobs set weight=(select count(*) from '%s'.deps as d where d.origin=pkgjobs.origin)";

-
	/* Working on multiple repositories */
-
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
-

-
	if (multirepos_enabled) {
-
		if (repo != NULL) {
-
			if (!is_attached(db->sqlite, repo)) {
-
				pkg_emit_error("repository '%s' does not exists", repo);
-
				return (NULL);
-
			}
-

-
			reponame = repo;
-
		} else {
-
			/* default repository in multi-repos is 'default' */
-
			reponame = "default";
-
		}
-
	} else {
-
		/* default repository in single-repo is 'remote' */
-
		reponame = "remote";
+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
+
		return (NULL);
	}

	create_temporary_pkgjobs(db->sqlite);
@@ -2528,14 +2518,9 @@ pkgdb_query_downgrades(struct pkgdb *db, const char *repo)
	struct sbuf *sql = sbuf_new_auto();
	const char *reponame = NULL;
	sqlite3_stmt *stmt = NULL;
-
	bool multirepos_enabled = false;

	assert(db != NULL);
-

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
-
		return (NULL);
-
	}
+
	assert(db->type == PKGDB_REMOTE);

	const char finalsql[] = ""
		"SELECT l.id, l.origin AS origin, l.name AS name, l.version AS version, l.comment AS comment, l.desc AS desc, "
@@ -2547,24 +2532,8 @@ pkgdb_query_downgrades(struct pkgdb *db, const char *repo)
		"WHERE l.origin = r.origin "
		"AND PKGGT(l.version, r.version)";

-
	/* Working on multiple repositories */
-
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
-

-
	if (multirepos_enabled) {
-
		if (repo != NULL) {
-
			if (!is_attached(db->sqlite, repo)) {
-
				pkg_emit_error("repository '%s' does not exists", repo);
-
				return (NULL);
-
			}
-

-
			reponame = repo;
-
		} else {
-
			/* default repository in multi-repos is 'default' */
-
			reponame = "default";
-
		}
-
	} else {
-
		/* default repository in single-repo is 'remote' */
-
		reponame = "remote";
+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
+
		return (NULL);
	}

	sbuf_printf(sql, finalsql, reponame, reponame);
@@ -2632,23 +2601,7 @@ pkgdb_query_delete(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, int
	sbuf_cat(sql, "INSERT OR IGNORE INTO delete_job (origin, pkgid) "
			"SELECT p.origin, p.id FROM packages as p ");

-
	switch (match) {
-
		case MATCH_ALL:
-
			how = NULL;
-
			break;
-
		case MATCH_EXACT:
-
			how = "%s = ?1";
-
			break;
-
		case MATCH_GLOB:
-
			how = "%s GLOB ?1";
-
			break;
-
		case MATCH_REGEX:
-
			how = "%s REGEXP ?1";
-
			break;
-
		case MATCH_EREGEX:
-
			how = "EREGEXP(?1, %s)";
-
			break;
-
	}
+
	how = pkgdb_get_match_how(match);

	sql_exec(db->sqlite, "DROP TABLE IF EXISTS delete_job; "
			"CREATE TEMPORARY TABLE IF NOT EXISTS delete_job ("
@@ -2703,29 +2656,71 @@ pkgdb_query_delete(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, int
	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
}

+
struct pkgdb_it *
+
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, const char *repo)
+
{
+
	sqlite3_stmt *stmt = NULL;
+
	struct sbuf *sql = NULL;
+
	bool multirepos_enabled = false;
+
	const char *reponame = NULL;
+
	const char *comp = NULL;
+
	char basesql[BUFSIZ] = ""
+
				"SELECT id, origin, name, version, comment, "
+
				"prefix, desc, arch, maintainer, www, "
+
				"licenselogic, flatsize AS newflatsize, pkgsize, "
+
				"cksum, path AS repopath, '%s' AS dbname "
+
				"FROM '%s'.packages p";
+

+
	assert(db != NULL);
+
	assert(match == MATCH_ALL || (pattern != NULL && pattern[0] != '\0'));
+

+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
+
		return (NULL);
+
	}
+

+
	sql = sbuf_new_auto();
+
	comp = pkgdb_get_pattern_query(pattern, match);
+
	if (comp && comp[0]) {
+
		strlcat(basesql, comp, sizeof(basesql));
+
	}
+

+
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
+

+
	/*
+
	 * Working on multiple remote repositories
+
	 */
+
	if (multirepos_enabled && !strcmp(reponame, "default")) {
+
		/* duplicate the query via UNION for all the attached databases */
+
		if (sql_on_all_attached_db(db->sqlite, sql, basesql) != EPKG_OK)
+
			return (NULL);
+
	} else {
+
		sbuf_printf(sql, basesql, reponame, reponame);
+
	}
+

+
	sbuf_cat(sql, " ORDER BY name;");
+
	sbuf_finish(sql);
+

+
	if (sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (NULL);
+
	}
+

+
	sbuf_delete(sql);
+

+
	if (match != MATCH_ALL && match != MATCH_CONDITION)
+
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_REMOTE));
+
}
+

+

static int
-
pkgdb_rquery_build_search_query(struct sbuf *sql, match_t match, unsigned int field)
+
pkgdb_search_build_search_query(struct sbuf *sql, match_t match, unsigned int field)
{
	const char *how = NULL;
	const char *what = NULL;

-
	switch (match) {
-
		case MATCH_ALL:
-
			how = NULL;
-
			break;
-
		case MATCH_EXACT:
-
			how = "%s = ?1";
-
			break;
-
		case MATCH_GLOB:
-
			how = "%s GLOB ?1";
-
			break;
-
		case MATCH_REGEX:
-
			how = "%s REGEXP ?1";
-
			break;
-
		case MATCH_EREGEX:
-
			how = "EREGEXP(?1, %s)";
-
			break;
-
	}
+
	how = pkgdb_get_match_how(match);

	switch(field) {
		case FIELD_NONE:
@@ -2755,7 +2750,7 @@ pkgdb_rquery_build_search_query(struct sbuf *sql, match_t match, unsigned int fi
}

struct pkgdb_it *
-
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, unsigned int field, const char *reponame)
+
pkgdb_search(struct pkgdb *db, const char *pattern, match_t match, unsigned int field, const char *reponame)
{
	sqlite3_stmt *stmt = NULL;
	struct sbuf *sql = NULL;
@@ -2772,12 +2767,10 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, unsigned int
					"cksum, path, '%s' AS dbname "
					"FROM '%s'.packages ";

+
	assert(db != NULL);
	assert(pattern != NULL && pattern[0] != '\0');
+
	assert(db->type == PKGDB_REMOTE);

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("%s", "remote database not attached (misuse)");
-
		return (NULL);
-
	}

	sql = sbuf_new_auto();
	sbuf_cat(sql, basesql);
@@ -2807,19 +2800,18 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, unsigned int

		/* close the UNIONs and build the search query */
		sbuf_cat(sql, ") WHERE ");
-
		pkgdb_rquery_build_search_query(sql, match, field);
-
		sbuf_finish(sql);
	} else {
		/* 
		 * Working on a single remote repository
		 */

		sbuf_cat(sql, ", 'remote' AS dbname FROM remote.packages WHERE ");
-
		pkgdb_rquery_build_search_query(sql, match, field);
-
		sbuf_cat(sql, ";");
-
		sbuf_finish(sql);
	}

+
	pkgdb_search_build_search_query(sql, match, field);
+
	sbuf_cat(sql, ";");
+
	sbuf_finish(sql);
+

	if (sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (NULL);
@@ -3060,7 +3052,6 @@ pkgdb_query_fetch(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, cons
	struct sbuf *sql = sbuf_new_auto();
	const char *how = NULL;
	const char *reponame = NULL;
-
	bool multirepos_enabled = false;

	const char finalsql[] = "SELECT pkgid AS id, origin, name, version, "
		"flatsize, newversion, newflatsize, pkgsize, cksum, repopath, "
@@ -3082,51 +3073,15 @@ pkgdb_query_fetch(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, cons
	const char weight_sql[] = "UPDATE pkgjobs SET weight=(SELECT count(*) FROM '%s'.deps AS d WHERE d.origin=pkgjobs.origin)";

	assert(db != NULL);
+
	assert(db->type == PKGDB_REMOTE);

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
+
	if ((reponame = pkgdb_get_reponame(db, repo)) == NULL) {
		return (NULL);
	}

-
	/* Working on multiple repositories */
-
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);
-

-
	if (multirepos_enabled) {
-
		if (repo != NULL) {
-
			if (!is_attached(db->sqlite, repo)) {
-
				pkg_emit_error("repository '%s' does not exists", repo);
-
				return (NULL);
-
			}
-

-
			reponame = repo;
-
		} else {
-
			/* default repository in multi-repos is 'default' */
-
			reponame = "default";
-
		}
-
	} else {
-
		/* default repository in single-repo is 'remote' */
-
		reponame = "remote";
-
	}
-

	sbuf_printf(sql, main_sql, reponame);

-
	switch (match) {
-
		case MATCH_ALL:
-
			how = NULL;
-
			break;
-
		case MATCH_EXACT:
-
			how = "%s = ?1";
-
			break;
-
		case MATCH_GLOB:
-
			how = "%s GLOB ?1";
-
			break;
-
		case MATCH_REGEX:
-
			how = "%s REGEXP ?1";
-
			break;
-
		case MATCH_EREGEX:
-
			how = "EREGEXP(?1, %s)";
-
			break;
-
	}
+
	how = pkgdb_get_match_how(match);

	create_temporary_pkgjobs(db->sqlite);

modified pkg/Makefile
@@ -15,6 +15,7 @@ SRCS= add.c \
		query.c \
		register.c \
		repo.c \
+
		rquery.c \
		update.c \
		upgrade.c \
		search.c \
@@ -64,6 +65,7 @@ MAN= pkg.8 \
	pkg-query.8 \
	pkg-register.8 \
	pkg-repo.8 \
+
	pkg-rquery.8 \
	pkg-search.8 \
	pkg-set.8 \
	pkg-shell.8 \
modified pkg/clean.c
@@ -93,7 +93,7 @@ exec_clean(int argc, char **argv)
		}

		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		it = pkgdb_rquery(db, origin, MATCH_EXACT, FIELD_ORIGIN, NULL);
+
		it = pkgdb_search(db, origin, MATCH_EXACT, FIELD_ORIGIN, NULL);

		if (it == NULL) {
			warnx("skipping %s", ent->fts_path);
modified pkg/main.c
@@ -77,6 +77,7 @@ static struct commands {
	{ "set", "Modify local database informations", exec_set, usage_set},
	{ "register", "Registers a package into the local package database", exec_register, usage_register},
	{ "repo", "Creates a package database repository", exec_repo, usage_repo},
+
	{ "rquery", "Query information from the remote repository", exec_rquery, usage_rquery},
	{ "shell", "Open a debug shell", exec_shell, usage_shell},
	{ "shlib", "Displays which package links against a specific shared library", exec_shlib, usage_shlib},
	{ "update", "Updates remote package repository databases", exec_update, usage_update},
modified pkg/pkg-add.8
@@ -69,6 +69,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-audit.8
@@ -79,6 +79,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-autoremove.8
@@ -66,6 +66,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-backup.8
@@ -76,6 +76,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-check.8
@@ -95,6 +95,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-clean.8
@@ -55,6 +55,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-create.8
@@ -207,6 +207,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-delete.8
@@ -79,6 +79,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-fetch.8
@@ -90,6 +90,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-info.8
@@ -131,6 +131,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-install.8
@@ -106,6 +106,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8,
.Xr pkg-shell 8 ,
modified pkg/pkg-query.8
@@ -220,6 +220,7 @@ See
.Xr pkg-install 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-register.8
@@ -84,6 +84,7 @@ See
.Xr pkg-install 8 ,
.Xr pkg-query 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-repo.8
@@ -74,6 +74,7 @@ See
.Xr pkg-install 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
added pkg/pkg-rquery.8
@@ -0,0 +1,213 @@
+
.\"
+
.\" FreeBSD pkg - a next generation package for the installation and maintenance
+
.\" of non-core utilities.
+
.\"
+
.\" Redistribution and use in source and binary forms, with or without
+
.\" modification, are permitted provided that the following conditions
+
.\" are met:
+
.\" 1. Redistributions of source code must retain the above copyright
+
.\"    notice, this list of conditions and the following disclaimer.
+
.\" 2. Redistributions in binary form must reproduce the above copyright
+
.\"    notice, this list of conditions and the following disclaimer in the
+
.\"    documentation and/or other materials provided with the distribution.
+
.\"
+
.\"
+
.\"     @(#)pkg.8
+
.\" $FreeBSD$
+
.\"
+
.Dd March 2, 2012
+
.Dt PKG-RQUERY 8
+
.Os
+
.Sh NAME
+
.Nm "pkg rquery"
+
.Nd query information from remote repositories
+
.Sh SYNOPSIS
+
.Nm
+
.Ao query-format Ac Ao pkg-name Ac
+
.Nm
+
.Fl a
+
.Op Fl r Ar reponame
+
.Ao query-format Ac
+
.Nm
+
.Fl e Ao evaluation-condition Ac
+
.Op Fl r Ar reponame
+
.Ao query-format Ac
+
.Nm
+
.Op Fl gxX
+
.Op Fl r Ar reponame
+
.Ao query-format Ac Ao pattern Ac Ao ... Ac
+
.Sh DESCRIPTION
+
.Nm
+
is used for displaying information about remote packages.
+
.Sh OPTIONS
+
The following options are supported by
+
.Nm :
+
.Bl -tag -width F1
+
.It Fl a
+
Match all packages from the database
+
.It Fl e
+
Match packages using the given
+
.Ar evaluation-condition.
+
See EVALUATION FORMAT for details.
+
.It Fl r Ar reponame
+
Fetches packages from the given
+
.Ar reponame
+
if multiple repo support is enabled.
+
See
+
.Xr pkg.conf 5 .
+
.It Fl g
+
Treat
+
.Ao pattern Ac
+
as a glob pattern.
+
.It Fl x
+
Treat
+
.Ao pattern Ac
+
as a regular expression.
+
.It Fl X
+
Treat
+
.Ao pattern Ac
+
as an extended regular expression.
+
.El
+
.Sh QUERY FORMAT
+
There are two type of keyword for the query format: the multiline and the normal
+
one. Only one type of multiline pattern is accepted for a given query.
+
.Ss Normal patterns:
+
.Bl -tag -width F1
+
.It Cm \&%R
+
Name of the repository package is in
+
.It Cm \&%n
+
Name of the matched package
+
.It Cm \&%v
+
Version of the matched package
+
.It Cm \&%o
+
Origin of the matched package
+
.It Cm \&%p
+
Prefix of the matched package
+
.It Cm \&%m
+
Maintainer of the matched package
+
.It Cm \&%c
+
Comment of the matched package
+
.It Cm \&%i
+
Additionnal information about the package
+
.It Cm \&%w
+
Home page of the matched package
+
.It Cm \&%l
+
license logic of the matched package - nothing for single, & for AND, and | for OR
+
.It Cm \&%sh
+
The flat size in human readable format of the matched package
+
.It Cm \&%sb
+
The flat size in bytes of the matched package
+
.It Cm \&%a
+
Returns 1 if the matched package is an orphan package and can be pkg-autoremove(1)'d, 0 otherwise
+
.It Cm \&%M
+
message contain in the matched package
+
.It Cm \&%? Ns Op dCOLB
+
Returns 0 if the list is empty and 1 if the list has information to display
+
.Bl -tag -width indent
+
.It Cm d
+
for dependencies
+
.It Cm r
+
for reverse dependencies
+
.It Cm C
+
for categories
+
.It Cm O
+
for options
+
.It Cm L
+
for licenses
+
.It Cm B
+
for shared libraries
+
.El
+
.El
+
.Ss Multiline patterns:
+
.Bl -tag -width F1
+
.It Cm \&%d Ns Op nov
+
Expands to the list of dependencies for the matched package, where
+
.Cm n
+
stands for the package name,
+
.Cm o
+
for the package origin, and
+
.Cm v
+
for the package version.
+
.It Cm \&%C
+
Expands to the list of categories the matched package belongs to.
+
.It Cm \&%O Ns Op kv
+
Expands to the list of options of the matched package, where
+
.Cm k
+
stands for option key, and
+
.Cm v
+
for option value.
+
.It Cm \&%L
+
Expands to the list of license(s) for the matched package.
+
.It Cm \&%B
+
Expands to the list of shared libraries used by programs from the matched package.
+
.El
+
.Sh EVALUATION FORMAT
+
.Ss Variables
+
.Bl -tag -width F1
+
.It Cm \&%n
+
Name of the package (type string)
+
.It Cm \&%o
+
Origin of the package (type string)
+
.It Cm \&%p
+
Prefix of the package (type string)
+
.It Cm \&%m
+
Maintainer of the package (type string)
+
.It Cm \&%c
+
Comment of the package (type string)
+
.It Cm \&%w
+
WWW address of the package (type string)
+
.It Cm \&%s
+
Flatsize of the package (type integer)
+
.It Cm \&%a
+
Automatic status of the package (type integer)
+
.It Cm \&%M
+
Message of the package (type string)
+
.El
+
.Ss Operators
+
.Bl -tag -width F1
+
.It Cm ~
+
String glob pattern matching
+
.It Cm > Ns Op =
+
Integer comparison
+
.It Cm > Ns Op =
+
Integer comparison
+
.It Cm = Ns Op =
+
Integer or string comparison
+
.Sh ENVIRONMENT
+
The following environment variables affect the execution of
+
.Nm .
+
See
+
.Xr pkg.conf 5
+
for further description.
+
.Bl -tag -width ".Ev NO_DESCRIPTIONS"
+
.It Ev PKG_DBDIR
+
.El
+
.Sh FILES
+
See
+
.Xr pkg.conf 5 .
+
.Sh SEE ALSO
+
.Xr pkg 8 ,
+
.Xr pkg-add 8 ,
+
.Xr pkg-audit 8 ,
+
.Xr pkg-autoremove 8 ,
+
.Xr pkg-backup 8 ,
+
.Xr pkg-check 8 ,
+
.Xr pkg-clean 8 ,
+
.Xr pkg-create 8 ,
+
.Xr pkg-delete 8 ,
+
.Xr pkg-fetch 8 ,
+
.Xr pkg-info 8 ,
+
.Xr pkg-install 8 ,
+
.Xr pkg-query 8 ,
+
.Xr pkg-register 8 ,
+
.Xr pkg-repo 8 ,
+
.Xr pkg-search 8 ,
+
.Xr pkg-set 8 ,
+
.Xr pkg-shell 8 ,
+
.Xr pkg-shlib 8 ,
+
.Xr pkg-update 8 ,
+
.Xr pkg-updating 8 ,
+
.Xr pkg-upgrade 8 ,
+
.Xr pkg-version 8 ,
+
.Xr pkg-which 8 ,
+
.Xr pkg.conf 5
modified pkg/pkg-search.8
@@ -117,6 +117,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
.Xr pkg-shlib 8 ,
modified pkg/pkg-set.8
@@ -68,6 +68,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-shell 8 ,
.Xr pkg-shlib 8 ,
modified pkg/pkg-shell.8
@@ -54,6 +54,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shlib 8 ,
modified pkg/pkg-shlib.8
@@ -66,6 +66,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-update.8
@@ -74,6 +74,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-updating.8
@@ -91,6 +91,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-upgrade.8
@@ -79,6 +79,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg-version.8
@@ -116,6 +116,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8,
.Xr pkg-shell 8 ,
modified pkg/pkg-which.8
@@ -66,6 +66,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg.8
@@ -103,6 +103,8 @@ Create a local package repository for remote usage.
.It Ic shell
Fires up a sqlite shell to the local or remote database.
Extreme care should be taken when using this command.
+
.It Ic rquery
+
Query information for remote repositories.
.It Ic search
Search for the given pattern in the remote package
repositories.
@@ -149,6 +151,7 @@ See
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkg.conf.5
@@ -123,6 +123,7 @@ file always overrides the value of an option set in the file.
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
.Xr pkg-search 8 ,
.Xr pkg-set 8 ,
.Xr pkg-shell 8 ,
modified pkg/pkgcli.h
@@ -82,6 +82,10 @@ int exec_register(int argc, char **argv);
int exec_repo(int, char **);
void usage_repo(void);

+
/* pkg rquery */
+
int exec_rquery(int, char **);
+
void usage_rquery(void);
+

/* pkg set */
int exec_set(int, char **);
void usage_set(void);
@@ -155,4 +159,30 @@ void print_jobs_summary(struct pkg_jobs *j, pkg_jobs_t type, const char *msg, ..
int event_callback(void *data, struct pkg_event *ev);

extern struct sbuf *messages;
+

+

+
/* pkg-query / pkg-rquery */
+
struct query_flags {
+
	const char flag;
+
	const char *options;
+
	const unsigned multiline;
+
	const int dbflags;
+
};
+

+
typedef enum {
+
	NONE,
+
	NEXT_IS_INT,
+
	OPERATOR_INT,
+
	INT,
+
	NEXT_IS_STRING,
+
	OPERATOR_STRING,
+
	STRING,
+
	QUOTEDSTRING,
+
	SQUOTEDSTRING
+
} type_t;
+

+
void print_query(struct pkg *pkg, char *qstr, char multiline);
+
int format_sql_condition(const char *str, struct sbuf *sqlcond);
+
int analyse_query_string(char *qstr, struct query_flags *q_flags, const unsigned int q_flags_len, int *flags, char *multiline);
+

#endif
modified pkg/query.c
@@ -40,12 +40,7 @@

#include "pkgcli.h"

-
static struct query_flags {
-
	const char flag;
-
	const char *options;
-
	const unsigned multiline;
-
	const int dbflags;
-
} q_flags[] = {
+
static struct query_flags accepted_query_flags[] = {
	{ 'd', "nov",		1, PKG_LOAD_DEPS },
	{ 'r', "nov",		1, PKG_LOAD_RDEPS },
	{ 'C', "",		1, PKG_LOAD_CATEGORIES },
@@ -73,20 +68,6 @@ static struct query_flags {
	{ 't', "",		0, PKG_LOAD_BASIC },
};

-
typedef enum {
-
	NONE,
-
	NEXT_IS_INT,
-
	OPERATOR_INT,
-
	INT,
-
	NEXT_IS_STRING,
-
	OPERATOR_STRING,
-
	STRING,
-
	QUOTEDSTRING,
-
	SQUOTEDSTRING
-
} type_t;
-

-
const unsigned int flags_len = (sizeof(q_flags)/sizeof(q_flags[0]));
-

static void
format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
{
@@ -118,6 +99,11 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
					if (tmp != NULL)
						sbuf_cat(dest, tmp);
					break;
+
				case 'R':
+
					pkg_get(pkg, PKG_REPONAME, &tmp);
+
					if (tmp != NULL)
+
						sbuf_cat(dest, tmp);
+
					break;
				case 'p':
					pkg_get(pkg, PKG_PREFIX, &tmp);
					if (tmp != NULL)
@@ -305,7 +291,7 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
	sbuf_finish(dest);
}

-
static void
+
void
print_query(struct pkg *pkg, char *qstr, char multiline)
{
	struct sbuf *output = sbuf_new_auto();
@@ -395,10 +381,11 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
	sbuf_delete(output);
}

-
static int
+
int
format_sql_condition(const char *str, struct sbuf *sqlcond)
{
	type_t state = NONE;
+
	sbuf_cat(sqlcond, " WHERE ");
	while (str[0] != '\0') {
		if (str[0] == ';') {
			fprintf(stderr, "';' is forbidden in evaluation format");
@@ -582,8 +569,8 @@ format_sql_condition(const char *str, struct sbuf *sqlcond)
	return (EPKG_OK);
}

-
static int
-
analyse_query_string(char *qstr, int *flags, char *multiline)
+
int
+
analyse_query_string(char *qstr, struct query_flags *q_flags, const unsigned int q_flags_len, int *flags, char *multiline)
{
	unsigned int i, j, k;
	unsigned int valid_flag = 0;
@@ -596,7 +583,7 @@ analyse_query_string(char *qstr, int *flags, char *multiline)
			qstr++;
			valid_flag = 0;
	
-
			for (i = 0; i < flags_len; i++) {
+
			for (i = 0; i < q_flags_len; i++) {
				/* found the flag */
				if (qstr[0] == q_flags[i].flag) {
					valid_flag = 1;
@@ -638,7 +625,7 @@ analyse_query_string(char *qstr, int *flags, char *multiline)

					/* handle the '?' flag cases */
					if (q_flags[i].flag == '?') {
-
						for (k = 0; k < flags_len; k++) 
+
						for (k = 0; k < q_flags_len; k++)
							if (q_flags[k].flag == q_flags[i].options[j]) {
								*flags |= q_flags[k].dbflags;
								break;
@@ -690,6 +677,7 @@ exec_query(int argc, char **argv)
	char multiline = 0;
	char *condition = NULL;
	struct sbuf *sqlcond = NULL;
+
	const unsigned int q_flags_len = (sizeof(accepted_query_flags)/sizeof(accepted_query_flags[0]));

	while ((ch = getopt(argc, argv, "agxXF:e:")) != -1) {
		switch (ch) {
@@ -709,6 +697,7 @@ exec_query(int argc, char **argv)
				pkgname = optarg;
				break;
			case 'e':
+
				match = MATCH_CONDITION;
				condition = optarg;
				break;
			default:
@@ -730,7 +719,7 @@ exec_query(int argc, char **argv)
		return (EX_USAGE);
	}

-
	if (analyse_query_string(argv[0], &query_flags, &multiline) != EPKG_OK)
+
	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK)
		return (EX_USAGE);

	if (pkgname != NULL) {
@@ -747,6 +736,7 @@ exec_query(int argc, char **argv)
		sqlcond = sbuf_new_auto();
		if (format_sql_condition(condition, sqlcond) != EPKG_OK)
			return (EX_USAGE);
+
		sbuf_finish(sqlcond);
	}

	ret = pkgdb_open(&db, PKGDB_DEFAULT);
@@ -761,24 +751,11 @@ exec_query(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

-
	if (condition != NULL) {
-
		sbuf_finish(sqlcond);
-
		if ((it = pkgdb_query_condition(db, sbuf_data(sqlcond))) == NULL)
-
			return (EX_IOERR);
-

-
		while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK)
-
			print_query(pkg, argv[0], multiline);
-

-
		pkgdb_it_free(it);
-

-
		if (ret != EPKG_END)
-
			return (EX_SOFTWARE);
-

-
		return (EXIT_SUCCESS);
-
	}
-

-
	if (match == MATCH_ALL) {
-
		if ((it = pkgdb_query(db, NULL, match)) == NULL)
+
	if (match == MATCH_ALL || match == MATCH_CONDITION) {
+
		const char *condition_sql = NULL;
+
		if (match == MATCH_CONDITION && sqlcond)
+
			condition_sql = sbuf_data(sqlcond);
+
		if ((it = pkgdb_query(db, condition_sql, match)) == NULL)
			return (EX_IOERR);

		while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK)
added pkg/rquery.c
@@ -0,0 +1,195 @@
+
/*
+
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
+
 * All rights reserved.
+
 * 
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 * 
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/types.h>
+
#include <sys/sbuf.h>
+

+
#include <ctype.h>
+
#include <inttypes.h>
+
#include <libutil.h>
+
#include <pkg.h>
+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <sysexits.h>
+
#include <unistd.h>
+

+
#include "pkgcli.h"
+

+
static struct query_flags accepted_rquery_flags[] = {
+
	{ 'd', "nov",		1, PKG_LOAD_DEPS },
+
	{ 'r', "nov",		1, PKG_LOAD_RDEPS },
+
	{ 'C', "",		1, PKG_LOAD_CATEGORIES },
+
	{ 'O', "kv",		1, PKG_LOAD_OPTIONS },
+
	{ 'L', "",		1, PKG_LOAD_LICENSES },
+
	{ 'B', "",		1, PKG_LOAD_SHLIBS },
+
	{ '?', "drCOLB",	1, PKG_LOAD_BASIC },	/* dbflags handled in analyse_query_string() */
+
	{ 's', "hb",		0, PKG_LOAD_BASIC },
+
	{ 'n', "",		0, PKG_LOAD_BASIC },
+
	{ 'v', "",		0, PKG_LOAD_BASIC },
+
	{ 'o', "",		0, PKG_LOAD_BASIC },
+
	{ 'R', "",		0, PKG_LOAD_BASIC },
+
	{ 'p', "",		0, PKG_LOAD_BASIC },
+
	{ 'm', "",		0, PKG_LOAD_BASIC },
+
	{ 'c', "",		0, PKG_LOAD_BASIC },
+
	{ 'w', "",		0, PKG_LOAD_BASIC },
+
	{ 'l', "",		0, PKG_LOAD_BASIC },
+
	{ 'a', "",		0, PKG_LOAD_BASIC },
+
	{ 'M', "",		0, PKG_LOAD_BASIC },
+
	{ 'i', "",		0, PKG_LOAD_BASIC },
+
	{ 't', "",		0, PKG_LOAD_BASIC },
+
};
+

+
void
+
usage_rquery(void)
+
{
+
	fprintf(stderr, "usage: pkg rquery [-r reponame] <query-format> <pkg-name>\n");
+
	fprintf(stderr, "       pkg rquery -a [-r reponame] <query-format>\n");
+
	fprintf(stderr, "       pkg rquery -e <evaluation> [-r reponame] <query-format>\n");
+
	fprintf(stderr, "       pkg rquery [-gxX] [-r reponame] <query-format> <pattern> <...>\n\n");
+
	fprintf(stderr, "For more information see 'pkg help rquery.'\n");
+
}
+

+
int
+
exec_rquery(int argc, char **argv)
+
{
+
	struct pkgdb *db = NULL;
+
	struct pkgdb_it *it = NULL;
+
	struct pkg *pkg = NULL;
+
	char *pkgname = NULL;
+
	int query_flags = PKG_LOAD_BASIC;
+
	match_t match = MATCH_EXACT;
+
	int ch;
+
	int ret = EPKG_OK;
+
	int retcode = EXIT_SUCCESS;
+
	int i;
+
	char multiline = 0;
+
	char *condition = NULL;
+
	struct sbuf *sqlcond = NULL;
+
	const unsigned int q_flags_len = (sizeof(accepted_rquery_flags)/sizeof(accepted_rquery_flags[0]));
+
	const char *reponame = NULL;
+

+
	while ((ch = getopt(argc, argv, "agxXe:r:")) != -1) {
+
		switch (ch) {
+
			case 'a':
+
				match = MATCH_ALL;
+
				break;
+
			case 'g':
+
				match = MATCH_GLOB;
+
				break;
+
			case 'x':
+
				match = MATCH_REGEX;
+
				break;
+
			case 'X':
+
				match = MATCH_EREGEX;
+
				break;
+
			case 'e':
+
				match = MATCH_CONDITION;
+
				condition = optarg;
+
				break;
+
			case 'r':
+
				reponame = optarg;
+
				break;
+
			default:
+
				usage_query();
+
				return (EX_USAGE);
+
		}
+
	}
+

+
	argc -= optind;
+
	argv += optind;
+

+
	if (argc == 0) {
+
		usage_query();
+
		return (EX_USAGE);
+
	}
+

+
	if ((argc == 1) ^ (match == MATCH_ALL) && condition == NULL) {
+
		usage_query();
+
		return (EX_USAGE);
+
	}
+

+
	if (analyse_query_string(argv[0], accepted_rquery_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK)
+
		return (EX_USAGE);
+

+
	if (condition != NULL) {
+
		sqlcond = sbuf_new_auto();
+
		if (format_sql_condition(condition, sqlcond) != EPKG_OK)
+
			return (EX_USAGE);
+
		sbuf_finish(sqlcond);
+
	}
+

+
	ret = pkgdb_open(&db, PKGDB_REMOTE);
+
	if (ret == EPKG_ENODB) {
+
		if (geteuid() == 0)
+
			return (EX_IOERR);
+

+
		/* do not fail if run as a user */
+
		return (EXIT_SUCCESS);
+
	}
+

+
	if (ret != EPKG_OK)
+
		return (EX_IOERR);
+

+
	if (match == MATCH_ALL || match == MATCH_CONDITION) {
+
		const char *condition_sql = NULL;
+
		if (match == MATCH_CONDITION && sqlcond)
+
			condition_sql = sbuf_data(sqlcond);
+
		if ((it = pkgdb_rquery(db, condition_sql, match, reponame)) == NULL)
+
			return (EX_IOERR);
+

+
		while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK)
+
			print_query(pkg, argv[0],  multiline);
+

+
		if (ret != EPKG_END)
+
			retcode = EX_SOFTWARE;
+

+
		pkgdb_it_free(it);
+
	} else {
+
		for (i = 1; i < argc; i++) {
+
			pkgname = argv[i];
+

+
			if ((it = pkgdb_rquery(db, pkgname, match, reponame)) == NULL)
+
				return (EX_IOERR);
+

+
			while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK)
+
				print_query(pkg, argv[0], multiline);
+

+
			if (ret != EPKG_END) {
+
				retcode = EX_SOFTWARE;
+
				break;
+
			}
+

+
			pkgdb_it_free(it);
+
		}
+
	}
+

+
	pkg_free(pkg);
+
	pkgdb_close(db);
+

+
	return (retcode);
+
}
modified pkg/search.c
@@ -122,7 +122,7 @@ exec_search(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

-
	if ((it = pkgdb_rquery(db, pattern, match, field, reponame)) == NULL) {
+
	if ((it = pkgdb_search(db, pattern, match, field, reponame)) == NULL) {
		pkgdb_close(db);
		return (1);
	}