Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion docs/pkg-query.8
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ Parentheses can be used for grouping in the usual manner.
String values are either any text not containing whitespace (possibly
followed by but not including whitespace) or any text enclosed in single or
double quotes.
.Ss Variables
.Ss Normal Variables
.Bl -tag -width F1
.It Cm \&%n
Name of the package (type string)
Expand Down Expand Up @@ -336,6 +336,35 @@ See
.Cm %?
above for what information is used.
.El
.Ss Multiline variables
When used in an evaluation, these variables refer to the items in the list.
The condition evaluates to true if
.Em any
element in the list matches.
Negative operators are an exception: they check for the
.Em absence
of any matching element.
.Pp
For example,
.Qq \&%dn != 'curl'
means
.Qq the package does not depend on curl .
.Bl -tag -width F1
.It Cm \&%d Ns Op nov
Dependency of the package (type string)
.It Cm \&%r Ns Op nov
Reverse dependency of the package (type string)
.It Cm \&%C
Category of the package (type string)
.It Cm \&%L
License of the package (type string)
.It Cm \&%B
Required shared library of the package (type string)
.It Cm \&%b
Provided shared library of the package (type string)
.It Cm \&%A Ns Op tv
Annotation of the package (type string)
.El
.Ss Operators
.Bl -tag -width F1
.It Va var Cm ~ Ar glob
Expand Down Expand Up @@ -420,6 +449,9 @@ List automatic packages:
.Pp
List unmaintained packages:
.Dl $ pkg query -e '%m = ports@FreeBSD.org' %o
.Pp
List packages that depend on python312:
.Dl $ pkg query -e '%dn = python312' %o
.Sh SEE ALSO
.Xr pkg_create 3 ,
.Xr pkg_printf 3 ,
Expand Down
104 changes: 97 additions & 7 deletions src/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
unsigned int bracket_level = 0;
const char *sqlop;
bool collate_nocase = false;
bool multiline_subquery = false;
const char *pending_subquery = NULL;

