Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
feat: query DB packages with filters (#40)
Browse files Browse the repository at this point in the history
* more query builder

* query builder test case

* fix verbosity in 'tests/read.cc'

* validate params

* carry error code in PkgQueryArgException

* omit packages which do not record a license when filtering by licenses

* test licenses, allowBroken, and allowUnfree

* fmt

* Update src/pkgdb/query-builder.cc

Co-authored-by: Matthew Kenigsberg <[email protected]>

* Update src/pkgdb/query-builder.cc

Co-authored-by: Matthew Kenigsberg <[email protected]>

* Update query-builder.cc

* Update query-builder.cc

* review changes

* use EXPECT_EQ in buildPkgQuery tests

* fmt

* fmt

* don't use '.value()' on 'std::optional's

* Update src/pkgdb/query-builder.cc

Co-authored-by: Matthew Kenigsberg <[email protected]>

* fix lints

* fix lints

---------

Co-authored-by: Matthew Kenigsberg <[email protected]>
  • Loading branch information
aakropotkin and mkenigs authored Aug 23, 2023
1 parent 27aea0f commit 5487967
Show file tree
Hide file tree
Showing 24 changed files with 784 additions and 186 deletions.
57 changes: 57 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
Checks: >
clang-diagnostic-*
,clang-analyzer-*
,bugprone-*
,cert-*
,readability-*
,performance-*
,portability-*
,hicpp-*
,cppcoreguidelines-*
,modernize-*
,-modernize-use-trailing-return-type
,-cppcoreguidelines-pro-type-union-access
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: none
CheckOptions:
- key: llvm-else-after-return.WarnOnConditionVariables
value: 'false'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons
value: 'false'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: cert-err33-c.CheckedFunctions
value: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: 'false'
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: 'true'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: llvm-qualified-auto.AddConstToQualified
value: 'false'
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: llvm-else-after-return.WarnOnUnfixable
value: 'false'
- key: google-readability-function-size.StatementThreshold
value: '800'
...
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
*.db
result
result-*
compile_commands.json
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ TR ?= tr
SED ?= sed
TEST ?= test
DOXYGEN ?= doxygen
BEAR ?= bear


# ---------------------------------------------------------------------------- #
Expand Down Expand Up @@ -279,8 +280,9 @@ most: bin lib ignores

# ---------------------------------------------------------------------------- #

.PHONY: ccls
.PHONY: ccls cdb
ccls: .ccls
cdb: compile_commands.json

.ccls: FORCE
echo 'clang' > "$@";
Expand All @@ -295,6 +297,11 @@ ccls: .ccls
}|$(TR) ' ' '\n'|$(SED) 's/-std=/%cpp -std=/' >> "$@";


compile_commands.json: FORCE
-$(MAKE) -C $(MAKEFILE_DIR) clean;
$(BEAR) --output "$@" -- $(MAKE) -C $(MAKEFILE_DIR) -j all;


# ---------------------------------------------------------------------------- #