fprintf(sqlcond->fp, " WHERE ");
while (str[0] != '\0') {
Expand Down Expand Up @@ -640,6 +642,57 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
}
state = OPERATOR_INT;
break;
case 'd':
str++;
if (str[0] == 'n')
pending_subquery = "SELECT * FROM deps AS d WHERE d.package_id=p.id AND name";
else if (str[0] == 'o')
pending_subquery = "SELECT * FROM deps AS d WHERE d.package_id=p.id AND origin";
else if (str[0] == 'v')
pending_subquery = "SELECT * FROM deps AS d WHERE d.package_id=p.id AND version";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'r':
str++;
if (str[0] == 'n')
pending_subquery = "SELECT * FROM packages AS p2 JOIN deps AS d ON d.package_id=p2.id WHERE d.name=p.name AND p2.name";
else if (str[0] == 'o')
pending_subquery = "SELECT * FROM packages AS p2 JOIN deps AS d ON d.package_id=p2.id WHERE d.name=p.name AND p2.origin";
else if (str[0] == 'v')
pending_subquery = "SELECT * FROM packages AS p2 JOIN deps AS d ON d.package_id=p2.id WHERE d.name=p.name AND p2.version";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'C':
pending_subquery = "SELECT * FROM categories AS c JOIN pkg_categories AS pc ON c.id=pc.category_id WHERE pc.package_id=p.id AND c.name";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'L':
pending_subquery = "SELECT * FROM licenses AS l JOIN pkg_licenses AS pl ON l.id=pl.license_id WHERE pl.package_id=p.id AND l.name";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'B':
pending_subquery = "SELECT * FROM shlibs AS s JOIN pkg_shlibs_required AS ps ON s.id=ps.shlib_id WHERE ps.package_id=p.id AND s.name";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'b':
pending_subquery = "SELECT * FROM shlibs AS s JOIN pkg_shlibs_provided AS ps ON s.id=ps.shlib_id WHERE ps.package_id=p.id AND s.name";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
case 'A':
str++;
if (str[0] == 't')
pending_subquery = "SELECT * FROM annotation AS a JOIN pkg_annotation AS pa ON a.annotation_id=pa.tag_id WHERE pa.package_id=p.id AND a.annotation";
else if (str[0] == 'v')
pending_subquery = "SELECT * FROM annotation AS a JOIN pkg_annotation AS pa ON a.annotation_id=pa.value_id WHERE pa.package_id=p.id AND a.annotation";
multiline_subquery = true;
state = OPERATOR_STRING;
break;
default:
bad_option:
fprintf(stderr, "malformed evaluation string\n");
Expand Down Expand Up @@ -705,13 +758,21 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
fprintf(stderr, "~ expected only for string testing\n");
return (EPKG_FATAL);
}
if (pending_subquery) {
fprintf(sqlcond->fp, "EXISTS (%s", pending_subquery);
pending_subquery = NULL;
}
state = NEXT_IS_STRING;
fprintf(sqlcond->fp, " GLOB ");
} else if (str[0] == '>' || str[0] == '<') {
if (state != OPERATOR_INT) {
fprintf(stderr, "> expected only for integers\n");
return (EPKG_FATAL);
}
if (pending_subquery) {
fprintf(sqlcond->fp, "EXISTS (%s", pending_subquery);
pending_subquery = NULL;
}
state = NEXT_IS_INT;
fprintf(sqlcond->fp, "%c", str[0]);
if (str[1] == '=') {
Expand All @@ -724,6 +785,10 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
} else {
state = NEXT_IS_INT;
}
if (pending_subquery) {
fprintf(sqlcond->fp, "EXISTS (%s", pending_subquery);
pending_subquery = NULL;
}
fprintf(sqlcond->fp, "%c", str[0]);
if (str[1] == '=') {
str++;
Expand All @@ -732,14 +797,27 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
collate_nocase = true;
}
} else if (str[0] == '!') {
if (str[1] == '=') {
fprintf(sqlcond->fp, "%c", str[0]);
fprintf(sqlcond->fp, "%c", str[1]);
} else if (str[1] == '~') {
fprintf(sqlcond->fp, " NOT GLOB ");
if (pending_subquery) {
fprintf(sqlcond->fp, "NOT EXISTS (%s", pending_subquery);
pending_subquery = NULL;
if (str[1] == '=') {
fprintf(sqlcond->fp, "%c", str[1]);
} else if (str[1] == '~') {
fprintf(sqlcond->fp, " GLOB ");
} else {
fprintf(stderr, "expecting = or ~ after !\n");
return (EPKG_FATAL);
}
} else {
fprintf(stderr, "expecting = or ~ after !\n");
return (EPKG_FATAL);
if (str[1] == '=') {
fprintf(sqlcond->fp, "%c", str[0]);
fprintf(sqlcond->fp, "%c", str[1]);
} else if (str[1] == '~') {
fprintf(sqlcond->fp, " NOT GLOB ");
} else {
fprintf(stderr, "expecting = or ~ after !\n");
return (EPKG_FATAL);
}
}
str++;
if (state == OPERATOR_STRING) {
Expand Down Expand Up @@ -781,6 +859,10 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
} else if (state == INT) {
if (!isdigit(str[0])) {
state = POST_EXPR;
if (multiline_subquery) {
fprintf(sqlcond->fp, ")");
multiline_subquery = false;
}
str--;
} else {
fprintf(sqlcond->fp, "%c", str[0]);
Expand All @@ -795,6 +877,10 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
fprintf(sqlcond->fp, " COLLATE NOCASE ");
collate_nocase = false;
}
if (multiline_subquery) {
fprintf(sqlcond->fp, ")");
multiline_subquery = false;
}
} else {
fprintf(sqlcond->fp, "%c", str[0]);
if (str[0] == '\'')
Expand All @@ -812,6 +898,10 @@ format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
fprintf(sqlcond->fp, " COLLATE NOCASE ");
collate_nocase = false;
}
if (multiline_subquery) {
fprintf(sqlcond->fp, ")");
multiline_subquery = false;
}
}

if (state != POST_EXPR && state != INT) {
Expand Down
17 changes: 17 additions & 0 deletions tests/frontend/query.sh
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ EOF
-s exit:0 \
pkg query -e "%#O > 0 && %#D > 0" "%n"

atf_check \
-o inline:"plop\n" \
-e empty \
-s exit:0 \
pkg query -e '%dn = "test"' "%n"

atf_check \
-o inline:"test\n" \
-e empty \
-s exit:0 \
pkg query -e '%dn != "test"' "%n"

atf_check \
-o inline:"plop\ntest\n" \
-e empty \
-s exit:0 \
pkg query -e '%dn != "plop"' "%n"

atf_check \
-o inline:"${TMPDIR}/test-dir\n" \
Expand Down
Loading