.PHONY: docs
Expand Down
5 changes: 5 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
( if pkgsFor.stdenv.cc.isGNU then pkgsFor.gdb else pkgsFor.lldb )
# For doc
pkgsFor.doxygen
# For IDEs
pkgsFor.ccls
pkgsFor.bear
# For lints/fmt
pkgsFor.clang-tools
] ++ nixpkgs.lib.optionals pkgsFor.stdenv.isLinux [
# For debugging
pkgsFor.valgrind
Expand Down
4 changes: 3 additions & 1 deletion include/flox/core/command.hh
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ namespace flox {
* } Verbosity;
*/
struct VerboseParser : public argparse::ArgumentParser {
explicit VerboseParser( std::string name, std::string version = "0.1.0" );
explicit VerboseParser( const std::string & name
, const std::string & version = "0.1.0"
);
}; /* End struct `VerboseParser' */


Expand Down
2 changes: 1 addition & 1 deletion include/flox/flox-flake.hh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class FloxFlake : public std::enable_shared_from_this<FloxFlake> {
nix::ref<nix::EvalState> state;
const nix::flake::LockedFlake lockedFlake;

FloxFlake( nix::ref<nix::EvalState> state
FloxFlake( const nix::ref<nix::EvalState> & state
, const nix::FlakeRef & ref
);

Expand Down
14 changes: 7 additions & 7 deletions include/flox/package.hh
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class Package {
{
std::optional<std::string> version = this->getVersion();
if ( ! version.has_value() ) { return std::nullopt; }
return versions::coerceSemver( version.value() );
return versions::coerceSemver( * version );
}

/**
Expand Down Expand Up @@ -194,34 +194,34 @@ class Package {
} } };
std::optional<std::string> os = this->getVersion();

if ( os.has_value() ) { j[system].emplace( "version", os.value() ); }
if ( os.has_value() ) { j[system].emplace( "version", * os ); }
else { j[system].emplace( "version", nlohmann::json() ); }

os = this->getSemver();
if ( os.has_value() ) { j[system].emplace( "semver", os.value() ); }
if ( os.has_value() ) { j[system].emplace( "semver", * os ); }
else { j[system].emplace( "semver", nlohmann::json() ); }

j[system].emplace( "outputs", this->getOutputs() );
j[system].emplace( "outputsToInstall", this->getOutputsToInstall() );

os = this->getLicense();
if ( os.has_value() ) { j[system].emplace( "license", os.value() ); }
if ( os.has_value() ) { j[system].emplace( "license", * os ); }
else { j[system].emplace( "license", nlohmann::json() ); }

std::optional<bool> ob = this->isBroken();
if ( ob.has_value() ) { j[system].emplace( "broken", ob.value() ); }
if ( ob.has_value() ) { j[system].emplace( "broken", * ob ); }
else { j[system].emplace( "broken", nlohmann::json() ); }

ob = this->isUnfree();
if ( ob.has_value() ) { j[system].emplace( "unfree", ob.value() ); }
if ( ob.has_value() ) { j[system].emplace( "unfree", * ob ); }
else { j[system].emplace( "unfree", nlohmann::json() ); }

if ( withDescription )
{
std::optional<std::string> od = this->getDescription();
if ( od.has_value() )
{
j[system].emplace( "description", od.value() );
j[system].emplace( "description", * od );
}
else
{
Expand Down
104 changes: 83 additions & 21 deletions include/flox/pkgdb/query-builder.hh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
#include <string>
#include <optional>
#include <vector>
#include <unordered_map>
#include <functional>

#include <nix/shared.hh>

#include "flox/core/exceptions.hh"
#include "flox/core/types.hh"
#include "flox/core/util.hh"

Expand All @@ -27,44 +30,103 @@ namespace flox {

/* -------------------------------------------------------------------------- */

/**
* Collection of query parameters used to lookup packages in a database.
* These use a combination of SQL statements and post processing with
* `node-semver` to produce a list of satisfactory packages.
*/
struct PkgQueryArgs {
/** Match partial name/description */
std::optional<std::string> match;
std::optional<flox::AttrPath> pathPrefix;
std::optional<subtree_type> subtree;
std::optional<std::string> stability;
std::optional<std::string> system;

std::optional<std::string> name;
std::optional<std::string> pname;
std::optional<std::string> version;
std::optional<std::string> semver;

/** Filter results by partial name/description match. */
std::optional<std::string> match;
std::optional<std::string> name; /**< Filter results by exact `name`. */
std::optional<std::string> pname; /**< Filter results by exact `pname`. */
std::optional<std::string> version; /**< Filter results by exact version. */
std::optional<std::string> semver; /**< Filter results by version range. */

/** Filter results to those explicitly marked with the given licenses. */
std::optional<std::vector<std::string>> licenses;

bool allowBroken = false;
bool allowUnfree = true;
/** Whether to include packages which are explicitly marked `broken`. */
bool allowBroken = false;
/** Whether to include packages which are explicitly marked `unfree`. */
bool allowUnfree = true;
/** Whether pre-release versions should be ordered before releases. */
bool preferPreReleases = false;

/**
* Subtrees to search.
* TODO: Default to first of `catalog`, `packages`, or `legacyPackages`.
* Requires `db` to be read.
*/
std::optional<std::vector<subtree_type>> subtrees;

std::vector<std::string> systems = { nix::settings.thisSystem.get() };
std::vector<std::string> stabilities = flox::defaultCatalogStabilities;
/** Systems to search */
std::vector<std::string> systems = { nix::settings.thisSystem.get() };

/** Stabilities to search ( if any ) */
std::optional<std::vector<std::string>> stabilities;


/** Errors concerning validity of package query parameters. */
struct PkgQueryInvalidArgException : public flox::FloxException {
public:
enum error_code {
PDQEC_ERROR = 1 /**< Generic Exception */
/** Name/{pname,version,semver} are mutually exclusive */
, PDQEC_MIX_NAME = 2
/** Version/semver are mutually exclusive */
, PDQEC_MIX_VERSION_SEMVER = 3
, PDQEC_INVALID_SEMVER = 4 /**< Semver Parse Error */
, PDQEC_INVALID_LICENSE = 5 /**< License has invalid character */
, PDQEC_INVALID_SUBTREE = 6 /**< Unrecognized subtree */
, PDQEC_CONFLICTING_SUBTREE = 7 /**< Conflicting subtree/stability */
, PDQEC_INVALID_SYSTEM = 8 /**< Unrecognized/unsupported system */
, PDQEC_INVALID_STABILITY = 9 /**< Unrecognized stability */
} errorCode;
protected:
static std::string errorMessage( const error_code & ec );
public:
PkgQueryInvalidArgException( const error_code & ec = PDQEC_ERROR )
: flox::FloxException(
PkgQueryInvalidArgException::errorMessage( ec )
)
, errorCode( ec )
{}
}; /* End struct `PkgDbQueryInvalidArgException' */

/**
* Sanity check parameters.
* Make sure `systems` are valid systems.
* Make sure `stability` is a valid stability.
* Make sure `pathPrefix` is consistent with `subtree`, `systems`,
* and `stability`.
* Make sure `stabilities` are valid stabilities.
* Make sure `name` is not set when `pname`, `version`, or `semver` are set.
* Make sure `version` is not set when `semver` is set.
* @return `true` iff the above conditions are met.
* @return `std::nullopt` iff the above conditions are met, an error
* code otherwise.
*/
bool validate();
std::optional<PkgQueryInvalidArgException::error_code> validate() const;

}; /* End struct `PkgQueryArgs' */


std::string buildPkgQuery( PkgQueryArgs && params );
/* -------------------------------------------------------------------------- */

/* A SQL statement string with a mapping of host parameters to their
* respective values. */
using SQLBinds = std::unordered_map<std::string, std::string>;

/**
* Construct a SQL query string with a set of parameters to be bound.
* Binding is left to the caller to allow a single result to be reused across
* multiple databases.
*
* This routine does NOT perform filtering by `semver`.
* TODO: filter by `match`.
*
* @return A SQL statement string with a mapping of host parameters to their
* respective values.
*/
std::pair<std::string, SQLBinds> buildPkgQuery( const PkgQueryArgs & params );


/* -------------------------------------------------------------------------- */
Expand Down
5 changes: 5 additions & 0 deletions include/flox/pkgdb/read.hh
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ class PkgDbReadOnly {
*/
std::optional<size_t> distanceFromMatch( Package & pkg, std::string match );

/* -------------------------------------------------------------------------- */

bool isSQLError( int rc );


/* -------------------------------------------------------------------------- */

} /* End Namespace `flox::pkgdb' */
Expand Down
16 changes: 8 additions & 8 deletions src/command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@

/* -------------------------------------------------------------------------- */

namespace flox {
namespace command {
namespace flox::command {

/* -------------------------------------------------------------------------- */

VerboseParser::VerboseParser( std::string name, std::string version )
VerboseParser::VerboseParser( const std::string & name
, const std::string & version
)
: argparse::ArgumentParser( name, version, argparse::default_arguments::help )
{
this->add_argument( "-q", "--quiet" )
Expand Down Expand Up @@ -115,9 +116,9 @@ AttrPathMixin::addAttrPathArgs( argparse::ArgumentParser & parser )
.help( "Attribute path to scrape" )
.metavar( "ATTRS..." )
.remaining()
.action( [&]( const std::string & p )
.action( [&]( const std::string & path )
{
this->attrPath.emplace_back( p );
this->attrPath.emplace_back( path );
}
);
}
Expand All @@ -126,7 +127,7 @@ AttrPathMixin::addAttrPathArgs( argparse::ArgumentParser & parser )
void
AttrPathMixin::postProcessArgs()
{
if ( this->attrPath.size() < 1 ) { this->attrPath.push_back( "packages" ); }
if ( this->attrPath.empty() ) { this->attrPath.push_back( "packages" ); }
if ( this->attrPath.size() < 2 )
{
this->attrPath.push_back(
Expand All @@ -144,8 +145,7 @@ AttrPathMixin::postProcessArgs()

/* -------------------------------------------------------------------------- */

} /* End namespaces `flox::command' */
} /* End namespaces `flox' */
} /* End namespaces `flox::command' */


/* -------------------------------------------------------------------------- *
Expand Down
Loading

0 comments on commit 5487967

Please sign in to comment.