RDF-Query-2.910/000755 000765 000024 00000000000 12173312225 013330 5ustar00gregstaff000000 000000 RDF-Query-2.910/bin/000755 000765 000024 00000000000 12173312223 014076 5ustar00gregstaff000000 000000 RDF-Query-2.910/Changes.ttl000644 000765 000024 00000361357 12173312036 015444 0ustar00gregstaff000000 000000 # -*-n3-*- @prefix dc: . @prefix dcterms: . @prefix doap: . @prefix asc: . <> dc:description "Changes for RDF::Query" ; dcterms:references . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.910" ; doap:created "2013-07-22" ; ]; asc:changes [ asc:update "Updated RDQL parser to work with recent RDF::Trine releases." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.909" ; doap:created "2012-11-24" ; ]; asc:changes [ asc:addition "Added support for VALUES graph pattern (in-place BINDINGS)." ; asc:addition "Added support for UUID() and STRUUID() functions." ; asc:addition "Added is_update() and specifies_update_dataset() methods to RDF::Query." ; asc:update "Accept common typo of SEPARATOR in SPARQL 1.1 parser ('SEPERATOR') with constructor arg 'allow_typos' in RDF::Query::Parser::SPARQL11." ; asc:update "Fixed bad sparql serialization of filters with equality tests; was using '==' instead of '=' (github issue 53)." ; asc:update "Fixed bug in RDF::Query::Algebra::Service->referenced_variables." ; asc:update "Fixed bug that wasn't passing the active graph to EXISTS filter evaluation." ; asc:update " Fixed bug that wasn't passing the active graph to EXISTS filter evaluation." ; asc:update "Fixed RDF::Query prepare and execute methods to properly localize the model object." ; asc:update "Fixed bug in RDQL parser that mistakenly required a USING clause (github issue 70)." ; asc:update "Fixed handling of aggregates over empty groups." ; asc:update "Fixed SPARQL 1.1 parsing to enforce not using shared bnode labels between update data operations." ; asc:update "Improved SPARQL 1.1 parser detection of invalid use of BIND() (when binding already in-scope variables)." ; asc:update "Fixed bug in SPARQL 1.1 parser to recognize legal Update operations." ; asc:update "Updated SPARQL 1.1 parser to allow colon in local part of a prefixname." ; asc:update "Updated STRBEFORE() and STRAFTER() implementations to track SPARQL 1.1 standard." ; asc:update "Updated property path implementation to track W3C standard (changed counting semantics and dropped {m,n} form)." ; asc:update "Added support to passthrough query eval to the model if supported and the 'allow_passthrough' option is set on the query object." ; asc:update "Added 'canonicalize' option to RDF::Query constructor to canonicalize literal values." ; asc:update "Updated handling of BIND() in the SPARQL 1.1 parser to match the latest spec semantics." ; asc:update "Added ability to run tests of type mf:CSVResultFormatTest." ; asc:update "Fixed config handling in rqsh to allow the use of hexastore backends." ; asc:update "Merged Log4perl initialization cleanup patches (from github/kba)." ; asc:update "Use $plan->explain instead of $plan->sse for 'explain' rqsh command." ; asc:update "Updated EARL IRIs in bin/failing_earl_tests.sh and bin/passing_earl_tests.sh." ; asc:update "Removed RDF::Redland recommendation in Makefile.PL." ; asc:update "Added doap:implements statements, and updated release data to doap.rdf." ; asc:update "Updated RDF::Query::Util::cli_parse_args to allow no-argument setup." ; asc:update "Updated xt/dawg/earl.pl to use new EARL IRIs earl:passed and earl:failed." ; asc:update "Added POD to bin/rqsh." ; asc:update "Updated DAWG test harnesses to support expected query bindings results in RDF/XML format." ; asc:update "Fixed xt/dawg-eval11.t to emit TAP failures when query parsing fails." ; asc:update "Require RDF::Endpoint 0.05 in xt/dawg-eval11.t." ; asc:update "Removed values from test directory list in xt/dawg-eval11.t." ; asc:update "Added values to list of test directories in xt/dawg-eval11.t." ; asc:update "Added exists test directory to xt/dawg-eval11.t." ; asc:update "Added test case confirming bad sparql serialization of equality testing filters (github issue 53)." ; asc:update "Removed tests for {m,n} property path forms." ; asc:update "Fixed RDF::Query::Algebra::Project to throw exception on bad constructor arguments." ; asc:update "Added bugtracker info to Makefile.PL." ; asc:update "Added POD marking modules as unstable: RDF::Query::BGPOptimizer, RDF::Query::Compiler::SQL, RDF::Query::Federate, RDF::Query::Federate::Plan" ; asc:update "Improved expected/actual results output when encountering failing tests." ; asc:update "Removed old bloom-filter federation code." ; asc:update "Removed RDF::Query::ExecutionContext->base method." ; asc:update "Fix POD in RDF::Query::Algebra::Table." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.908" ; doap:created "2012-01-31" ; ]; asc:changes [ asc:addition "Added support for Service as a binary op (allowing variable-endpoint SERVICE blocks)." ; asc:addition "Added implementations for functions STRBEFORE, STRAFTER, and REPLACE." ; asc:addition "Added RDF::Query->prepare_with_named_graphs method." ; asc:addition "Added support for COPY and MOVE operations." ; asc:addition "Allow percent encoding and backslash escaping in prefix names." ; asc:update "Fixed SPARQL serialization of expressions using && and ||." ; asc:update "Fixed SPARQL 1.1 parser to support 'GRAPH' without whitespace." ; asc:update "Fixed bug resulting in false positive error when projecting expressions with aggregates." ; asc:update "Fixed aggregate evaluation to result in unbound variables on error (instead of dropping the result)." ; asc:update "Fixed numeric divide operation to return xsd:decimal when operands are xsd:integers." ; asc:update "Fixed RDF::Query::Expression::Binary->evaluate to properly throw on div-by-zero." ; asc:update "Fixed RDF::Query::Expression::Function->evaluate to propogate type errors in IF()." ; asc:update "Fixed bin/rqsh to handle queries that use BASE." ; asc:update "Fixed bug in RDF::Query::Plan::Join::PushDownNestedLoop that produced invalid results when the RHS was a subselect." ; asc:update "Fixed RDF::Query::Algebra::Filter->as_sparql to handle variation of serialization of the child pattern." ; asc:update "Fixed bug in SPARQL 1.1 parser that mistakenly introduced aggregate operations in non-aggregate queries." ; asc:update "Fixed RDF::Query::Expression::Binary to canonicalize numeric literal results." ; asc:update "Added syntax support for SILENT form of LOAD." ; asc:update "Added support for SILENT and variable endpoint handling for SERVICE patterns." ; asc:update "Added syntax support for optional GRAPH keyword on SPARQL 1.1 update shortcuts." ; asc:update "Updated RDF::Query::Plan::Extend to copy variable bindings instead of using the existing reference." ; asc:update "Added RDF::Query::Plan::Extend->explain." ; asc:update "Changed explain() syntax of plan quads." ; asc:update "Updated plan classes to optionally register intermediate results with a execution delegate object." ; asc:update "Made RDF::Query::Plan::Construct uniq the returned triples." ; asc:update "Added custom RDF::Query::Plan::Construct->explain method." ; asc:update "Normalize language tags used in SPARQL query syntax to lowercase." ; asc:update "Modularize RDF::Query::Plan::Service to allow mock testing." ; asc:update "Added exception handling in RDF::Query->set_error." ; asc:update "Added subplans_of_type method to Plan classes." ; asc:update "Fixed use of '__DEFAULT__' sentinel value in RDF::Query::Plan, RDF::Query::Plan::Service, RDF::Query::Node::Resource->as_sparql, and RDF::Query->as_sparql." ; asc:update "Force the planner to avoid using a bind-join when the RHS contains a Service subplan (to avoid a DOS attack on the remote endpoint)." ; asc:update "Updated DATATYPE() to return rdf:langString on language-tagged literals (per RDF 1.1 change)." ; asc:update "Fixed sse serialization in RDF::Query::Algebra::Service to handle binary op (variable endpoint) form." ; asc:update "Croak rather than die in some places, confess and use logdie in one place" ; asc:update "Allow aggregates in ORDER BY clause." ; asc:update "Added examples/query_url.pl." ; asc:update "Added RDF::Trine::Error::UnimplementedError exception class." ; asc:update "Updated required version of RDF::Trine to 0.138." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.907" ; doap:created "2011-06-04" ; ]; asc:changes [ asc:addition "Fixed bug in SPARQL 1.1 parser for DESCRIBE queries without a WHERE clause." ; asc:addition "Fixed join ordering bug for queries with a BINDINGS clause and several joins." ; asc:addition "Fixed RDF::Query->as_sparql for DESCRIBE queries which project URI terms, not variables." ; asc:addition "Fixed expected test results for DESCRIBE queries without a WHERE clause." ; asc:addition "examples/query.pl now emits a warning if the query object cannot be constructed." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.906" ; doap:created "2011-05-14" ; ]; asc:changes [ asc:addition "Added support for empty SPARQL 1.1 Update sequence operations (allowing no-op updates)." ; asc:addition "Added options to RDF::Query->new to allow forcing RDF::Query to disable query delegation to the underlying store." ; asc:addition "Added option to RDF::Query::Util::cli_parse_args to specify arbitrary options to be supplied to the RDF::Query->new constructor." ; asc:addition "Added new, cleaner line-based 'explain' format for serializing query Plan and Algebra objects." ; asc:addition "Added bindings accessor and is_unit method to RDF::Query::Plan::Constant." ; asc:addition "Added ability to forcibly remove a particular join algorithm from consideration in plan generation." ; asc:addition "Added 'execute' command in rqsh to allow loading a query from a file or URL." ; asc:update "Globally changed 'base' to 'base_uri' in code and API." ; asc:update "Fixed SPARQL serialization in RDF::Query::Plan generation of SERVICE blocks to always include braces around the query body." ; asc:update "Fixed SPARQL 1.1 parsing bug in property paths using alternatives ('|')." ; asc:update "Fixed SPARQL 1.1 bug in parsing prefixnames where a graph term is expected." ; asc:update "Fixed bug in ZeroOrMore reverse path handling (patterns matching { var path* term })." ; asc:update "Fixed bug in RDF::Query::Node::Literal::_cmp that was causing wrong node comparison results." ; asc:update "Fixed bug in parsing solution modifiers in CONSTRUCT and DESCRIBE queries." ; asc:update "Fixed bug in handling of unbound variables in delete templates in RDF::Query::Plan::Update." ; asc:update "Fixed bug in calls to RDF::Query->var_or_expr_value that was preventing use of EXISTS filters in project expressions." ; asc:update "Updated RDF::Query::Plan::Iterator->new to allow passing in a callback that will return an iterator, instead of an iterator directly." ; asc:update "Updated RDF::Query->query_plan to delegate entire queries to the RDF::Trine::Model when possible." ; asc:update "Updated bin/deparse.pl to allow specifying an endpoint url for SPARQL-backed models (which can affect the query plan)." ; asc:update "Updated SPARQL 1.1 parser to prevent bnode use in DELETE blocks." ; asc:update "Updated rqsh to associate common filename extensions with media types (helps in loading local files)." ; asc:update "Updated RDF::Query::Plan::Path to align with new ALP algorithm from the spec text." ; asc:update "Added some simple statistics generation code in RDF::Query::Plan::Join subclasses." ; asc:update "Added missing implementation for property paths using {n,} modifier syntax." ; asc:update "Added code to guard against mishandling unbound/blank/variable nodes in DELETE templates in RDF::Query::Plan::Update." ; asc:update "Updated tests to use new RDF::Trine::Iterator->seen_count method instead of deprecated count method." ; asc:update "Updated serialization text for zero-length paths in RDF::Query::Plan::Path->plan_node_data." ; asc:update "Removed warning of unknown options passed to RDF::Query->new." ; asc:update "Removed unused projection code in RDF::Query->execute_plan." ; asc:update "Removed Digest::SHA1 from prereq modules, and updated code to use Digest::SHA instead." ; asc:update "Remove the meaningless 'All rights reserved' copyright text." ; asc:update "Fixed test count in t/sparql11-propery_paths.t." ; asc:update "Commented out noisy debugging output in RDF::Query::Plan::Path." ; asc:update "Added trace debugging in RDF::Query::Plan::Sort." ; asc:update "Added options instance variable to RDF::Query::ExecutionContext." ; asc:update "Added Module::Pluggable to list of required modules in Makefile.PL." ; asc:update "Added mappings from file extensions to media types in dawg test scripts." ; asc:update "Various updates to SPARQL 1.1 dawg test harness scripts." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.905" ; doap:created "2011-02-18" ; ]; asc:changes [ asc:addition "Added SPARQL 1.1 numeric functions: ABS, CEIL, FLOOR, ROUND, RAND" ; asc:addition "Added SPARQL 1.1 string functions: CONCAT, SUBSTR, STRLEN, UCASE, LCASE, ENCODE_FOR_URI, CONTAINS, STRSTARTS, STRENDS" ; asc:addition "Added SPARQL 1.1 date functions: NOW, TIMEZONE, TZ, YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS" ; asc:addition "Added SPARQL 1.1 hashing functions: MD5, SHA1, SHA224, SHA256, SHA384, and SHA512" ; asc:addition "Added RDF::Query::Functions->install_function method, and implementations for fn:compare, fn:concat, and fn:substring." ; asc:addition "Updated RDF::Query->execute_with_named_graphs to accept optional arguments to be passed to C<< execute >>." ; asc:addition "Added support for CONSTRUCT WHERE syntax shortcut." ; asc:addition "Added support for SILENT modifier to SERVICE patterns." ; asc:addition "Added SILENT flag in RDF::Query::Algebra::Service." ; asc:addition "Added initial code to create SPIN serializations of queries." ; asc:addition "Added RDF::Query::Node::Literal->type_list method." ; asc:update "Fixed mistaken case sensitivity of COALESCE, BNODE, CONCAT, and SUBSTR keywords in SPARQL 1.1 parser." ; asc:update "Fixed parsing ambiguity between MIN aggregate and MINUTES function." ; asc:update "Fixed Xpath fn:timezone-from-dateTime function return for UTC timezones." ; asc:update "Fixed RDF::Query::Algebra::Project->as_sparql handling of binary select expressions." ; asc:update "Fixed RDF::Query::Algebra::Construct->sse serialization to include construct triples." ; asc:update "Updated handling of BIND() to not close group scope (which was applying filters in the wrong place)." ; asc:update "Fixed RDF::Query::Parser::SPARQL11 to handle whitespace between tokens in 'INSERT DATA' and 'DELETE DATA'." ; asc:update "Fixed RDF::Query::Parser::SPARQL11 to handle empty ModifyTemplates." ; asc:update "Fixed SPARQL 1.1 parser to properly set relevant datasets for update operations." ; asc:update "Fixed plan code for zero-length paths to return RDF::Query::Node-based results (not Trine-based)." ; asc:update "Fixed bug in RDF::Query::Plan::Clear when attempting to clear the default graph." ; asc:update "Fixed exception throwing on numeric binary expression eval without numeric literal arguments." ; asc:update "Updated RDF::Query::Plan to handle update operations with different datasets for matching (USING clause)." ; asc:update "Updated xt/dawg-syntax11.t to find more syntax queries in the dawg test data." ; asc:update "SPARQL 1.1 parser now throws errors for more classes of syntactically invalid queries (repeated expression aliases, wrong number of BINDINGS values, SELECT * with use of grouping)." ; asc:update "Added support for non-strict comparisons of xsd:strings in RDF::Query::Node::Literal (based on $LAZY_COMPARISONS variable)." ; asc:update "Major refactor of xt/dawg-eval11.t test harness." ; asc:update "Updated required version of Test::More to 0.88 (0.86 doesn't have done_testing() used in t/store-config.t)." ; asc:update "Updated RDF::Query::Expression::Function->evaluate to throw an error if no function coderef is found." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.904" ; doap:created "2010-11-23" ; ]; asc:changes [ asc:addition "Added 'results' command to rqsh to allow serializing query results in SPARQL XML format." ; asc:addition "Added 'set prefix' and 'time' commands to rqsh." ; asc:addition "Added string concatenation overloading of binary +." ; asc:addition "Added xpath function implementations." ; asc:addition "Added SPARQL 1.1 Update shortcut syntax support for COPY, MOVE, and ADD graph operations." ; asc:update "Fixed bug in property path evaluation on encountering blank path nodes." ; asc:update "Fixed use of binary '*' in FILTER without numeric data in t/resultforms.t." ; asc:update "Fixed bin/dawg11-status.pl to only load SPARQL 1.1 earl result files." ; asc:update "Fixed RDF::Query::Plan::Update to ignore non-ground triples (unbound variables don't act as wildcards)." ; asc:update "Fixed handling of named graph data in insert/delete patterns of update operations." ; asc:update "Updated RDF::Query::Util to use RDF::Trine::Model->temporary_model and guess an appropriate parser based on filenames." ; asc:update "Updated namespace URI in RDF::Query::Functions::Jena to ." ; asc:update "Updated namespace URI in RDF::Query::Functions::Xpath to ." ; asc:update "Updated xt/dawg-eval11.t to align with updated test case manifest schemas." ; asc:update "Updated xt/dawg-eval11.t to prevent test cases with missing files from emitting failures (now skipped)." ; asc:update "Updated documentation to explicitly discuss inherited methods due to subclassing." ; asc:update "Fixed bug in RDF::Query::Plan::Construct that wasn't giving each result a new set of blank nodes (RT 63155)." ; asc:update "Updated SYNOPSIS and execute() and execute_plan() methods POD (RT 63153 comment from Ivan Shmakov)." ; asc:update "Fixed RDF::Query::Algebra::Sequence->sse to serialize operations in proper order." ; asc:update "Added $silent argument to RDF::Query::Algebra::Clear constructor." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.903" ; doap:created "2010-11-03" ; ]; asc:changes [ asc:addition "Added support for BIND(expr AS ?var) operator." ; asc:addition "Added isNumeric built-in function, checking for proper datatype and valid lexical forms for numerics." ; asc:addition "Added ability to show the parsed algebra with 'parse' command in rqsh." ; asc:addition "Added abiility to show the query execution plan with the 'explain' command in rqsh." ; asc:addition "Added ability to change the RDF serializer with the 'serializer' command in rqsh." ; asc:addition "Added ability to initialize new stores with the 'init' command in rqsh." ; asc:addition "Added ability to change underlying store with the 'use' command in rqsh." ; asc:addition "Added 'help' command in rqsh." ; asc:addition "Added trace debugging to RDF::Query::Plan::Union." ; asc:addition "Added trace debugging to RDF::Query::Plan::Project." ; asc:addition "Added trace debugging to RDF::Query::Plan::Path." ; asc:addition "Added SPARQL 1.1 syntax and eval test harnesses." ; asc:update "Updated packaging to install rqsh script (renamed from old bin/rqsh.pl)." ; asc:update "Updated rqsh to catch errors thrown while trying to create new storage backends (e.g. DBI->connect failing)." ; asc:update "rqsh now defaults to read-write models." ; asc:update "rqsh now sets a base URI for the current working directory." ; asc:update "Password input now doesn't echo to the terminal in rqsh." ; asc:update "Filename input now autocompletes via Term::ReadLine in rqsh." ; asc:update "Updated SPARQL 1.1 parser to handle WITH clauses with PrefixedNames." ; asc:update "SPARQL 1.1 parser now sets error and returns if no query string is supplied." ; asc:update "Added more exception handling in RDF::Query::Parser::SPARQL11." ; asc:update "Updated CLEAR and DROP handling in SPARQL 1.1 parser and engine to handle 'NAMED' and 'ALL' variants." ; asc:update "Renamed 'binding_variables' methods to 'potentially_bound' to align with current SPARQL 1.1 terminology." ; asc:update "Results returned by property path plans are now RDF::Query::VariableBindings objects and are properly projected." ; asc:update "The query planner now interprets simple paths as basic graph patterns." ; asc:update "Removed Algebra and Plan classes for Not and Exists operations (now handled in other classes)." ; asc:update "Updated RDF::Query::Plan::Extend to drop the extended variable binding if evaluating the extension yields an error." ; asc:update "Updated RDF::Query::Plan::Update to accept delete patterns with variables." ; asc:update "Updated subquery plan objects to use a sub-plan, not a full query object." ; asc:update "Fixed SPARQL 1.1 parser bug that was returning partial data from a failed parse call." ; asc:update "Fixed SPARQL 1.1 parser bug for queries with multiple FILTERs in a GGP including an EXISTS filter." ; asc:update "Fixed SPARQL 1.1 parser bug for DROP/CLEAR queries." ; asc:update "Fixed SPARQL 1.1 parser typo of 'SEPARATOR' argument to GROUP_CONCAT aggregate." ; asc:update "Fixed SPARQL 1.1 parser to handle FITLERS to the left of MINUS/OPTIONAL and allow projecting '*' with other vars/epressions." ; asc:update "Fixed SPARQL 1.1 parser case sensitivity of project expression 'AS' token." ; asc:update "Fixed SPARQL 1.1 parser handling of of subqueries within GRAPH blocks." ; asc:update "Fixed miscellaneous bugs and improved performance of parsing SPARQL 1.1 queries." ; asc:update "Made parsing of very large triple blocks non-recursive in SPARQL 1.1 parser." ; asc:update "Fixed COALESCE function to handle errors properly." ; asc:update "Fixed RDF::Query::Plan::Aggregate to extend results using AliasExpressions from HAVING clauses." ; asc:update "Fixed bug that was ignoring GROUP BY clauses when no aggregate operations were present." ; asc:update "Fixed bad looping on start node of unbounded property paths." ; asc:update "Fixed bug in query planning of '^' property paths." ; asc:update "Fixed zero-length property path evaluation within GRAPH blocks." ; asc:update "Removed plan duplication of zero-length case in '*' property paths." ; asc:update "Fixed handling of named graphs within subqueries (changes to active graph handling in the query planner)." ; asc:update "Fixed type-promotion and handling of numeric types in aggregate handling." ; asc:update "Fixed RDF::Query::Algebra::Update->as_sparql to handle INSERT/DELETE updates properly." ; asc:update "Fixed RDF::Query::Plan::Aggregate->sse to allow handling of (non-blessed, scalar) '*' columns." ; asc:update "Added ability to pass through arguments to the query planner in RDF::Query->prepare." ; asc:update "Updated RDF::Query::Node::Literal::_cmp to handle literals that fail to parse as DateTime objects." ; asc:update "Updated RDF::Query::Plan::Limit to use the limit accessor internally." ; asc:update "RDF::Query::Plan::NamedGraph now uses lazy iterator for graph names." ; asc:update "Updated RDF::Query::VariableBindings to subclass the new RDF::Trine::VariableBindings." ; asc:update "RDF::Query::Algebra::Project now implements bind_variables()." ; asc:update "Updated RDF::Query->describe to use $model->bounded_description instead of simply outgoing arcs." ; asc:update "Fixed bug in evaluation of function expressions in which the execution context object was lost (causing EXISTS filters to fail)." ; asc:update "Fixed optimization of COUNT(*) over 1-triple BGPs to keep the variable binding for the pseudo-variable ?COUNT(*)." ; asc:update "Fixed sse serialization in RDF::Query::Algebra::Distinct." ; asc:update "Fixed RDF::Query::Plan::Join::PushDownNestedLoop to close sub-plans in close() method." ; asc:update "Replaced calls to die with throwing exceptions." ; asc:update "RDF::Query->new now warns about unrecognized options." ; asc:update "DATATYPE() now throws an exception unless passed a valid node object." ; asc:update "RDF::Query::Expresion::Binary now throws exceptions on numeric comparisions with non-numeric operands." ; asc:update "Fixed error message in RDF::Query::Plan::NamedGraph." ; asc:update "Fixed bug in xt/dawg-eval11.t parsing of srx files containing literal values that evaluate to false in perl (e.g. '0')." ; asc:update "Fixed turtle output for rdfs:comments in xt/dawg/earl.pl." ; asc:update "Moved developer tests from t/ to xt/." ; asc:update "Refactored RDF::Query::Functions into several auto-loaded sub-packages via Module::Pluggable (patch from tobyink)." ; asc:update "Removed references to the now-deprecated mac.com in network-based tests." ; asc:update "Updated expected values in t/queryform-describe.t for results using bounded descriptions." ; asc:update "Updated RDF::Query::Util to look for .prefix-cmd cache of common namespace names." ; asc:update "Updated RDF::Query::Util::cli_parse_args to accept '-r' as a read-only flag." ; asc:update "Updated required version of RDF::Trine to 0.126." ; asc:update "Updated to use URI 1.52 to allow direct usage of IRIs without unicode workarounds." ; asc:update "Made code updates to improve performance based on profiling." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.902" ; doap:created "2010-07-02" ; ]; asc:changes [ asc:update "Fixed bug in requiring prerequisite modules." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.901" ; doap:created "2010-06-29" ; ]; asc:changes [ asc:addition "Added support for SPARQL 1.1 CREATE/DROP GRAPH update operations." ; asc:update "Fixed named graph handling." ; asc:update "Fixed bug in handling of * aggregates." ; asc:update "Simplified code to create new temporary models when FROM clauses dereference graph data." ; asc:update "Fixed infinite loop bug on parsing certain invalid SPARQL 1.1 update queries." ; asc:update "Fixed handling of BGPs within named graphs in RDF::Query::Plan::BasicGraphPattern." ; asc:update "Updated syntax error exception messages in SPARQL parsers." ; asc:update "Fixed property path evaluation within named graphs." ; asc:update "Converted Makefile.PL to properly use Module::Install." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.900" ; doap:created "2010-06-23" ; ]; asc:changes [ asc:addition "Added bin/json.pl to print a JSON-formatted representation of the query algebra." ; asc:addition "Added bin/rqsh.pl CLI interface to executing updates and queries." ; asc:addition "Added ability to serialize algebra and plan in bin/deparse.pl." ; asc:addition "Added support for SPARQL 1.1 Basic Query Federation with BINDINGS and UNDEF support." ; asc:addition "Added support for SPARQL 1.1 functions: IF, STRDT, STRLANG, IRI, URI, BNODE." ; asc:addition "Added support for SPARQL 1.1 infix operators IN and NOT IN." ; asc:addition "Added support for SPARQL 1.1 Aggregates COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, and SAMPLE." ; asc:addition "Added support for SPARQL 1.1 Property paths." ; asc:addition "Added support for SPARQL 1.1 Negation with MINUS graph patterns and NOT EXISTS filters." ; asc:addition "Added support for SPARQL 1.1 INSERT/DELETE DATA, INSERT/DELETE WHERE, LOAD, and CLEAR operations." ; asc:addition "Added support for multi-statement SPARQL 1.1 Updates." ; asc:addition "Added as_hash method to Query, Algebra, and Node classes." ; asc:addition "Added new RDF::Query::Plan::ComputedStatement class." ; asc:addition "Added new RDF::Query::Plan::Iterator class." ; asc:addition "Implemented optimization for COUNT(*) over a single triplepattern." ; asc:update "SPARQL 1.1 is now the default query parser." ; asc:update "RDF::Core is no longer supported as a backend triplestore." ; asc:update "Redland is still supported as a backend store, but updated handling of default and named graphs means that existing triples stored in Redland without a context (named graph) will not be accessible from RDF::Query." ; asc:update "Removed all bridge classes (RDF::Query::Model::*)." ; asc:update "Removed RDF::Query::Logger class." ; asc:update "Removed net filter function code from RDF::Query." ; asc:update "Removed SPARUL and SPARQLP parsers." ; asc:update "RDF::Query no longer loads URIs in FROM and FROM NAMED clauses with SPARQL 1.1 by default." ; asc:update "Added ability for RDF::Query::Plan::Iterator to fire a callback when execute() is called." ; asc:update "Fixed whitespace unescaping bug in SPARQL (1.0 and 1.1) parsers." ; asc:update "bin/deparse.pl now catches parsing errors and prints stacktrace." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.202" ; doap:created "2010-05-22" ; ]; asc:changes [ asc:addition "Added initial SPARQL 1.1 syntax and eval support for select expressions, subqueries, negation, aggregates, subqueries, and basic federation." ; asc:addition "Added RDF::Query::VariableBindings.pm->set method." ; asc:addition "Added RDF::Query::_new constructor without any sanity or setup code (used for subquery construction)." ; asc:addition "Added RDF::Query::Node::Blank::new constructor to avoid RDF::Trine's N-Triples syntax requirements." ; asc:addition "Added SAMPLE and GROUP_CONCAT aggregate support." ; asc:addition "Added shortcut functions for constructing RDF::Query::Node objects and Algebra Triples, BGPs and GGPs." ; asc:addition "Added warning to RDF::Query::Algebra::Project constructor for improper variable lists." ; asc:update "Updated DAWG tests to use SPARQL 1.1 parser." ; asc:update "Removed SPARQLP aggregate tests that don't align with new SPARQL 1.1 semantics." ; asc:update "Updated DAWG eval tests to skip non-approved tests." ; asc:update "Fixed handling of unicode decoding in DAWG eval tests." ; asc:update "Bumped required version of RDF::Trine to 0.123." ; asc:update "Updated t/models.pl to use RDF::Trine::Parser->parse_url_into_model method and redland's 'guess' parser." ; asc:update "Fixed logging key name in RDF::Query::Plan::Exists." ; asc:update "Fixed sse and as_sparql methods in RDF::Query::Algebra::Exists." ; asc:update "Implemented RDF::Query::Algebra::Exists::binding_variables." ; asc:update "Updated BGPOptimizer to estimate selectivity directly instead of using costmodel code." ; asc:update "Removed costmodel code." ; asc:update "Removed unused fixup and execute methods in Algebra classes." ; asc:update "Updated RDF::Query to only instantiate DateTime parser and LWP::UserAgent if needed." ; asc:update "Changed DBPedia network test to align with recent DBPedia update in t/dev-service-description." ; asc:update "Updated SPARQLP parser tests to align with internal changes in RDF::Query::Algebra::Aggregate." ; asc:update "Updated RDF::Query::Algebra::Aggregate and RDF::Query::Plan::Aggregate to syntactically handle HAVING clauses." ; asc:update "Fixed bin/graph-bgp.pl (had bitrotted)." ; asc:update "Changed bnodes to named variables in examples/queries/sparql-bgp-people-knows.rq." ; asc:update "RDF::Query::Util::cli_make_query now defaults the 'class' parameter to 'RDF::Query'." ; asc:update "Removed dependency list and added perlrdf link to POD in RDF::Trine and RDF::Query." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.201" ; doap:created "2010-01-30" ; ]; asc:changes [ asc:addition "Added benchmark/lubm.pl." ; asc:addition "Added examples/queries/sparql-ask-people.rq." ; asc:addition "Added RDFa tests." ; asc:addition "Added Data::UUID prerequisite to META.yml and Makefile.PL." ; asc:update "Updated ::Model::RDFTrine::add_uri and ::add_string to use new RDF::Trine::Parser methods." ; asc:update "Updated as_sparql and sse code to work with more recent RDF::Trine versions." ; asc:update "Removed as_sparql caching in RDF::Query::Algebra::Triple." ; asc:update "Renamed RDF::Query test files to remove old-style numbering." ; asc:update "Updated parser tests to track new RDF::Trine::Node::Literal internal structure." ; asc:update "Updated prereq version of RDF::Trine to 0.114." ; asc:update "Fixed NAME POD section in RDF::Query::ServiceDescription (RT52264 from KjetilK)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.200" ; doap:created "2009-08-06" ; ]; asc:changes [ asc:addition "Added support for average aggregates, and fixed datatype support in aggregates." ; asc:addition "Added RDF::Query::Util as home to helper functions (added CLI args parsing functions to create queries and models)." ; asc:addition "Added examples of queries and service descriptions in examples/." ; asc:addition "Added new RDF::Query::ServiceDescription::new_with_model constructor." ; asc:addition "Added test data in data/federation_data/." ; asc:addition "Added t/federate.t with tests for optimistic federated query optimization using ::Plan::ThresholdUnion and RDF::Endpoint::Server." ; asc:addition "Added the ability to add label annotations to RDF::Query::VariableBindings, RDF::Query::Algebra::Triple and RDF::Query::Algebra::Quad objects." ; asc:addition "Added distinguish_bnode_variables method to RDF::Query::Algebra::Quad and RDF::Query::Algebra::Triple." ; asc:addition "Added RDF::Query::Node::Blank::make_distinguished_variable method." ; asc:addition "Added caching of sparql serializations to RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Triple." ; asc:addition "Added code to RDF::Query::VariableBindings::new to up-cast any RDF::Trine::Node objects to their RDF::Query equivalents." ; asc:addition "Added hook in RDF::Query::execute_plan() to print the query plan to STDERR if $options{plan} is set (can be set if defines)." ; asc:addition "Added threshold timeout argument to RDF::Query::Plan::ThresholdUnion and support for it in RDF::Query::Federate::Plan." ; asc:addition "Added 'optimistic_threshold_time' query flag to RDF::Query::ExecutionContext and RDF::Query constructor." ; asc:addition "Added bin/query.pl, a fork of examples/query.pl, to support simple query execution." ; asc:addition "Added and updated POD to RDF::Query::Util, RDF::Query::Node and RDF::Query::Plan and RDF::Query::Federate::Plan." ; asc:addition "Added check for RDFQUERY_THROW_ON_SERVICE environment variable in RDF::Query::Plan::Service." ; asc:addition "Added logging in RDF::Query::Plan::ThresholdUnion, and RDF::Query::Model." ; asc:addition "Added calls to Log::Log4perl::is_debug to eliminate unnecessary serialization of logging when not in use." ; asc:addition "Added check for ref($node) in RDF::Query::VariableBindings::new (broke code after previous removal of blessed() check)." ; asc:addition "Added use of defined() in code that had been testing boolean value of objects (and causing expensive string overloading)." ; asc:update "Fixed bug in RDF::Query::Plan::Offset in cases where the offset was beyond the end of the result set." ; asc:update "Fixed testing for Bloom::Filter in t/31-service.t." ; asc:update "Fixed test expectations when making remote DBPedia query in t/34-servicedescription.t." ; asc:update "Fixed check of $ENV{RDFQUERY_NETWORK_TESTS} to test boolean value, not just existence." ; asc:update "Fixed (bad) expected serializations in t/29-serialize.t." ; asc:update "Fixed bug in RDF::Query::Plan::ThresholdUnion attempting to close an iterator twice." ; asc:update "Fixed sse serialization issues in RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Project." ; asc:update "Fixed bug in RDF::Query::Node::from_trine() that up-cast blank nodes to variables." ; asc:update "Fixed quoting issue in RDF::Query::Algebra::Service::sse()." ; asc:update "Fixed parameter handling in bin/graph-qeps.pl:prune_plans()." ; asc:update "Fixed handling of 'GRAPH ?g {}' (empty GraphGraphPatterns) to return all graph names." ; asc:update "Fixed serialization quoting issue in RDF::Query::Algebra::Aggregate::sse()." ; asc:update "RDF::Query::Plan::Aggregate now attempts plain string comparisons for MIN,MAX when strict_errors is not set." ; asc:update "Simplified CLI argument parsing in bin/ and examples/ programs." ; asc:update "Cleaned up code in RDF::Query::Model::get_computed_statements()." ; asc:update "Updated SSE formatting and uninitialized warning in RDF::Query::Plan, RDF::Query::Algebra::Filter, RDF::Query::Algebra::Distinct and RDF::Query::Algebra::Sort." ; asc:update "Moved shared RDF::Trine-related model methods into RDF::Query::Model from subclasses." ; asc:update "Raised required RDF::Trine version to 0.111 which brings RDF::Trine::Graph support." ; asc:update "RDF::Query::Model::RDFTrine::BasicGraphPattern::execute now returns $self." ; asc:update "Removed dependency on Test::JSON, List::MoreUtils, and XML::Parser." ; asc:update "Removed TODO on test in t/29-serialize.t." ; asc:update "Removed unnecessary code in RDF::Query::Plan subclasses, bin/query.pl, bin/graph-bgp.pl and bin/graph-query.pl." ; asc:update "Changed logging level from debug to trace in RDF::Query::Plan::Triple, RDF::Query::Plan::Project, RDF::Query::Plan::Filter, RDF::Query::Plan::Join::NestedLoop, RDF::Query::Plan::PushDownNestedLoop, and RDF::Query::Model::RDFTrine." ; asc:update "Made error message more useful when SERVICE calls fail in RDF::Query::Plan::Service." ; asc:update "Rewrote the optimistic plan generator in RDF::Query::Federate::Plan." ; asc:update "Simplified logging in RDF::Query::Federate::Plan (now only logs to category 'rdf.query.federate.plan')." ; asc:update "RDF::Query::ServiceDescription now adds an 'origin' label annotations to RDF::Query::Algebra::Triple objects." ; asc:update "Removed check of the sd:definitive property in RDF::Query::ServiceDescription (was based on wrong assumptions)." ; asc:update "Updated RDF::Query::ServiceDescription::answers_triple_pattern() to recognize wildcard capabilities, constructor now adds wildcard by default." ; asc:update "RDF::Query::ServiceDescription::computed_statement_generator now returns empty iterators when passed triple patterns with bound blank nodes." ; asc:update "RDF::Query::Plan::Service now adds an 'origin' label annotation to the RDF::Query::VariableBindings object." ; asc:update "RDF::Query::Federate::add_service() now adds the appropriate computed statement generators to the query object." ; asc:update "Removed optimistic query rewriting test from t/34-servicedescription.t (now covered by t/federate.t)." ; asc:update "Updated dawg-eval.t to actually test graph equivalence." ; asc:update "Clarified graph labels in RDF::Query::Model::RDFTrine::BasicGraphPattern::graph()." ; asc:update "RDF::Query::Plan::Quad and RDF::Query::Plan::Triple now add an 'origin' label annotation to the RDF::Query::VariableBindings object if the underlying statement has one." ; asc:update "RDF::Query::Plan::prune_plans now uses a stable sort when comparing plan costs." ; asc:update "RDF::Query::Algebra::Triple::new now up-casts to RDF::Query node objects, if necessary." ; asc:update "RDF::Query::Model::RDFTrine::generate_plans now respects the force_no_optimization query flag." ; asc:update "RDF::Query::Algebra::BasicGraphPattern::sse() now sorts triples for output." ; asc:update "RDF::Query::new() now looks for $args{ defines }, and adds them to the internal %options HASH." ; asc:update "Updated RDF::Query::Plan to only consider model-specific plans (if there are any)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.100" ; doap:created "2009-03-18" ; ]; asc:changes [ asc:addition "Added ::Algebra::BasicGraphPattern::connected method." ; asc:addition "Added ::Model::node_count method." ; asc:addition "Added ::Model::RDFTrine::count_statements() and ::Model::RDFTrine::node_count() methods." ; asc:addition "Added 'node_counts' as a recognized key to ::Model::RDFTrine::suports()." ; asc:addition "Added 'store' field to the data returned by the meta() method in the ::Model::* classes." ; asc:addition "Added a peek method to ::Iterator to support execution deferment like in ::Algebra::Service." ; asc:addition "Added a RequestedInterruptError exception class." ; asc:addition "Added ability to force SERVICE calls to abort using $ENV{RDFQUERY_THROW_ON_SERVICE} and RequestedInterruptError." ; asc:addition "Added algebra classes for solution modifiers and query forms (construct, project)." ; asc:addition "Added benchmark/costmodel.pl for testing the RDF::Query::CostModel." ; asc:addition "Added benchmark/plans.pl to show the runtimes of the available QEPs for a query." ; asc:addition "Added bf methods to ::Triple and ::BasicGraphPattern to describe the BGP in terms of bound/free." ; asc:addition "Added bin/graph-bgp.pl to produce a png of a query's BGP variable connectivity graph." ; asc:addition "Added bin/graph-qeps.pl to vizualize all QEPs of a query with GraphViz." ; asc:addition "Added bin/graph-query.pl to graph the (one chosen) QEP tree." ; asc:addition "Added bind_variables method to RDF::Query::Algebra." ; asc:addition "Added caching to ::Algebra::Service::_names_for_node." ; asc:addition "Added code and tests for Query Execution Plan classes RDF::Query::Plan::*." ; asc:addition "Added code for RDF-Trine specific BGP query plans." ; asc:addition "Added code to count (and warn) the rate of false positives from a bloomjoin." ; asc:addition "Added cost model hooks in RDF::Query and ::Algebra::BasicGraphPattern." ; asc:addition "Added debugging information when RDFQUERY_THROW_ON_SERVICE is in effect." ; asc:addition "Added examples/create_query_api.pl for generating queries programatically (based on request from KjetilK)." ; asc:addition "Added examples/query.pl to show a simple example of loading data and executing a query." ; asc:addition "Added expected result count test in t/34-servicedescription.t." ; asc:addition "Added FeDeRate BINDINGS to the list of supported extensions." ; asc:addition "Added from_sse method to ::Statement, ::Node." ; asc:addition "Added get_basic_graph_pattern to ::Model::RDFTrine (not used yet)." ; asc:addition "Added initial code for ARQ-style property paths." ; asc:addition "Added initial parser code for SPARQL Update (SPARUL) extension." ; asc:addition "Added initial tests for algebra subsumes method." ; asc:addition "Added is_solution_modifier() methods to ::Algebra classes." ; asc:addition "Added labels and common patterns to service description template." ; asc:addition "Added logging and costmodel tests." ; asc:addition "Added logging for execution time (time to construct iterator) of Triples, BGPs, GGPs and sorting." ; asc:addition "Added logging of cardinalities in ::Algebra::Triple, ::Algebra::BasicGraphPattern and ::Algebra::Service." ; asc:addition "Added logging to plan classes ::NestedLoop, ::Service, ::Triple." ; asc:addition "Added more example capabilities and patterns to the test service descriptions." ; asc:addition "Added more logic and debugging to aggregating triples into BGPs for SERVICE calls." ; asc:addition "Added new 'UNSAID' syntax for SPARQLP, implementing negation." ; asc:addition "Added new bloom:filter function variant that doesn't use identity reasoning." ; asc:addition "Added optional restriction argument to ::Algebra::subpatterns_of_type." ; asc:addition "Added parse_expr method to RDF::Query::Parser::SPARQL." ; asc:addition "Added RDF::Query::Algebra::Quad::bf() method." ; asc:addition "Added RDF::Query::BGPOptimizer implementing a basic optimizer for basic selectivity-based join ordering." ; asc:addition "Added RDF::Query::CostModel classes for computing/estimating the cost of executing a specific pattern." ; asc:addition "Added RDF::Query::ExecutionContext to hold all necessary information for query execution (query, model, bound variables)." ; asc:addition "Added RDF::Query::Federate::Plan for federation-specific code." ; asc:addition "Added RDF::Query::Model::RDFTrine::BasicGraphPattner::graph() method." ; asc:addition "Added RDF::Query::Node::compare for sorting (either Trine or Query) node objects." ; asc:addition "Added RDF::Query::plan_class so that ::Federate can overload calls to ::Plan methods." ; asc:addition "Added RDF::Query::ServiceDescription for parsing DARQ-style service descriptions." ; asc:addition "Added RDF::Query::VariableBindings to wrap existing HASH-based variable binding structure." ; asc:addition "Added RDF::Trine::Store::Hexastore to test model construction list in t/models.pl." ; asc:addition "Added sparql:pattern data to test service descriptions." ; asc:addition "Added sse re-serialization test to t/29-serialize.t." ; asc:addition "Added stub ::Plan::ThresholdUnion class for running optimistic queries." ; asc:addition "Added support for computed statement generators (like ARQ's computed property support [e.g. list:member])." ; asc:addition "Added support for sparql:pattern in service description parsing." ; asc:addition "Added t/plan-rdftrine.t to test QEP generation optimized for RDF::Trine BGPs." ; asc:addition "Added test data for repeat patterns (e.g. { ?a ?a ?b})." ; asc:addition "Added tests to t/plan.t for QEP sse serialization." ; asc:addition "Added trial subsumes() methods to ::Algebra::BasicGraphPattern and ::Algebra::Triple." ; asc:addition "Added workaround to RDFCore bridge so that RDF::Core doesn't die if getStmts is called with a Literal in the subj or pred position." ; asc:update "Algebra classes now call RDF::Query::algebra_fixup for optimization before falling back on normal fixup code." ; asc:update "Allow equality test and disjunction filters in patterns that can be compiled to SQL." ; asc:update "Bloom filter use is now only attempted on SERVICE blocks that don't immediately contain a FILTER." ; asc:update "Changed debugging in RDF::Query modules to use Log::Log4perl." ; asc:update "Cleaned up federation tests." ; asc:update "Code now materializes all node identities before creating the Bloom filter (so capacity arg is accurate)." ; asc:update "Commented out in-progress service description tests." ; asc:update "Converted RDF::Query::execute() to use ::Plan classes." ; asc:update "Fixed ::Algebra::GroupGraphPattern to use join_bnode_streams in cases where the bloom filter wasn't automatically created but instead provided by the user." ; asc:update "Fixed ::Model::RDFTrine::as_string use with Quads." ; asc:update "Fixed argument list for RDF::Query::Algebra::Service::_names_for_node called from bloom function." ; asc:update "Fixed bitrotted livejournal example script to work with new 2.000 API." ; asc:update "Fixed bug in SQL compilation when restricting left-joins to specific node types (based on functions like isIRI)." ; asc:update "Fixed bug in t/23-model_bridge.t to allow two models from the same model class to be used in testing." ; asc:update "Fixed bug so ::Model::RDFTrine::meta may be called as a class method." ; asc:update "Fixed bugs in RDF::Query and RDF::Query::Expression classes that insisted variables be RDF::Query objects (and not simply RDF::Trine objects)." ; asc:update "Fixed error handling in t/plan.t." ; asc:update "Fixed node identity computation based on owl:sameAs." ; asc:update "Fixed propogation of iterator binding_names when pre-bound values are used in ::Algebra execution." ; asc:update "Fixed RDF::Query::algebra_fixup to ignore service capabilities that would result in empty BGPs." ; asc:update "Fixed RDF::Query::Algebra::Triple to correctly set binding_names when pre-binding is used." ; asc:update "Fixed RDQL parser to qualify URIs before returning from parse()." ; asc:update "Fixed SSE serialization bug in ::Algebra::Sort." ; asc:update "Fixed SSE serialization of Aggregates using '*' instead of a variable as the column." ; asc:update "Fixed t/costmodel-naive.t to provide a serialized query to ::Plan::Service constructor." ; asc:update "Fixed test count in algebra-bgp.t." ; asc:update "Fixed test in t/31-service.t that relied on identity-reasoning support in bloom filters (which is now off by default)." ; asc:update "Fixed test plan for t/optimizer.t in cases where no appropriate model is available." ; asc:update "Fixed use of pre-binding in execution of RDF::Trine optimized BasicGraphPatterns." ; asc:update "Fixed use of RDFQUERY_NETWORK_TESTS in 31-service.t." ; asc:update "Improved use of temporary RDF::Trine stores in RDF::Query tests." ; asc:update "Logging code now uses sse serialization as log keys (because of expressions that can't be serialized as SPARQL)." ; asc:update "Logging object can now be passed to RDF::Query constructor." ; asc:update "Made SERVICE work again by rolling back streaming socket work (now commented out)." ; asc:update "Marked tests TODO for federated query serialization." ; asc:update "Modified code for aggregate queries (though they are currently broken)." ; asc:update "Moved construction of bloom-filter-optimized patterns to RDF::Query::Algebra::GroupGraphPattern::_bloom_optimized_pattern()." ; asc:update "Moved federation code to new RDF::Query::Federate class." ; asc:update "Moved initial federated service code into RDF::Query::fixup and ::algebra_fixup." ; asc:update "Moved QEP generation code to RDF::Query::query_plan so it can be overloaded." ; asc:update "Moved RDF::Query::add_service() to RDF::Query::Federate::add_service()." ; asc:update "Naive plan is used in ::Federate::Plan::generate_plans only if no optimistic plans are available." ; asc:update "Parsing service descriptions now more forgiving in the face of missing values." ; asc:update "Plan generation now includes any plans the model object can provide." ; asc:update "RDF::Query now always uses a cost model (defaulting to ::Naive)." ; asc:update "RDF::Query::Algebra::Triple::new now adds RDF::Query::Node::Variable objects for undefined nodes." ; asc:update "RDF::Query::Federate now defaults to SPARQLP parser." ; asc:update "RDF::Query::fixup() now only returns a construct pattern (the query pattern now being returned by RDF::Query::query_plan())." ; asc:update "RDF::Query::Model::get_computed_statements now doesn't die if there's no query object present." ; asc:update "RDF::Query::new() now accepts two-argument form with just $query and \\%options." ; asc:update "RDF::Query::Node::Literal objects now can compare as equal when they're of numeric type but the lexical values aren't numeric." ; asc:update "RDF::Query::prune_plans now takes ExecutionContext as an argument, and in turn calls ::Plan::prune_plans." ; asc:update "RDF::Query::query_plan() now returns all possible query plans when called in list context." ; asc:update "RDF::Query::query_plans now calls RDF::Query::prune_plans to select from the list of possible QEPs." ; asc:update "RDF::Trine::Node::Resource now escapes unicode in base URIs (now just relative URI part) before calling URI->new_abs." ; asc:update "Re-ordered conditionals so that the service-bloom-filter try block is called less frequently." ; asc:update "Removed (now unused) parser generator script." ; asc:update "Removed logging/warn calls from forked process in ::Service (was screwing up the parent-child IO pipe)." ; asc:update "Removed now unused RDF::Query::construct() and RDF::Query::fixup()." ; asc:update "Removed old execute() code from ::Algebra classes." ; asc:update "Removed unused redland fallback code from RDF::Query::Model::RDFTrine." ; asc:update "Removed use of \"DISTINCT\" queries in SERVICE calls (for pipelining)." ; asc:update "Removed what looks like an accidentally pasted test in t/plan.t." ; asc:update "SERVICE execution now uses non-identity reasoning Bloom filter function." ; asc:update "SERVICE tests involving bloom filter handling marked as TODO." ; asc:update "ServiceDescription now only runs sofilter if ?subject/?object are bound." ; asc:update "ServiceDescription parsing now gets entire sparql:patterns and not just arcs to depth one." ; asc:update "Silenced unfinished test debugging in t/logging.t." ; asc:update "SPARQL parser now always adds a ::Algebra::Project (even when the query selects '*')." ; asc:update "SPARQL parser now constructs ::Algebra::Project objects (had been in RDF::Query::execute)." ; asc:update "SPARQL parser now creates ::Algebra::Construct objects for CONSTRUCT queries." ; asc:update "SPARQL parser now puts ::Distinct above ::Sort algebras in the parse tree." ; asc:update "SPARQLP parser now creates ::Project object as parent of any ::Aggregate." ; asc:update "Split RDF::Query::execute into prepare() and execute_plan() methods." ; asc:update "Started work on a more holistic approach to supporting service descriptions (instead of using add_computed_statement_generator())." ; asc:update "Turtle parser now doesn't modify the lexical value of numeric typed literals." ; asc:update "Turtle parser now makes universal IDs for all blank node (even those with given IDs like _:xyz)." ; asc:update "Updated ::Algebra SSE serializations to conform to Jena's serialization syntax." ; asc:update "Updated ::Algebra::Limit::sse to emit 'slice' serialization if its child is a ::Algebra::Offset." ; asc:update "Updated ::Algebra::Service to fork and use LWP's callback mechanism for concurrency." ; asc:update "Updated ::Compiler::SQL to recognize ::Algebra::Project objects." ; asc:update "Updated args to roqet call in failing_earl_tests.sh." ; asc:update "Updated as_sparql() methods to support the new ::Construct classes." ; asc:update "Updated bin/parse.pl to emit the SSE serialization of the query algebra tree." ; asc:update "Updated bin/rdf_parse_turtle.pl to warn on any parser error." ; asc:update "Updated cost model code to work with ::Plan classes instead of ::Algebra classes." ; asc:update "Updated costmodel and logging test expected results that changed due to changes to ::Plan::Join code." ; asc:update "Updated dawg-eval.t regex to recognize RDFTrine blank nodes." ; asc:update "Updated DBPedia test query in t/34-servicedescription.t to reflect new source data." ; asc:update "Updated expected cardinalities in t/logging.t for current plan choice." ; asc:update "Updated logging of algebra execution to use SPARQL serialization as logging key." ; asc:update "Updated logging tests to use the new sparql serialization keys." ; asc:update "Updated plan generation code to use ::BGPOptimizer when the model supports node_counts." ; asc:update "Updated RDF::Query to require version 0.108 of RDF::Trine." ; asc:update "Updated RDF::Query::sse to emit base and prefix serializations." ; asc:update "Updated SERVICE test in t/plan.t (still broken, but only runs when dev env var RDFQUERY_NETWORK_TESTS in effect)." ; asc:update "Updated SPARQL parser and serializer tests to always assume an ::Algebra::Project on SELECT queries." ; asc:update "Updated SSE serialization of ::Join::PushDownNestedLoop to use 'bind-join' terminology." ; asc:update "Updated t/plan.t to be less dependent on the specific state of the kasei.us service endpoint." ; asc:update "Updated test service description RDF for new tests." ; asc:update "Updates to improve logging and test coverage." ; asc:update "Updates to RDF::Query::execute() to support explicit pre-binding lists (instead of just scalar values)." ; asc:update "Updates to SPARQLP parser to support FeDeRate BINDINGS keyword." ; asc:update "::Algebra::Distinct now does proper serialization in as_sparql()." ; asc:update "::Algebra::GroupGraphPattern now throws an exception if passed unblessed values as patterns." ; asc:update "::Algebra::Service now defers loading of content until the first result is requested." ; asc:update "::Federate now labels nodes in the QEP tree with applicable services." ; asc:update "::GroupGraphPattern::sse() updated to output the '(join ...)' only if the GGP has more than one pattern." ; asc:update "::Model::debug() now shows data from the named graph model." ; asc:update "::Model::RDFTrine now only produces BGP plans if there is no get_computed_statement_generators in the query object." ; asc:update "::Model::RDFTrine::add_uri now decodes resulting content as utf8 before proceeding." ; asc:update "::Model::RDFTrine::meta() probes the underlying store object to declare the proper 'store' class." ; asc:update "::Service::_names_for_node updated to use ::Plan classes for query execution (fixes use of the k:bloom filter)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.002" ; doap:created "2008-04-25" ; ]; asc:changes [ asc:update "Updated Bloom::Filter required version in RDF-Query's Makefile.PL." ; asc:update "Fixed bug in get_function() when called as a class method." ; asc:update "Updated sparql:logical-* functions to accept more than two operands." ; asc:addition "Added code to make loading of Bloom::Filter optional." ; asc:addition "Added Bloom::Filter to list of recommended modules." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.001" ; doap:created "2008-04-19" ; ]; asc:changes [ asc:addition "Added test for using AS syntax for variable renaming: \"(?var AS ?newvar)\"." ; asc:addition "Added support for MIN, MAX, COUNT and COUNT-DISTINCT aggregate operators to the SPARQLP parser." ; asc:addition "Added COUNT DISTINCT variant aggregate operator." ; asc:addition "Added as_sparql submethod to RDF::Query::Node::Literal to serialize numeric types unquoted." ; asc:addition "Added var_or_expr_value method in RDF::Query." ; asc:addition "Added serialization tests for aggregate and union patterns." ; asc:addition "Added support for GROUP BY clauses to aggregation queries." ; asc:addition "Added methods to RDF::Query for retrieving lists of supported SPARQL extensions and extension functions." ; asc:addition "Added ::Algebra::subpatterns_of_type method for retrieving all subpatterns of a particular type." ; asc:addition "Added tests for select expressions." ; asc:addition "Added initial support for selecting expression values." ; asc:update "Fixed use of \"DESCRIBE \" (instead of \"DESCRIBE ?var\")." ; asc:update "Fixed SPARQL serialization of queries with DISTINCT." ; asc:update "Moved sort_rows() into new ::Algebra classes Sort, Limit, Offset and Distinct." ; asc:update "Updated SQL compiler to handle new ::Algebra classes." ; asc:update "Bumped required RDF::Trine version to 0.106." ; asc:update "RDF::Trine pattern optimization now holds onto the original BGP object for forwarding of calls to as_sparql()." ; asc:update "Removed use of Storable module. Query execution no longer clones parse tree before running." ; asc:update "Simplified project operation on query stream in RDF::Query::execute()." ; asc:update "fixup() method in ::Algebra and ::Expression now passed the query object as an argument." ; asc:update "Replaced ::RDFTrine::unify_bgp with more general fixup() implementation." ; asc:update "::Algebra classes now defer to the bridge during fixup() to allow model-specific optimizations." ; asc:update "RDF::Trine::Iterator::smap now allows overriding default construct_args (e.g. binding names)." ; asc:update "sparql:str now throws a TypeError if argument isn't bound." ; asc:update "Fixed referenced_variables in RDF::Query::Expression." ; asc:update "Fixed COUNT function to only count bound variables." ; asc:update "Fixed aggregation to work with expressions." ; asc:update "Removed now unused ::Algebra::OldFilter class." ; asc:update "Moved as_sparql methods from RDF::Trine:: to RDF::Query:: classes." ; asc:update "Removed context- (quad-) specific code from RDF::Query::Algebra::Triple." ; asc:update "Fixed serialization of BOUND filter functions." ; asc:update "Fixed serialization of unary expressions." ; asc:update "Fixed call to join_streams to use ::Iterator::Bindings instead of ::Iterator." ; asc:update "var_or_expr_value now takes bridge object as an argument." ; asc:update "var_or_expr_value will now accept any RDF::Query::Expression object as an argument." ; asc:update "Aggregates (MIN, MAX, COUNT, COUNT-DISTINCT) now return xsd:decimal values (this shouldn't really happen for non-numeric operands to MIN and MAX...)" ; asc:update "Removed unused _true and _false methods in RDF::Query." ; asc:update "Fixed existing aggregates code and tests." ; asc:update "Removed unused (and bitrotted) t/05-stress.t test." ; asc:update "Made all tests that access the network opt-in with the RDFQUERY_NETWORK_TESTS environment variable." ; asc:update "Updated POD for ::Expression::Alias." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "2.000" ; doap:created "2008-03-19" ; ]; asc:changes [ asc:addition "RDF::Query now uses RDF::Trine package for common classes (nodes, statements, iterators)." ; asc:addition "Added support for RDF::Trine::Model." ; asc:addition "RDF::Trine::Iterator can serialize to XML and JSON natively; Updated tests accordingly." ; asc:addition "Added RDF::Query::pattern method to return the GGP algebra pattern for the query." ; asc:addition "Added RDF::Query::as_sparql method to serialize query as SPARQL string." ; asc:addition "Added POD to RDF::Query::Node classes." ; asc:addition "Added equal method to RDF::Query::Node classes." ; asc:addition "Added definite_variables method to RDF::Query::Algebra classes." ; asc:addition "Added more (sparql and see) serialization tests." ; asc:addition "Added as_native method to bridge superclass for converting ::Node objects to bridge-native objects." ; asc:addition "Added support for SERVICE queries (ala ARQ) with Bloom filter semijoins." ; asc:update "Moved RDF::Query::Stream into RDF::Trine::Iterator namespace." ; asc:update "Reshuffled SPARQL parsers tests." ; asc:update "rdf namespace is only added by default for RDQL." ; asc:update "Fixed bug where the wrong bridge object would be returned for queries with FROM clauses." ; asc:update "Moved query_more code to Algebra classes." ; asc:update "Removed unused RDF::Query::set_named_graph_query method." ; asc:update "Fixed bug where triples from a FROM clause were loaded into the underlying persistent store." ; asc:update "RDF::Query::Node::Blank constructor now optionally produces identifiers." ; asc:update "Merged tSPARQL parsing code into new SPARQLP parser." ; asc:update "Triple class now serializes rdf:type shortcut 'a' appropriately." ; asc:update "Removed 'VAR' type from ::Node::Variable object structure." ; asc:update "Updated code to always expect a HASH reference from ::Bindings iterator." ; asc:update "Removed old fixup code, replaced by ::Algebra fixup methods." ; asc:update "Moved FROM clause data loading to its own method." ; asc:update "Started removing old code (RDF::Base, direct DBI use, AUTOLOAD, profiling)." ; asc:update "Moved general count_statements() method into bridge superclass." ; asc:update "Fixed SQL compiler to work with ::Algebra and ::Node classes." ; asc:update "Updated document namespace declaration for SPARQL XML Results." ; asc:update "Moved Expression classes out of the Algebra hierarchy and into their own space (RDF::Query::Expression)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.501" ; doap:created "2007-11-15" ; ]; asc:changes [ asc:addition "Added as_sparql methods to Algebra and Node classes to serialize patterns in SPARQL syntax." ; asc:addition "Added deparse script." ; asc:update "Fixed CONSTRUCT+OPTIONAL bug." ; asc:update "Fixed jena:sha1sum tests when Digest::SHA1 isn't available." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.500" ; doap:created "2007-11-13" ; ]; asc:changes [ asc:addition "Added new RDF::Query::Algebra:: classes that are used to represent query ASTs." ; asc:addition "Added new RDF::Query::Node:: classes that are used to represent RDF Nodes and Variables." ; asc:addition "Added RDF::Query::query_more_triple() and RDF::Query::query_more_bgp() for triple and bgp matching." ; asc:addition "Added sgrep, smap, swatch and concat methods to RDF::Query::Stream class." ; asc:addition "Added join_streams() to perform netsted-loop natural joins on two Stream objects." ; asc:addition "Added call_function() method to abstract the generation of ['FUNCTION',...] blocks." ; asc:addition "Added rudimentary profiling code (Devel::DProf seems to crash on RDF::Query)" ; asc:addition "Added initial code for supporting property functions (using time:inside as an example)." ; asc:update "URIs are now properly qualified with a BASE when present." ; asc:update "Base URI passed to constructor is now used if no BASE clause exists in the query." ; asc:update "Fixed BASE qualification when an IRI contains Unicode characters (Emulating IRI support with the URI module)." ; asc:update "NAMED graph data is now seperated from the default model into new (temporary) models." ; asc:update "NAMED graphs now work with RDF::Core." ; asc:update "Major refactoring to RDF::Query::query_more() to enhance extensibility." ; asc:update "Improved support of GGP pattern matching." ; asc:update "Refactored query_more() variants and sort_rows() to use new stream methods sgrep, smap, and concat." ; asc:update "Continued to fix bugs to more closely align with DAWG tests." ; asc:update "Updated DAWG tests to run with the RDF::Core backend." ; asc:update "Any DAWG tests with mf:requires are now automatically marked TODO." ; asc:update "DAWG tests graph equality is now punted to user verification." ; asc:update "Fixed bNode merging code in DAWG tests." ; asc:update "query_more() variants and sort_rows() now all return RDF::Query::Stream objects." ; asc:update "query_more() (and everything it calls) now expects bridge object as a method argument (to ensure NAMED graph querying works)." ; asc:update "Parsers now generate ASTs using the Algebra and Node classes." ; asc:update "FILTER operator != is now negative for unknown datatypes." ; asc:update "Fixed exception handling in check_constraints()." ; asc:update "Fixed type-promotion in arithmetic operations and added recognized xsd numeric types." ; asc:update "API Chnage: extension functions now take a bridge object as their second argument (after the query object)." ; asc:update "Fixed equals() method in RDF::Core bridge to properly use the RDF::Core API." ; asc:update "Javascript function makeTerm now accepts language and datatype parameters." ; asc:update "toString() javascript funtion now returns just the literal value (not including language or datatype)." ; asc:update "sop:str now will stringify blank nodes properly." ; asc:update "sparql:langmatches now properly differentiates between no language tag and an empty language tag." ; asc:update "Fixed bugs in SPARQL tokenizer for some Unicode strings (with combining accents)." ; asc:update "RDF::Query::Parser::new_literal() now canonicalizes language tags (to lowercase)." ; asc:update "Fixed GGP verification in RDF::Query::Parser::SPARQL::fixup()." ; asc:update "Merged GGPAtom changes from tSPARQL to SPARQL grammar." ; asc:update "Fixed bug in RDF::Query::Model::RDFCore::equals() when comparing two blank nodes." ; asc:update "Changed RDF::Query::Model::RDFCore::as_string to return strings where node type is identifiable (like Redland, URIs as [...], literal \"...\", bnodes (...))." ; asc:update "Model methods literal_value, literal_datatype and literal_value_langauge now return undef if argument isn't a literal." ; asc:update "Model methods uri_value and blank_identifier now return undef unless argument is of correct type." ; asc:update "Model add_string methods now normalize Unicode input." ; asc:update "Blank node prefix is now scoped to bridge, not lexically in RDF::Query::Model::RDFCore." ; asc:update "RDF::Query::Model::Redland::new_literal now forces argument to a string (XS code breaks on non-PVOK scalars)." ; asc:update "RDF::Query::Model::Redland::add_uri now uses LWP instead of relying on Redland to fetch content." ; asc:update "Redland model class now recognizes DateTime objects as literals." ; asc:update "Updated add_uri() method in RDF::Core bridge to support named graphs (only one name per model for now)." ; asc:update "RDF::Query::Stream constructor now accepts an ARRAY reference." ; asc:update "Stopped using the peephole optimizers. Will replace with an algebra-based optimizer in the future." ; asc:update "Removed code for MULTI patterns. Will replace with algebra-based optimization in the future." ; asc:update "Removed multi_get code." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.044" ; doap:created "2007-09-13" ; ]; asc:changes [ asc:addition "DAWG tests now emit EARL reports." ; asc:addition "Added test harness and temporal data importing scripts to new bin/ directory." ; asc:addition "Added support for temporal queries in RDF::Query and optimizers." ; asc:addition "Added support for CONSTRUCT queries in new YAPP-based SPARQL parsers." ; asc:addition "Added TIMED graph tests for new SPARQL temporal extensions." ; asc:addition "Added tests to SPARQL parser for bugs that were discovered with temporal extensions." ; asc:addition "Added POD to new SPARQL parsers." ; asc:addition "Added new Parse::Yapp-based SPARQL parser in preparation for temporal SPARQL extensions." ; asc:addition "Added stub functions for temporal extension support." ; asc:addition "Added model_as_stream() method to Redland bridge class." ; asc:addition "Addded debug() method to RDF::Query::Model." ; asc:addition "Added UNION tests." ; asc:addition "Added a javascript debugging flag to RDF::Query." ; asc:addition "Added RDF::Query::groupgraphpattern() for handling GGPs." ; asc:addition "Added xsd:integer casting function." ; asc:addition "Added as a FILTER tee that warns to STDERR." ; asc:addition "Added Eyapp SPARQL grammer file and replaced parser with auto-generated code from eyapp." ; asc:update "Marked some SPARQL parser tests as TODO (due to incompatability with new YAPP-based parser)." ; asc:update "Network tests are now run against all available models." ; asc:update "Stream tests are now run against all available models." ; asc:update "Query parser initialization code cleaned up (and now supports beta tSPARQL parser)." ; asc:update "net_filter_function() now properly accesses nodes with bridge methods." ; asc:update "RDF::Query::Parser::new_blank now assigns default names if none is supplied." ; asc:update "Updated error messages in SPARQL parser to align with new YAPP-based parser." ; asc:update "SPARQL parse_predicate_object() fixed to support tripleNodes." ; asc:update "SPARQL parse_object() and parse_predicate() fixed for whitespace insensitivity." ; asc:update "Moved old hand-written SPARQL parser to now unused SPARQL-RD.pm." ; asc:update "Moved new YAPP-based SPARQL parser to SPARQL.pm." ; asc:update "Copied YAPP-based SPARQL parser for new temporal extension tSPARQL.pm." ; asc:update "Moved existing tests for hand-written SPARQL parser to 01-sparql_parser.t.rd-old." ; asc:update "Moved new YAPP-based SPARQL tests to t/01-sparql_parser.t." ; asc:update "Removed bad-syntax queries from optimizer and SQL compiler tests." ; asc:update "Fixed bad-syntax queries in stream and hook tests." ; asc:update "Made output of coverage tests easier to read by adding test names." ; asc:update "Fixed SPARQL parsers to allow keywords as variable names." ; asc:update "Fixed SPARQL parsers to allow dashes and underscores in variable names." ; asc:update "Fixed bad parser constructor call in SQL compiler tests." ; asc:update "Suppressed RDF::Base loading (again) during RDF::Query startup." ; asc:update "RDF::Query::Model::Redland::debug now shows contexts during iterator debugging." ; asc:update "Moved the old (hand written) SPARQL parser out of the way to make test harnesses happy." ; asc:update "SPARQL parser now respects spec regarding the reuse of blank node labels across BGPs." ; asc:update "SPARQL parser now does the right thing with NIL (the empty list)." ; asc:update "SPARQL parser now handles embedded GGPs correctly." ; asc:update "Updated SPARQL parser and tests to pass (almost all) DAWG tests." ; asc:update "Fixed bug where results couldn't be sorted by a non-selected variable." ; asc:update "Removed the unused RDF::Query::timed_graph() method." ; asc:update "RDF::Query::qualify_uri is now more liberal in what it accepts." ; asc:update "Fixed xsd:boolean casting of integer values." ; asc:update "RDF::Query::Parser::new_variable can now generate private variable names." ; asc:update "Fixed test suite to work properly on systems without Redland." ; asc:update "Disabled loading of tSPARQL parser for release." ; asc:update "Removed function prototype definitions in Model base class (was causing loading problems when models are missing)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.043" ; doap:created "2007-06-14" ; ]; asc:changes [ asc:update "Fixed broken MANIFEST causing MakeMaker code to break." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.042" ; doap:created "2007-06-13" ; ]; asc:changes [ asc:addition "Added support for Javascript URL-based extension functions." ; asc:addition "Added GPG signing support for Javascript functions." ; asc:addition "Added meta methods to model classes." ; asc:addition "Added count_statements methods to model classes." ; asc:addition "Added default format for stringifying query result streams." ; asc:addition "Added SPARQL syntax coverage tests." ; asc:addition "Added stream tests." ; asc:update "ISIRI() now returns a sop:isIRI function IRI." ; asc:update "Redland bridge add_uri strips off URI fragment from parser base URI." ; asc:update "Underlying models aren't loaded if turned off by environment variables." ; asc:update "Stopped using the (currently broken) multi-get code." ; asc:update "Model classes aren't used if turned off by environment variable." ; asc:update "Standardized node testing to is_node (replacing older isa_node)." ; asc:update "GPG verification failure now throws exceptions." ; asc:update "GPG fingerprint handling is now whitespace insensitive." ; asc:update "Removed (currently) unused multiget tests." ; asc:update "Redland bridge uri_value returns undef if not called with a node." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.041" ; doap:created "2006-11-26" ; ]; asc:changes [ asc:addition "Added tests for model bridge classes." ; asc:addition "Added partial support for new RDF::Storage::DBI storage class." ; asc:addition "Added tests for backend bridge classes (RDF::Query::Model::*)." ; asc:addition "Added checks for which backends support XML serialization." ; asc:addition "Added RDF::Base support." ; asc:addition "Added SimpleQueryPatternError exception class for future work on multi-get optimizations." ; asc:addition "Removed unwanted '+' signs on stringified bigints when running under perl 5.6.x." ; asc:update "Fixed unicode errors when running under perl 5.6.x." ; asc:update "Skips tests that rely on stable sorting when running under perl 5.6.x." ; asc:update "Fixed bugs in SQL bridge class." ; asc:update "RDFCore and Redland bridge classes now throw error when passed a bad model object." ; asc:update "Bridge class support() method now responds to feature requests for 'temp_model'." ; asc:update "Fixed bug in RDF::Query::Model::RDFCore::isa_resource returning true for blank nodes." ; asc:update "Fixed code in RDF::Query::Model::::SQL::equals()." ; asc:update "Added support for RDF::Query:::Model::SQL models in RDF::Query bridge code." ; asc:update "Removed RDF::Query::Model::SQL::* code that's now in RDF::Storage::DBI." ; asc:update "Fixed naive peephole tests in cases where model supports cost-based analysis." ; asc:update "Fixed bugs in tests that were relying on C to act like C." ; asc:update "Fixed bug in C that prevented running queries against multiple models." ; asc:update "Removed forced dependence on Digest::SHA1." ; asc:update "Makefile.PL now recommends Digest::SHA1 (for jena function) and Geo::Distance (for 07-filters.t)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.040" ; doap:created "2006-07-21" ; ]; asc:changes [ asc:addition "Added support for BOUND() fiters in SQL compilation." ; asc:addition "Added peephole optimizer code with naive and cost-analysis strategies." ; asc:addition "Added add_string method to RDF::Query::Model::Redland." ; asc:addition "Added node_count method to RDF::Query::Model::Redland (only used for testing at this point)." ; asc:update "RDF::Query::execute() now uses the peephole optimizer." ; asc:update "SQL compiler now produces valid SQL when query variable names are SQL reserved keywords." ; asc:update "Moved SPARQL parser test data into YAML." ; asc:update "Added YAML as a build-time prerequisite." ; asc:update "Fixed SPARQL parsing bug for blank nodes as statement objects." ; asc:update "Removed punting code in RDF::Query::execute that tried to do JIT optimization." ; asc:update "Removed calls to getLabel() on model objects in test files." ; asc:update "Fixed dawg tests (was dying because of multiple plans being printed)." ; asc:update "Fixed cost-based peephole optimizer tests (by forcing Redland to do node counting)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.039" ; doap:created "2006-07-14" ; ]; asc:changes [ asc:update "Removed dawg tests from distribution. Only used as developer tests now." ; asc:update "Updated package to use Module::Install instead of ExtUtils::MakeMaker." ; asc:update "Fixed a spurious uninitialized warning in RDF::Query::get_bridge." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.038" ; doap:created "2006-07-09" ; ]; asc:changes [ asc:update "Fixed DBI case-sensitivity for PostgreSQL support." ; asc:update "Cleaned up SQL syntax for easier debugging." ; asc:update "Removed extra parens in SQL that were causing postgresql to break." ; asc:update "Reference test foaf file using File::Spec->catfile() instead of directly." ; asc:update "Fixed SPARQL parsing bug for anonymous nodes in FILTER expressions." ; asc:update "Fixed major SQL compilation bugs for testing equality in FILTER expressions." ; asc:update "Fixed bug in hashing code for blank nodes in SQL compiler." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.037" ; doap:created "2006-07-06" ; ]; asc:changes [ asc:addition "execute() method now takes optional 'bind' parameter for pre-binding query variables." ; asc:update "Updated code to support basic FILTERs in SQL compilation." ; asc:update "Fixed bug in SQL compilation where no WHERE clause was needed." ; asc:update "Fixed bug in SQL compilation when using model-specific Statements tables." ; asc:update "Added Storable to the list of required modules (was missing in the list)." ; asc:update "Fixed typos in metadata about past versions in DOAP Changes.ttl." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.036" ; doap:created "2006-06-26" ; ]; asc:changes [ asc:addition "Initial support for OPTIONALs in SQL compiler." ; asc:addition "Added support for class variable to hold parsing errors. (Beware under threads.)" ; asc:addition "Added model API methods to SQL model class." ; asc:addition "Added C method to RDF::Query::Stream." ; asc:update "Fixed memory leak in RDF::Query::Stream that resulted in too many database handles." ; asc:update "Removed LWP requirement for systems without libwww." ; asc:update "RDF::Query now sets error variable upon parsing error. (Access with C.)" ; asc:update "Fixed POD errors, and added tests for POD coverage." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.035" ; doap:created "2006-06-04" ; ]; asc:changes [ asc:addition "Added DAWG tests and harness." ; asc:addition "Added custom functions: jena:sha1sum, jena:now, jena:langeq, jena:listMember, ldodds:Distance" ; asc:addition "Added new model methods: equals, subject, predicate, object." ; asc:update "Rewrote core logic in OPTIONAL handling code." ; asc:update "Comparisons on literals now respect numeric datatypes." ; asc:update "Fixed outdated calling conventions in casting functions." ; asc:update "Relocated external http-based test data to .Mac URLs." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.034" ; doap:created "2006-05-01" ; ]; asc:changes [ asc:addition "Added JSON serialization for bindings and boolean queries." ; asc:addition "Initial support for compiling RDF queries to SQL queries using the Redland schema." ; asc:addition "Added to_string method to query results Stream class." ; asc:update "Model objects now store the query parse tree for access to data needed in serialization." ; asc:update "Unquoted number and boolean literals in SPARQL are now datatyped appropriately." ; asc:update "Fixed crashing bug when RDF::Query::Model::Redland::as_string was called with an undefined value." ; asc:update "Fixed bug parsing queries with predicate starting with 'a' (confused with { ?subj a ?type})." ; asc:update "Fixed bug parsing queries whose triple pattern ended with the optional dot." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.033" ; doap:created "2006-03-08" ; ]; asc:changes [ asc:update "Updated test suite to work if one of the model classes is missing." ; asc:update "Data-typed literals are now cast appropriately when used in a FILTER." ; asc:addition "Added support for xsd:dateTime datatypes using the DateTime module." ; asc:addition "Added support for LANG(), LANGMATCHES() and DATATYPE() built-in functions." ; asc:update "Updated TODO list." ; asc:addition "Added more exception types to RDF::Query::Error." ; asc:addition "Added POD coverage." ; asc:update "Fixed SPARQL parsing bug for logical operators <= and >=." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.032" ; doap:created "2006-03-03" ; ]; asc:changes [ asc:update "Replaced the Parse::RecDescent SPARQL parser with a much faster hand-written one." ; asc:update "Updated SPARQL parsing rules to be better about URI and QName character sets." ; asc:update "FILTER equality operator now '=', not '==' (to match SPARQL spec)." ; asc:update "Initial support for FILTER constraints as part of the triple pattern structure (Will allow for nested FILTERs)." ; asc:addition "Implemented support for ordering query results by an expression." ; asc:update "Fixed bug in expresion handling of unary minus." ; asc:update "Fixed bug in Redland NAMED GRAPH parsing." ; asc:update "Fixed bug in RDF::Core parsing code where blank nodes would be accidentally smushed." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.031" ; doap:created "2006-02-08" ; ]; asc:changes [ asc:addition "Added support for NAMED graphs." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.030" ; doap:created "2006-01-13" ; ]; asc:changes [ asc:addition "Added support for SELECT * in SPARQL queries." ; asc:addition "Added support for default namespaces in SPARQL queries." ; asc:addition "Added tests for querying RDF collections in SPARQL (1 ?x 3)" ; asc:addition "Added tests for triple patterns of the form { ?a ?a ?b . }" ; asc:addition "Added tests for default namespaces in SPARQL." ; asc:addition "Added tests for SELECT * SPARQL queries." ; asc:update "Bugfix where one of two identical triple variables would be ignored ({ ?a ?a ?b })." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.028" ; doap:created "2005-11-18" ; ]; asc:changes [ asc:addition "Added SPARQL functions: BOUND, isURI, isBLANK, isLITERAL." ; asc:addition "Added SPARQL RDF Collections syntactic forms." ; asc:addition "Added binding_value_by_name method to Query results stream class." ; asc:addition "Added isa_blank methods to RDF::Redland and RDF::Core model classes." ; asc:addition "Added directives to SPARQL grammar." ; asc:addition "Updated SPARQL REGEX syntax." ; asc:update "Updated SPARQL FILTER syntax." ; asc:update "Fixed FILTER support in OPTIONAL queries." ; asc:update "Fixed RDF literal datatyping when using Redland versions >= 1.00_02." ; asc:update "Updated SPARQL grammar to make 'WHERE' token optional." ; asc:update "Updated SPARQL 'ORDER BY' syntax to use parenthesis." ; asc:update "Fixed SPARQL FILTER logical-and support for more than two operands." ; asc:update "Fixed SPARQL FILTER equality operator syntax to use '=' instead of '=='." ; asc:update "Now requires Test::More 0.52 because of changes to is_deeply()." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.027" ; doap:created "2005-07-28" ; ]; asc:changes [ asc:addition "Added binding_value_by_name() method to query result streams." ; asc:update "ORDER BY arguments now use parenthesis (SPARQL Draft 2005.07.21)." ; asc:update "SPARQL parser now supports ORDER BY operands: variable, expression, or function call (SPARQL Draft 2005.07.21)." ; ] . dcterms:isVersionOf ; dcterms:replaces ; doap:Version [ doap:revision "1.026" ; doap:created "2005-06-05" ; ]; asc:changes [ asc:addition "Added new DBI model bridge (accesses Redland's mysql storage directly)." ; asc:addition "Added built-in SPARQL functions and operators (not connected to grammar yet)." ; asc:addition "Added bridge methods for accessing typed literal information." ; ] . a doap:Project ; doap:download-page ; doap:download-mirror . RDF-Query-2.910/data/000755 000765 000024 00000000000 12173312223 014237 5ustar00gregstaff000000 000000 RDF-Query-2.910/doap.rdf000644 000765 000024 00000004526 12054225575 014770 0ustar00gregstaff000000 000000 Gregory Todd Williams f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8 RDF::Query RDF::Query RDF Query Engine. 2004-07-05 An RDF query implementation of SPARQL/RDQL in Perl for use with RDF::Trine. Gregory Todd Williams f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8 stable 2012-01-31 2.908 Perl RDF-Query-2.910/examples/000755 000765 000024 00000000000 12173312223 015144 5ustar00gregstaff000000 000000 RDF-Query-2.910/inc/000755 000765 000024 00000000000 12173312223 014077 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/000755 000765 000024 00000000000 12173312223 014074 5ustar00gregstaff000000 000000 RDF-Query-2.910/Makefile.PL000644 000765 000024 00000002427 12137221214 015304 0ustar00gregstaff000000 000000 use strict; use warnings; use inc::Module::Install; name 'RDF-Query'; all_from 'lib/RDF/Query.pm'; author 'Gregory Todd Williams '; license 'perl'; recommends 'LWP::Simple' => 0; recommends 'Getopt::Simple' => 0; recommends 'Geo::Distance' => 0.09; recommends 'Crypt::GPG' => 0; recommends 'Bloom::Filter' => 1; build_requires 'Test::JSON' => 0; build_requires 'Test::More' => 0.88; build_requires 'Test::Exception' => 0; build_requires 'YAML' => 0; build_requires 'FindBin' => 0; requires 'Module::Pluggable' => 0; requires 'Data::UUID' => 0; requires 'Digest::SHA' => 0; requires 'DateTime::Format::W3CDTF' => 0; requires 'Error' => 0; requires 'File::Spec' => 0; requires 'File::Temp' => 0; requires 'JSON' => 2; requires 'Parse::RecDescent' => 0; requires 'RDF::Trine' => 1.003; requires 'Scalar::Util' => 0; requires 'Set::Scalar' => 0; requires 'URI' => 1.52; requires 'I18N::LangTags' => 0; resources( 'homepage' => "http://search.cpan.org/dist/RDF-Query/", 'repository' => "http://github.com/kasei/perlrdf/", 'bugtracker' => "https://github.com/kasei/perlrdf/issues" ); author_tests('xt'); install_script 'bin/rqsh'; sign; WriteAll; RDF-Query-2.910/MANIFEST000644 000765 000024 00000012644 12171750060 014471 0ustar00gregstaff000000 000000 bin/dawg11-status.pl bin/deparse.pl bin/failing_earl_tests.sh bin/fedoptimize.pl bin/fedquery.pl bin/graph-bgp.pl bin/graph-qeps.pl bin/graph-query.pl bin/hexastore_query.pl bin/json.pl bin/open-test-files.pl bin/parse_profile.pl bin/parse.pl bin/passing_earl_tests.sh bin/query.pl bin/rqsh bin/rqsh-server.pl Changes.ttl data/about.xrdf data/bnode-person.rdf data/federation_data/alice_bob.rdf data/federation_data/alice.rdf data/federation_data/bob.rdf data/Flower-2.rdf data/foaf.xrdf data/greenwich.rdf data/named_graphs/alice.rdf data/named_graphs/alice.ttl data/named_graphs/bob.rdf data/named_graphs/bob.ttl data/named_graphs/meta.rdf data/named_graphs/meta.ttl data/named_graphs/repeats1.rdf data/named_graphs/repeats2.rdf data/rdfa-test.xhtml data/service-kasei.ttl data/service.ttl data/t-sparql11-aggregates-1.rdf data/temporal.rdf doap.rdf examples/create_query_api.pl examples/livejournal.pl examples/queries/sparql-ask-people.rq examples/queries/sparql-bgp-people-knows.rq examples/queries/sparql-bgp-people.rq examples/queries/sparql-ggp-person-opt-knows.rq examples/queries/sparqlp-service-people-names.rq examples/query.pl examples/query_url.pl examples/service_descriptions/dbpedia_org.ttl examples/service_descriptions/kasei_us.ttl inc/Module/Install.pm inc/Module/Install/AuthorTests.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Scripts.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm lib/RDF/Query.pm lib/RDF/Query/Algebra.pm lib/RDF/Query/Algebra/Aggregate.pm lib/RDF/Query/Algebra/BasicGraphPattern.pm lib/RDF/Query/Algebra/Clear.pm lib/RDF/Query/Algebra/Construct.pm lib/RDF/Query/Algebra/Copy.pm lib/RDF/Query/Algebra/Create.pm lib/RDF/Query/Algebra/Distinct.pm lib/RDF/Query/Algebra/Extend.pm lib/RDF/Query/Algebra/Filter.pm lib/RDF/Query/Algebra/GroupGraphPattern.pm lib/RDF/Query/Algebra/Limit.pm lib/RDF/Query/Algebra/Load.pm lib/RDF/Query/Algebra/Minus.pm lib/RDF/Query/Algebra/Move.pm lib/RDF/Query/Algebra/NamedGraph.pm lib/RDF/Query/Algebra/Offset.pm lib/RDF/Query/Algebra/Optional.pm lib/RDF/Query/Algebra/Path.pm lib/RDF/Query/Algebra/Project.pm lib/RDF/Query/Algebra/Quad.pm lib/RDF/Query/Algebra/Sequence.pm lib/RDF/Query/Algebra/Service.pm lib/RDF/Query/Algebra/Sort.pm lib/RDF/Query/Algebra/SubSelect.pm lib/RDF/Query/Algebra/Table.pm lib/RDF/Query/Algebra/TimeGraph.pm lib/RDF/Query/Algebra/Triple.pm lib/RDF/Query/Algebra/Union.pm lib/RDF/Query/Algebra/Update.pm lib/RDF/Query/BGPOptimizer.pm lib/RDF/Query/Compiler/SQL.pm lib/RDF/Query/Error.pm lib/RDF/Query/ExecutionContext.pm lib/RDF/Query/Expression.pm lib/RDF/Query/Expression/Alias.pm lib/RDF/Query/Expression/Binary.pm lib/RDF/Query/Expression/Function.pm lib/RDF/Query/Expression/Nary.pm lib/RDF/Query/Expression/Unary.pm lib/RDF/Query/Federate.pm lib/RDF/Query/Federate/Plan.pm lib/RDF/Query/Functions.pm lib/RDF/Query/Functions/Geo.pm lib/RDF/Query/Functions/Jena.pm lib/RDF/Query/Functions/Kasei.pm lib/RDF/Query/Functions/SPARQL.pm lib/RDF/Query/Functions/Xpath.pm lib/RDF/Query/Node.pm lib/RDF/Query/Node/Blank.pm lib/RDF/Query/Node/Literal.pm lib/RDF/Query/Node/Resource.pm lib/RDF/Query/Node/Variable.pm lib/RDF/Query/Parser.pm lib/RDF/Query/Parser/RDQL.pm lib/RDF/Query/Parser/SPARQL.pm lib/RDF/Query/Parser/SPARQL11.pm lib/RDF/Query/Plan.pm lib/RDF/Query/Plan/Aggregate.pm lib/RDF/Query/Plan/BasicGraphPattern.pm lib/RDF/Query/Plan/Clear.pm lib/RDF/Query/Plan/ComputedStatement.pm lib/RDF/Query/Plan/Constant.pm lib/RDF/Query/Plan/Construct.pm lib/RDF/Query/Plan/Copy.pm lib/RDF/Query/Plan/Distinct.pm lib/RDF/Query/Plan/Extend.pm lib/RDF/Query/Plan/Filter.pm lib/RDF/Query/Plan/Iterator.pm lib/RDF/Query/Plan/Join.pm lib/RDF/Query/Plan/Join/NestedLoop.pm lib/RDF/Query/Plan/Join/PushDownNestedLoop.pm lib/RDF/Query/Plan/Limit.pm lib/RDF/Query/Plan/Load.pm lib/RDF/Query/Plan/Minus.pm lib/RDF/Query/Plan/Move.pm lib/RDF/Query/Plan/NamedGraph.pm lib/RDF/Query/Plan/Offset.pm lib/RDF/Query/Plan/Path.pm lib/RDF/Query/Plan/Project.pm lib/RDF/Query/Plan/Quad.pm lib/RDF/Query/Plan/Sequence.pm lib/RDF/Query/Plan/Service.pm lib/RDF/Query/Plan/Sort.pm lib/RDF/Query/Plan/SubSelect.pm lib/RDF/Query/Plan/ThresholdUnion.pm lib/RDF/Query/Plan/Triple.pm lib/RDF/Query/Plan/Union.pm lib/RDF/Query/Plan/Update.pm lib/RDF/Query/ServiceDescription.pm lib/RDF/Query/Temporal.pm lib/RDF/Query/Util.pm lib/RDF/Query/VariableBindings.pm Makefile.PL MANIFEST This list of files META.yml README README.html SIGNATURE t/00-simple.t t/01-coverage.t t/algebra-bgp.t t/algebra-expr.t t/algebra-func.t t/algebra-subsumption.t t/algebra.t t/constraints.t t/dataset-from-file.t t/dataset-from-net.t t/dev-computed-statements.t t/distance.js t/ext-select-expr.t t/filters.t t/functions.rdf t/functions.t t/hooks.t t/iterators.t t/literals.t t/models.pl t/named-graphs.t t/optional.t t/plan.t t/protocol-serialization.t t/queryform-ask.t t/queryform-construct.t t/queryform-describe.t t/rdql.t t/resultforms.t t/serialize.t t/sha1.js t/sha1.js.asc t/sparql11-aggregates.t t/sparql11-federation.t t/sparql11-negation.t t/sparql11-propery_paths.t t/sparql11-select_expressions.t t/sparql11-subselect.t t/sparql11-update.t t/streams.t.deprecated t/syntactic_forms.t t/union.t xt/dawg-eval10.t xt/dawg-eval11.t xt/dawg-syntax11.t xt/dev-service-description.t xt/dev-sql-compiler.t xt/dev-time-intervals.t xt/federate.t xt/parser-rdql.t xt/parser-sparql.t xt/parser-sparql11.t xt/pod.t xt/pod_coverage.t xt/sparql11-federation.t RDF-Query-2.910/META.yml000644 000765 000024 00000002315 12173312215 014601 0ustar00gregstaff000000 000000 --- abstract: 'A complete SPARQL 1.1 Query and Update implementation for use with RDF::Trine.' author: - 'Gregory Todd Williams ' - 'Gregory Todd Williams ' build_requires: ExtUtils::MakeMaker: 6.36 FindBin: 0 Test::Exception: 0 Test::JSON: 0 Test::More: 0.88 YAML: 0 configure_requires: ExtUtils::MakeMaker: 6.36 distribution_type: module dynamic_config: 1 generated_by: 'Module::Install version 1.06' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: RDF-Query no_index: directory: - examples - inc - t - xt recommends: Bloom::Filter: 1 Crypt::GPG: 0 Geo::Distance: 0.09 Getopt::Simple: 0 LWP::Simple: 0 requires: Data::UUID: 0 DateTime::Format::W3CDTF: 0 Digest::SHA: 0 Error: 0 File::Spec: 0 File::Temp: 0 I18N::LangTags: 0 JSON: 2 Module::Pluggable: 0 Parse::RecDescent: 0 RDF::Trine: 1.003 Scalar::Util: 0 Set::Scalar: 0 URI: 1.52 resources: bugtracker: https://github.com/kasei/perlrdf/issues homepage: http://search.cpan.org/dist/RDF-Query/ license: http://dev.perl.org/licenses/ repository: http://github.com/kasei/perlrdf/ version: 2.910 RDF-Query-2.910/README000644 000765 000024 00000240612 12173312056 014217 0ustar00gregstaff000000 000000 RDF::Query - A SPARQL 1.1 Query implementation for use with RDF::Trine. RDF::Query allows SPARQL 1.1 and RDQL queries to be run against an RDF model, returning rows of matching results. REQUIREMENTS To install RDF::Query you'll need the following perl modules installed: * DateTime * DateTime::Format::W3CDTF * Digest::SHA * Error * I18N::LangTags * JSON * List::Util * LWP * Parse::RecDescent * RDF::Trine * Scalar::Util * Set::Scalar * Storable * URI The following additional modules are recommended for some functionality: * Geo::Distance INSTALLATION To install, run: perl Makefile.PL make make test make install VERSION HISTORY Version 2.910 (2012-07-22) * BUG FIXES Updated RDQL parser to work with recent RDF::Trine releases. Version 2.909 (2012-11-24) * BUG FIXES Fixed bad sparql serialization of filters with equality tests; was using '==' instead of '=' (github issue 53). Fixed bug in RDF::Query::Algebra::Service->referenced_variables. Fixed bug that wasn't passing the active graph to EXISTS filter evaluation. Fixed RDF::Query prepare and execute methods to properly localize the model object. Fixed bug in RDQL parser that mistakenly required a USING clause (github issue 70). Fixed handling of aggregates over empty groups. * NEW FEATURES Added support for VALUES graph pattern (in-place BINDINGS). Added support for UUID() and STRUUID() functions. Added is_update() and specifies_update_dataset() methods to RDF::Query. Accept common typo of SEPARATOR in SPARQL 1.1 parser ("SEPERATOR") with constructor arg 'allow_typos' in RDF::Query::Parser::SPARQL11. * ENHANCEMENTS Fixed SPARQL 1.1 parsing to enforce not using shared bnode labels between update data operations. Improved SPARQL 1.1 parser detection of invalid use of BIND() (when binding already in-scope variables). Fixed bug in SPARQL 1.1 parser to recognize legal Update operations. Updated SPARQL 1.1 parser to allow colon in local part of a prefixname. Updated STRBEFORE() and STRAFTER() implementations to track SPARQL 1.1 standard. Updated property path implementation to track W3C standard (changed counting semantics and dropped {m,n} form). Added support to passthrough query eval to the model if supported and the 'allow_passthrough' option is set on the query object. Added 'canonicalize' option to RDF::Query constructor to canonicalize literal values. * OTHER Updated handling of BIND() in the SPARQL 1.1 parser to match the latest spec semantics. Added ability to run tests of type mf:CSVResultFormatTest. Fixed config handling in rqsh to allow the use of hexastore backends. Merged Log4perl initialization cleanup patches (from github/kba). Use $plan->explain instead of $plan->sse for "explain" rqsh command. Updated EARL IRIs in bin/failing_earl_tests.sh and bin/passing_earl_tests.sh. Removed RDF::Redland recommendation in Makefile.PL. Added doap:implements statements, and updated release data to doap.rdf. Updated RDF::Query::Util::cli_parse_args to allow no-argument setup. Updated xt/dawg/earl.pl to use new EARL IRIs earl:passed and earl:failed. Added POD to bin/rqsh. Updated DAWG test harnesses to support expected query bindings results in RDF/XML format. Fixed xt/dawg-eval11.t to emit TAP failures when query parsing fails. Require RDF::Endpoint 0.05 in xt/dawg-eval11.t. Removed values from test directory list in xt/dawg-eval11.t. Added values to list of test directories in xt/dawg-eval11.t. Added exists test directory to xt/dawg-eval11.t. Added test case confirming bad sparql serialization of equality testing filters (github issue 53). Removed tests for {m,n} property path forms. Fixed RDF::Query::Algebra::Project to throw exception on bad constructor arguments. Added bugtracker info to Makefile.PL. Added POD marking modules as unstable: RDF::Query::BGPOptimizer, RDF::Query::Compiler::SQL, RDF::Query::Federate, RDF::Query::Federate::Plan Improved expected/actual results output when encountering failing tests. Removed old bloom-filter federation code. Removed RDF::Query::ExecutionContext->base method. Fix POD in RDF::Query::Algebra::Table. Version 2.908 (2012-01-31) * BUG FIXES Fixed SPARQL serialization of expressions using && and ||. Fixed SPARQL 1.1 parser to support "GRAPH" without whitespace. Fixed bug resulting in false positive error when projecting expressions with aggregates. Fixed aggregate evaluation to result in unbound variables on error (instead of dropping the result). Fixed numeric divide operation to return xsd:decimal when operands are xsd:integers. Fixed RDF::Query::Expression::Binary->evaluate to properly throw on div-by-zero. Fixed RDF::Query::Expression::Function->evaluate to propogate type errors in IF(). Fixed bin/rqsh to handle queries that use BASE. Fixed bug in RDF::Query::Plan::Join::PushDownNestedLoop that produced invalid results when the RHS was a subselect. Fixed RDF::Query::Algebra::Filter->as_sparql to handle variation of serialization of the child pattern. Fixed bug in SPARQL 1.1 parser that mistakenly introduced aggregate operations in non-aggregate queries. * NEW FEATURES Added support for Service as a binary op (allowing variable-endpoint SERVICE blocks). Added implementations for functions STRBEFORE, STRAFTER, and REPLACE. Added RDF::Query->prepare_with_named_graphs method. Added support for COPY and MOVE operations. Allow percent encoding and backslash escaping in prefix names. * ENHANCEMENTS Fixed RDF::Query::Expression::Binary to canonicalize numeric literal results. Added syntax support for SILENT form of LOAD. Added support for SILENT and variable endpoint handling for SERVICE patterns. Added syntax support for optional GRAPH keyword on SPARQL 1.1 update shortcuts. Updated RDF::Query::Plan::Extend to copy variable bindings instead of using the existing reference. Added RDF::Query::Plan::Extend->explain. Changed explain() syntax of plan quads. Updated plan classes to optionally register intermediate results with a execution delegate object. Made RDF::Query::Plan::Construct uniq the returned triples. Added custom RDF::Query::Plan::Construct->explain method. Normalize language tags used in SPARQL query syntax to lowercase. Modularize RDF::Query::Plan::Service to allow mock testing. Added exception handling in RDF::Query->set_error. Added subplans_of_type method to Plan classes. Fixed use of '__DEFAULT__' sentinel value in RDF::Query::Plan, RDF::Query::Plan::Service, RDF::Query::Node::Resource->as_sparql, and RDF::Query->as_sparql. Force the planner to avoid using a bind-join when the RHS contains a Service subplan (to avoid a DOS attack on the remote endpoint). Updated DATATYPE() to return rdf:langString on language-tagged literals (per RDF 1.1 change). Fixed sse serialization in RDF::Query::Algebra::Service to handle binary op (variable endpoint) form. Croak rather than die in some places, confess and use logdie in one place Allow aggregates in ORDER BY clause. * OTHER Added examples/query_url.pl. Added RDF::Trine::Error::UnimplementedError exception class. Updated required version of RDF::Trine to 0.138. Version 2.907 (2011-06-04) * BUG FIXES Fixed bug in SPARQL 1.1 parser for DESCRIBE queries without a WHERE clause. Fixed join ordering bug for queries with a BINDINGS clause and several joins. Fixed RDF::Query->as_sparql for DESCRIBE queries which project URI terms, not variables. * OTHER Fixed expected test results for DESCRIBE queries without a WHERE clause. examples/query.pl now emits a warning if the query object cannot be constructed. Version 2.906 (2011-05-14) * API CHANGES Globally changed 'base' to 'base_uri' in code and API. * BUG FIXES Fixed SPARQL serialization in RDF::Query::Plan generation of SERVICE blocks to always include braces around the query body. Fixed SPARQL 1.1 parsing bug in property paths using alternatives ('|'). Fixed SPARQL 1.1 bug in parsing prefixnames where a graph term is expected. Fixed bug in ZeroOrMore reverse path handling (patterns matching { var path* term }). Fixed bug in RDF::Query::Node::Literal::_cmp that was causing wrong node comparison results. Fixed bug in parsing solution modifiers in CONSTRUCT and DESCRIBE queries. Fixed bug in handling of unbound variables in delete templates in RDF::Query::Plan::Update. Fixed bug in calls to RDF::Query->var_or_expr_value that was preventing use of EXISTS filters in project expressions. * NEW FEATURES Updated RDF::Query::Plan::Iterator->new to allow passing in a callback that will return an iterator, instead of an iterator directly. Updated RDF::Query->query_plan to delegate entire queries to the RDF::Trine::Model when possible. Updated bin/deparse.pl to allow specifying an endpoint url for SPARQL-backed models (which can affect the query plan). Added support for empty SPARQL 1.1 Update sequence operations (allowing no-op updates). Added options to RDF::Query->new to allow forcing RDF::Query to disable query delegation to the underlying store. Added option to RDF::Query::Util::cli_parse_args to specify arbitrary options to be supplied to the RDF::Query->new constructor. Added new, cleaner line-based "explain" format for serializing query Plan and Algebra objects. Added bindings accessor and is_unit method to RDF::Query::Plan::Constant. Added ability to forcibly remove a particular join algorithm from consideration in plan generation. Added 'execute' command in rqsh to allow loading a query from a file or URL. * ENHANCEMENTS Updated SPARQL 1.1 parser to prevent bnode use in DELETE blocks. Updated rqsh to associate common filename extensions with media types (helps in loading local files). Updated RDF::Query::Plan::Path to align with new ALP algorithm from the spec text. Added some simple statistics generation code in RDF::Query::Plan::Join subclasses. Added missing implementation for property paths using {n,} modifier syntax. Added code to guard against mishandling unbound/blank/variable nodes in DELETE templates in RDF::Query::Plan::Update. * OTHER Updated tests to use new RDF::Trine::Iterator->seen_count method instead of deprecated count method. Updated serialization text for zero-length paths in RDF::Query::Plan::Path->plan_node_data. Removed warning of unknown options passed to RDF::Query->new. Removed unused projection code in RDF::Query->execute_plan. Removed Digest::SHA1 from prereq modules, and updated code to use Digest::SHA instead. Remove the meaningless "All rights reserved" copyright text. Fixed test count in t/sparql11-propery_paths.t. Commented out noisy debugging output in RDF::Query::Plan::Path. Added trace debugging in RDF::Query::Plan::Sort. Added options instance variable to RDF::Query::ExecutionContext. Added Module::Pluggable to list of required modules in Makefile.PL. Added mappings from file extensions to media types in dawg test scripts. Various updates to SPARQL 1.1 dawg test harness scripts. Version 2.905 (2011-02-18) * BUG FIXES Fixed mistaken case sensitivity of COALESCE, BNODE, CONCAT, and SUBSTR keywords in SPARQL 1.1 parser. Fixed parsing ambiguity between MIN aggregate and MINUTES function. Fixed Xpath fn:timezone-from-dateTime function return for UTC timezones. Fixed RDF::Query::Algebra::Project->as_sparql handling of binary select expressions. Fixed RDF::Query::Algebra::Construct->sse serialization to include construct triples. Updated handling of BIND() to not close group scope (which was applying filters in the wrong place). Fixed RDF::Query::Parser::SPARQL11 to handle whitespace between tokens in 'INSERT DATA' and 'DELETE DATA'. Fixed RDF::Query::Parser::SPARQL11 to handle empty ModifyTemplates. Fixed SPARQL 1.1 parser to properly set relevant datasets for update operations. Fixed plan code for zero-length paths to return RDF::Query::Node-based results (not Trine-based). Fixed bug in RDF::Query::Plan::Clear when attempting to clear the default graph. Fixed exception throwing on numeric binary expression eval without numeric literal arguments. Updated RDF::Query::Plan to handle update operations with different datasets for matching (USING clause). * NEW FEATURES Added SPARQL 1.1 numeric functions: ABS, CEIL, FLOOR, ROUND, RAND Added SPARQL 1.1 string functions: CONCAT, SUBSTR, STRLEN, UCASE, LCASE, ENCODE_FOR_URI, CONTAINS, STRSTARTS, STRENDS Added SPARQL 1.1 date functions: NOW, TIMEZONE, TZ, YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS Added SPARQL 1.1 hashing functions: MD5, SHA1, SHA224, SHA256, SHA384, and SHA512 Added RDF::Query::Functions->install_function method, and implementations for fn:compare, fn:concat, and fn:substring. Updated RDF::Query->execute_with_named_graphs to accept optional arguments to be passed to C<< execute >>. Added support for CONSTRUCT WHERE syntax shortcut. Added support for SILENT modifier to SERVICE patterns. Added SILENT flag in RDF::Query::Algebra::Service. Added initial code to create SPIN serializations of queries. Added RDF::Query::Node::Literal->type_list method. * ENHANCEMENTS Updated xt/dawg-syntax11.t to find more syntax queries in the dawg test data. SPARQL 1.1 parser now throws errors for more classes of syntactically invalid queries (repeated expression aliases, wrong number of BINDINGS values, SELECT * with use of grouping). Added support for non-strict comparisons of xsd:strings in RDF::Query::Node::Literal (based on $LAZY_COMPARISONS variable). * OTHER Major refactor of xt/dawg-eval11.t test harness. Updated required version of Test::More to 0.88 (0.86 doesn't have done_testing() used in t/store-config.t). Updated RDF::Query::Expression::Function->evaluate to throw an error if no function coderef is found. Version 2.904 (2010-11-23) * Added SPARQL 1.1 Update shortcut syntax support for COPY, MOVE, and ADD graph operations. * Added 'set prefix' and 'time' commands to rqsh. * Added string concatenation overloading of binary +. * Added xpath function implementations. * Fixed bug in property path evaluation on encountering blank path nodes. * Fixed use of binary '*' in FILTER without numeric data in t/resultforms.t. * Fixed bin/dawg11-status.pl to only load SPARQL 1.1 earl result files. * Fixed RDF::Query::Plan::Update to ignore non-ground triples (unbound variables don't act as wildcards). * Fixed handling of named graph data in insert/delete patterns of update operations. * Updated RDF::Query::Util to use RDF::Trine::Model->temporary_model and guess an appropriate parser based on filenames. * Fixed bug in RDF::Query::Plan::Construct that wasn't giving each result a new set of blank nodes (RT 63155). * Updated SYNOPSIS and execute() and execute_plan() methods POD (RT 63153 comment from Ivan Shmakov). * Updated documentation to explicitly discuss inherited methods due to subclassing. * Fixed RDF::Query::Algebra::Sequence->sse to serialize operations in proper order. * Added $silent argument to RDF::Query::Algebra::Clear constructor. * Updated namespace URI in RDF::Query::Functions::Jena to . * Updated namespace URI in RDF::Query::Functions::Xpath to . * Updated xt/dawg-eval11.t to align with updated test case manifest schemas. * Updated xt/dawg-eval11.t to prevent test cases with missing files from emitting failures (now skipped). Version 2.903 (2010-11-03) * SPARQL 1.1 * New features * Added support for BIND(expr AS ?var) operator. * Added isNumeric built-in function, checking for proper datatype and valid lexical forms for numerics. * Updates * Updated SPARQL 1.1 parser to handle WITH clauses with PrefixedNames. * SPARQL 1.1 parser now sets error and returns if no query string is supplied. * Added more exception handling in RDF::Query::Parser::SPARQL11. * Updated CLEAR and DROP handling in SPARQL 1.1 parser and engine to handle 'NAMED' and 'ALL' variants. * Renamed 'binding_variables' methods to 'potentially_bound' to align with current SPARQL 1.1 terminology. * Results returned by property path plans are now RDF::Query::VariableBindings objects and are properly projected. * The query planner now interprets simple paths as basic graph patterns. * Removed Algebra and Plan classes for Not and Exists operations (now handled in other classes). * Updated RDF::Query::Plan::Extend to drop the extended variable binding if evaluating the extension yields an error. * Updated RDF::Query::Plan::Update to accept delete patterns with variables. * Updated subquery plan objects to use a sub-plan, not a full query object. * Bug fixes * Fixed SPARQL 1.1 parser bug that was returning partial data from a failed parse call. * Fixed SPARQL 1.1 parser bug for queries with multiple FILTERs in a GGP including an EXISTS filter. * Fixed SPARQL 1.1 parser bug for DROP/CLEAR queries. * Fixed SPARQL 1.1 parser typo of 'SEPARATOR' argument to GROUP_CONCAT aggregate. * Fixed SPARQL 1.1 parser to handle FITLERS to the left of MINUS/OPTIONAL and allow projecting '*' with other vars/epressions. * Fixed SPARQL 1.1 parser case sensitivity of project expression 'AS' token. * Fixed SPARQL 1.1 parser handling of of subqueries within GRAPH blocks. * Fixed miscellaneous bugs and improved performance of parsing SPARQL 1.1 queries. * Made parsing of very large triple blocks non-recursive in SPARQL 1.1 parser. * Fixed COALESCE function to handle errors properly. * Fixed RDF::Query::Plan::Aggregate to extend results using AliasExpressions from HAVING clauses. * Fixed bug that was ignoring GROUP BY clauses when no aggregate operations were present. * Fixed bad looping on start node of unbounded property paths. * Fixed bug in query planning of '^' property paths. * Fixed zero-length property path evaluation within GRAPH blocks. * Removed plan duplication of zero-length case in '*' property paths. * Fixed handling of named graphs within subqueries (changes to active graph handling in the query planner). * Fixed type-promotion and handling of numeric types in aggregate handling. * Fixed RDF::Query::Algebra::Update->as_sparql to handle INSERT/DELETE updates properly. * Fixed RDF::Query::Plan::Aggregate->sse to allow handling of (non-blessed, scalar) '*' columns. * Query Engine * Added ability to pass through arguments to the query planner in RDF::Query->prepare. * Updated RDF::Query::Node::Literal::_cmp to handle literals that fail to parse as DateTime objects. * Updated RDF::Query::Plan::Limit to use the limit accessor internally. * RDF::Query::Plan::NamedGraph now uses lazy iterator for graph names. * Updated RDF::Query::VariableBindings to subclass the new RDF::Trine::VariableBindings. * RDF::Query::Algebra::Project now implements bind_variables(). * Updated RDF::Query->describe to use $model->bounded_description instead of simply outgoing arcs. * Fixed bug in evaluation of function expressions in which the execution context object was lost (causing EXISTS filters to fail). * Fixed optimization of COUNT(*) over 1-triple BGPs to keep the variable binding for the pseudo-variable ?COUNT(*). * Fixed sse serialization in RDF::Query::Algebra::Distinct. * Fixed RDF::Query::Plan::Join::PushDownNestedLoop to close sub-plans in close() method. * rqsh - RDF::Query Shell * Added ability to show the parsed algebra with 'parse' command. * Added abiility to show the query execution plan with the 'explain' command. * Added ability to change the RDF serializer with the 'serializer' command. * Added ability to initialize new stores with the 'init' command. * Added ability to change underlying store with the 'use' command. * Added 'help' command. * Updated packaging to install rqsh script (renamed from old bin/rqsh.pl). * Updated to catch errors thrown while trying to create new storage backends (e.g. DBI->connect failing). * rqsh now defaults to read-write models. * rqsh now sets a base URI for the current working directory. * Password input now doesn't echo to the terminal. * Filename input now autocompletes via Term::ReadLine. * Warning and Error Handling * Replaced calls to die with throwing exceptions. * RDF::Query->new now warns about unrecognized options. * DATATYPE() now throws an exception unless passed a valid node object. * RDF::Query::Expresion::Binary now throws exceptions on numeric comparisions with non-numeric operands. * Fixed error message in RDF::Query::Plan::NamedGraph. * Added trace debugging to RDF::Query::Plan::Union. * Added trace debugging to RDF::Query::Plan::Project. * Added trace debugging to RDF::Query::Plan::Path. * Tests and Miscellany * Added SPARQL 1.1 syntax and eval test harnesses. * Fixed bug in xt/dawg-eval11.t parsing of srx files containing literal values that evaluate to false in perl (e.g. '0'). * Fixed turtle output for rdfs:comments in xt/dawg/earl.pl. * Moved developer tests from t/ to xt/. * Refactored RDF::Query::Functions into several auto-loaded sub-packages via Module::Pluggable (patch from tobyink). * Removed references to the now-deprecated mac.com in network-based tests. * Updated expected values in t/queryform-describe.t for results using bounded descriptions. * Updated RDF::Query::Util to look for .prefix-cmd cache of common namespace names. * Updated RDF::Query::Util::cli_parse_args to accept '-r' as a read-only flag. * Updated required version of RDF::Trine to 0.126. * Updated to use URI 1.52 to allow direct usage of IRIs without unicode workarounds. * Made code updates to improve performance based on profiling. Version 2.902 (2010-07-02) * Fixed bug in requiring prerequisite modules. Version 2.901 (2010-06-29) * Added support for SPARQL 1.1 CREATE/DROP GRAPH update operations. * Fixed named graph handling. * Fixed bug in handling of * aggregates. * Simplified code to create new temporary models when FROM clauses dereference graph data. * Fixed infinite loop bug on parsing certain invalid SPARQL 1.1 update queries. * Fixed handling of BGPs within named graphs in RDF::Query::Plan::BasicGraphPattern. * Updated syntax error exception messages in SPARQL parsers. * Fixed property path evaluation within named graphs. * Converted Makefile.PL to properly use Module::Install. Version 2.900 (2010-06-23) * Major Changes * SPARQL 1.1 is now the default query parser. * RDF::Core is no longer supported as a backend triplestore. * Redland is still supported as a backend store, but updated handling of default and named graphs means that existing triples stored in Redland without a context (named graph) will not be accessible from RDF::Query. * SPARQL Language Updates * Added support for SPARQL 1.1 Query features: * Basic Query Federation with BINDINGS and UNDEF support. * Functions: IF, STRDT, STRLANG, IRI, URI, BNODE. * Infix operators IN and NOT IN. * Aggregates COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, and SAMPLE. * Property paths. * Negation with MINUS graph patterns and NOT EXISTS filters. * Added support for SPARQL 1.1 Update features: * INSERT/DELETE DATA, INSERT/DELETE WHERE, LOAD, and CLEAR * Added support for multi-statement updates. * API Updates * Removed all bridge classes (RDF::Query::Model::*). * Removed RDF::Query::Logger class. * Removed net filter function code from RDF::Query. * Added as_hash method to Query, Algebra, and Node classes. * Removed SPARUL and SPARQLP parsers. * RDF::Query no longer loads URIs in FROM and FROM NAMED clauses with SPARQL 1.1 by default. * Added ability for RDF::Query::Plan::Iterator to fire a callback when execute() is called. * Added new RDF::Query::Plan::ComputedStatement class. * Added new RDF::Query::Plan::Iterator class. * Bugfixes * Fixed whitespace unescaping bug in SPARQL (1.0 and 1.1) parsers. * Scripts * Added bin/json.pl to print a JSON-formatted representation of the query algebra. * Added bin/rqsh.pl CLI interface to executing updates and queries. * Added ability to serialize algebra and plan in bin/deparse.pl. * bin/deparse.pl now catches parsing errors and prints stacktrace. * Optimizations * Implemented optimization for COUNT(*) over a single triplepattern. Version 2.202 (2010-05-22) * Added initial SPARQL 1.1 syntax and eval support for select expressions, subqueries, negation, aggregates, subqueries, and basic federation. * Added RDF::Query::VariableBindings.pm->set method. * Added RDF::Query::_new constructor without any sanity or setup code (used for subquery construction). * Added RDF::Query::Node::Blank::new constructor to avoid RDF::Trine's N-Triples syntax requirements. * Added SAMPLE and GROUP_CONCAT aggregate support. * Added shortcut functions for constructing RDF::Query::Node objects and Algebra Triples, BGPs and GGPs. * Added warning to RDF::Query::Algebra::Project constructor for improper variable lists. * Updated DAWG tests to use SPARQL 1.1 parser. * Removed SPARQLP aggregate tests that don't align with new SPARQL 1.1 semantics. * Updated DAWG eval tests to skip non-approved tests. * Fixed handling of unicode decoding in DAWG eval tests. * Bumped required version of RDF::Trine to 0.123. * Updated t/models.pl to use RDF::Trine::Parser->parse_url_into_model method and redland's 'guess' parser. * Fixed logging key name in RDF::Query::Plan::Exists. * Fixed sse and as_sparql methods in RDF::Query::Algebra::Exists. * Implemented RDF::Query::Algebra::Exists::binding_variables. * Updated BGPOptimizer to estimate selectivity directly instead of using costmodel code. * Removed costmodel code. * Removed unused fixup and execute methods in Algebra classes. * Updated RDF::Query to only instantiate DateTime parser and LWP::UserAgent if needed. * Changed DBPedia network test to align with recent DBPedia update in t/dev-service-description. * Updated SPARQLP parser tests to align with internal changes in RDF::Query::Algebra::Aggregate. * Updated RDF::Query::Algebra::Aggregate and RDF::Query::Plan::Aggregate to syntactically handle HAVING clauses. * Fixed bin/graph-bgp.pl (had bitrotted). * Changed bnodes to named variables in examples/queries/sparql-bgp-people-knows.rq. * RDF::Query::Util::cli_make_query now defaults the 'class' parameter to 'RDF::Query'. * Removed dependency list and added perlrdf link to POD in RDF::Trine and RDF::Query. Version 2.201 (2010-01-30) * Added benchmark/lubm.pl. * Added examples/queries/sparql-ask-people.rq. * Added RDFa tests. * Added Data::UUID prerequisite to META.yml and Makefile.PL. * Updated ::Model::RDFTrine::add_uri and ::add_string to use new RDF::Trine::Parser methods. * Updated as_sparql and sse code to work with more recent RDF::Trine versions. * Removed as_sparql caching in RDF::Query::Algebra::Triple. * Renamed RDF::Query test files to remove old-style numbering. * Updated parser tests to track new RDF::Trine::Node::Literal internal structure. * Updated prereq version of RDF::Trine to 0.114. * Fixed NAME POD section in RDF::Query::ServiceDescription (RT52264 from KjetilK). Version 2.200 (2009-08-06) * Federation / Service Descriptions: * Rewrote the optimistic plan generator in RDF::Query::Federate::Plan. * Added threshold timeout argument to RDF::Query::Plan::ThresholdUnion and support for it in RDF::Query::Federate::Plan. * Simplified logging in RDF::Query::Federate::Plan (now only logs to category 'rdf.query.federate.plan'). * RDF::Query::ServiceDescription now adds an 'origin' label annotations to RDF::Query::Algebra::Triple objects. * Removed check of the sd:definitive property in RDF::Query::ServiceDescription (was based on wrong assumptions). * Updated RDF::Query::ServiceDescription::answers_triple_pattern() to recognize wildcard capabilities, constructor now adds wildcard by default. * Added new RDF::Query::ServiceDescription::new_with_model constructor. * RDF::Query::ServiceDescription::computed_statement_generator now returns empty iterators when passed triple patterns with bound blank nodes. * Added test data in data/federation_data/. * RDF::Query::Plan::Service now adds an 'origin' label annotation to the RDF::Query::VariableBindings object. * Added 'optimistic_threshold_time' query flag to RDF::Query::ExecutionContext and RDF::Query constructor. * RDF::Query::Federate::add_service() now adds the appropriate computed statement generators to the query object. * Removed optimistic query rewriting test from t/34-servicedescription.t (now covered by t/federate.t). * Added t/federate.t with tests for optimistic federated query optimization using ::Plan::ThresholdUnion and RDF::Endpoint::Server. * Aggregates: * Fixed serialization quoting issue in RDF::Query::Algebra::Aggregate::sse(). * RDF::Query::Plan::Aggregate now attempts plain string comparisons for MIN,MAX when strict_errors is not set. * Added support for average aggregates, and fixed datatype support in aggregates. * Command Line Inferface: * Added bin/query.pl, a fork of examples/query.pl, to support simple query execution. * Added RDF::Query::Util as home to helper functions (added CLI args parsing functions to create queries and models). * Simplified CLI argument parsing in bin/ and examples/ programs. * Refactoring, Code Cleanup, and Documentation: * Added and updated POD to RDF::Query::Util, RDF::Query::Node and RDF::Query::Plan and RDF::Query::Federate::Plan. * Added check for RDFQUERY_THROW_ON_SERVICE environment variable in RDF::Query::Plan::Service. * Cleaned up code in RDF::Query::Model::get_computed_statements(). * Updated SSE formatting and uninitialized warning in RDF::Query::Plan, RDF::Query::Algebra::Filter, RDF::Query::Algebra::Distinct and RDF::Query::Algebra::Sort. * Moved shared RDF::Trine-related model methods into RDF::Query::Model from subclasses. * Raised required RDF::Trine version to 0.111 which brings RDF::Trine::Graph support. * RDF::Query::Model::RDFTrine::BasicGraphPattern::execute now returns $self. * Removed dependency on Test::JSON, List::MoreUtils, and XML::Parser. * Removed TODO on test in t/29-serialize.t. * Removed unnecessary code in RDF::Query::Plan subclasses, bin/query.pl, bin/graph-bgp.pl and bin/graph-query.pl. * Logging: * Added logging in RDF::Query::Plan::ThresholdUnion, and RDF::Query::Model. * Changed logging level from debug to trace in RDF::Query::Plan::Triple, RDF::Query::Plan::Project, RDF::Query::Plan::Filter, RDF::Query::Plan::Join::NestedLoop, RDF::Query::Plan::PushDownNestedLoop, and RDF::Query::Model::RDFTrine. * Added calls to Log::Log4perl::is_debug to eliminate unnecessary serialization of logging when not in use. * Made error message more useful when SERVICE calls fail in RDF::Query::Plan::Service. * Bugfixes: * Fixed bug in RDF::Query::Plan::Offset in cases where the offset was beyond the end of the result set. * Fixed testing for Bloom::Filter in t/31-service.t. * Fixed test expectations when making remote DBPedia query in t/34-servicedescription.t. * Fixed check of $ENV{RDFQUERY_NETWORK_TESTS} to test boolean value, not just existence. * Fixed (bad) expected serializations in t/29-serialize.t. * Fixed bug in RDF::Query::Plan::ThresholdUnion attempting to close an iterator twice. * Fixed sse serialization issues in RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Project. * Fixed bug in RDF::Query::Node::from_trine() that up-cast blank nodes to variables. * Fixed quoting issue in RDF::Query::Algebra::Service::sse(). * Fixed parameter handling in bin/graph-qeps.pl:prune_plans(). * Fixed handling of 'GRAPH ?g {}' (empty GraphGraphPatterns) to return all graph names. * Added check for ref($node) in RDF::Query::VariableBindings::new (broke code after previous removal of blessed() check). * Added use of defined() in code that had been testing boolean value of objects (and causing expensive string overloading). * Miscellaneous Changes: * Added examples of queries and service descriptions in examples/. * Updated dawg-eval.t to actually test graph equivalence. * Clarified graph labels in RDF::Query::Model::RDFTrine::BasicGraphPattern::graph(). * Added the ability to add label annotations to RDF::Query::VariableBindings, RDF::Query::Algebra::Triple and RDF::Query::Algebra::Quad objects. * RDF::Query::Plan::Quad and RDF::Query::Plan::Triple now add an 'origin' label annotation to the RDF::Query::VariableBindings object if the underlying statement has one. * RDF::Query::Plan::prune_plans now uses a stable sort when comparing plan costs. * RDF::Query::Algebra::Triple::new now up-casts to RDF::Query node objects, if necessary. * RDF::Query::Model::RDFTrine::generate_plans now respects the force_no_optimization query flag. * RDF::Query::Algebra::BasicGraphPattern::sse() now sorts triples for output. * Added distinguish_bnode_variables method to RDF::Query::Algebra::Quad and RDF::Query::Algebra::Triple. * Added RDF::Query::Node::Blank::make_distinguished_variable method. * Added caching of sparql serializations to RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Triple. * Added code to RDF::Query::VariableBindings::new to up-cast any RDF::Trine::Node objects to their RDF::Query equivalents. * RDF::Query::new() now looks for $args{ defines }, and adds them to the internal %options HASH. * Added hook in RDF::Query::execute_plan() to print the query plan to STDERR if $options{plan} is set (can be set if defines). * Updated RDF::Query::Plan to only consider model-specific plans (if there are any). Version 2.100 (2009-03-18) * API: * Added ::Algebra::BasicGraphPattern::connected method. * Added 'node_counts' as a recognized key to ::Model::RDFTrine::suports(). * Added ::Model::node_count method. * Added ::Model::RDFTrine::count_statements() and ::Model::RDFTrine::node_count() methods. * Added 'store' field to the data returned by the meta() method in the ::Model::* classes. * Added a peek method to ::Iterator to support execution deferment like in ::Algebra::Service. * Added ability to force SERVICE calls to abort using $ENV{RDFQUERY_THROW_ON_SERVICE} and RequestedInterruptError. * Added bf methods to ::Triple and ::BasicGraphPattern to describe the BGP in terms of bound/free. * Added bind_variables method to RDF::Query::Algebra. * Added caching to ::Algebra::Service::_names_for_node. * Added code to count (and warn) the rate of false positives from a bloomjoin. * Added cost model hooks in RDF::Query and ::Algebra::BasicGraphPattern. * Added FeDeRate BINDINGS to the list of supported extensions. * Added get_basic_graph_pattern to ::Model::RDFTrine (not used yet). * Added is_solution_modifier() methods to ::Algebra classes. * Added labels and common patterns to service description template. * Added more logic and debugging to aggregating triples into BGPs for SERVICE calls. * Added optional restriction argument to ::Algebra::subpatterns_of_type. * Added RDF::Query::Model::RDFTrine::BasicGraphPattner::graph() method. * Added RDF::Query::Node::compare for sorting (either Trine or Query) node objects. * Added RDF::Query::plan_class so that ::Federate can overload calls to ::Plan methods. * Added support for computed statement generators (like ARQ's computed property support [e.g. list:member]). * Added trial subsumes() methods to ::Algebra::BasicGraphPattern and ::Algebra::Triple. * Algebra classes now call RDF::Query::algebra_fixup for optimization before falling back on normal fixup code. * Allow equality test and disjunction filters in patterns that can be compiled to SQL. * Fixed ::Algebra::GroupGraphPattern to use join_bnode_streams in cases where the bloom filter wasn't automatically created but instead provided by the user. * Fixed ::Model::RDFTrine::as_string use with Quads. * Fixed argument list for RDF::Query::Algebra::Service::_names_for_node called from bloom function. * Fixed bug so ::Model::RDFTrine::meta may be called as a class method. * Fixed RDF::Query::algebra_fixup to ignore service capabilities that would result in empty BGPs. * Modified code for aggregate queries (though they are currently broken). * Moved construction of bloom-filter-optimized patterns to RDF::Query::Algebra::GroupGraphPattern::_bloom_optimized_pattern(). * Moved initial federated service code into RDF::Query::fixup and ::algebra_fixup. * Moved QEP generation code to RDF::Query::query_plan so it can be overloaded. * Moved RDF::Query::add_service() to RDF::Query::Federate::add_service(). * Parsing service descriptions now more forgiving in the face of missing values. * RDF::Query::Algebra::Triple::new now adds RDF::Query::Node::Variable objects for undefined nodes. * RDF::Query::fixup() now only returns a construct pattern (the query pattern now being returned by RDF::Query::query_plan()). * RDF::Query::Model::get_computed_statements now doesn't die if there's no query object present. * RDF::Query::new() now accepts two-argument form with just $query and \%options. * RDF::Query::Node::Literal objects now can compare as equal when they're of numeric type but the lexical values aren't numeric. * RDF::Query::prune_plans now takes ExecutionContext as an argument, and in turn calls ::Plan::prune_plans. * RDF::Query::query_plan() now returns all possible query plans when called in list context. * RDF::Query::query_plans now calls RDF::Query::prune_plans to select from the list of possible QEPs. * RDF::Trine::Node::Resource now escapes unicode in base URIs (now just relative URI part) before calling URI->new_abs. * Removed now unused RDF::Query::construct() and RDF::Query::fixup(). * Removed old execute() code from ::Algebra classes. * Removed unused redland fallback code from RDF::Query::Model::RDFTrine. * Split RDF::Query::execute into prepare() and execute_plan() methods. * Converted RDF::Query::execute() to use ::Plan classes. * Updated ::Compiler::SQL to recognize ::Algebra::Project objects. * Updates to RDF::Query::execute() to support explicit pre-binding lists (instead of just scalar values). * ::Algebra::GroupGraphPattern now throws an exception if passed unblessed values as patterns. * ::Federate now labels nodes in the QEP tree with applicable services. * ::Model::debug() now shows data from the named graph model. * ::Model::RDFTrine::add_uri now decodes resulting content as utf8 before proceeding. * ::Model::RDFTrine::meta() probes the underlying store object to declare the proper 'store' class. * ::Service::_names_for_node updated to use ::Plan classes for query execution (fixes use of the k:bloom filter). * CLASSES: * Added algebra classes for solution modifiers and query forms (construct, project). * Added code and tests for Query Execution Plan classes RDF::Query::Plan::*. * Added RDF::Query::Federate::Plan for federation-specific code. * Added RDF::Query::BGPOptimizer implementing a basic optimizer for basic selectivity-based join ordering. * Added RDF::Query::CostModel classes for computing/estimating the cost of executing a specific pattern. * Added RDF::Query::ExecutionContext to hold all necessary information for query execution (query, model, bound variables). * Added RDF::Query::ServiceDescription for parsing DARQ-style service descriptions. * Added RDF::Query::VariableBindings to wrap existing HASH-based variable binding structure. * Added stub ::Plan::ThresholdUnion class for running optimistic queries. * Added workaround to RDFCore bridge so that RDF::Core doesn't die if getStmts is called with a Literal in the subj or pred position. * Added a RequestedInterruptError exception class. * Added code for RDF-Trine specific BGP query plans. * Changed debugging in RDF::Query modules to use Log::Log4perl. * Made SERVICE work again by rolling back streaming socket work (now commented out). * Moved federation code to new RDF::Query::Federate class. * Plan generation now includes any plans the model object can provide. * RDF::Query now always uses a cost model (defaulting to ::Naive). * RDF::Query::Federate now defaults to SPARQLP parser. * Removed logging/warn calls from forked process in ::Service (was screwing up the parent-child IO pipe). * Removed use of "DISTINCT" queries in SERVICE calls (for pipelining). * ServiceDescription now only runs sofilter if ?subject/?object are bound. * Started work on a more holistic approach to supporting service descriptions (instead of using add_computed_statement_generator()). * Updated ::Algebra::Service to fork and use LWP's callback mechanism for concurrency. * ::Algebra::Service now defers loading of content until the first result is requested. * ::Model::RDFTrine now only produces BGP plans if there is no get_computed_statement_generators in the query object. * Code now materializes all node identities before creating the Bloom filter (so capacity arg is accurate). * Bloom filter use is now only attempted on SERVICE blocks that don't immediately contain a FILTER. * Re-ordered conditionals so that the service-bloom-filter try block is called less frequently. * SERVICE execution now uses non-identity reasoning Bloom filter function. * SYNTAX and SERIALIZATION: * Added from_sse method to ::Statement, ::Node. * Added initial code for ARQ-style property paths. * Added initial parser code for SPARQL Update (SPARUL) extension. * Added new 'UNSAID' syntax for SPARQLP, implementing negation. * Added parse_expr method to RDF::Query::Parser::SPARQL. * Added RDF::Query::Algebra::Quad::bf() method. * Fixed RDQL parser to qualify URIs before returning from parse(). * Fixed SSE serialization of Aggregates using '*' instead of a variable as the column. * Removed (now unused) parser generator script. * SPARQL parser now always adds a ::Algebra::Project (even when the query selects '*'). * SPARQL parser now creates ::Algebra::Construct objects for CONSTRUCT queries. * SPARQL parser now puts ::Distinct above ::Sort algebras in the parse tree. * SPARQLP parser now creates ::Project object as parent of any ::Aggregate. * Turtle parser now doesn't modify the lexical value of numeric typed literals. * Turtle parser now makes universal IDs for all blank node (even those with given IDs like _:xyz). * Updated ::Algebra SSE serializations to conform to Jena's serialization syntax. * Updated ::Algebra::Limit::sse to emit 'slice' serialization if its child is a ::Algebra::Offset. * Updated as_sparql() methods to support the new ::Construct classes. * Updated RDF::Query::sse to emit base and prefix serializations. * Updated SPARQL parser and serializer tests to always assume an ::Algebra::Project on SELECT queries. * Updated SSE serialization of ::Join::PushDownNestedLoop to use 'bind-join' terminology. * Updates to SPARQLP parser to support FeDeRate BINDINGS keyword. * ::Algebra::Distinct now does proper serialization in as_sparql(). * ::GroupGraphPattern::sse() updated to output the '(join ...)' only if the GGP has more than one pattern. * OPTIMIZER: * Added benchmark/plans.pl to show the runtimes of the available QEPs for a query. * Added benchmark/costmodel.pl for testing the RDF::Query::CostModel. * Added logging for execution time (time to construct iterator) of Triples, BGPs, GGPs and sorting. * Added logging of cardinalities in ::Algebra::Triple, ::Algebra::BasicGraphPattern and ::Algebra::Service. * Added logging to plan classes ::NestedLoop, ::Service, ::Triple. * Naive plan is used in ::Federate::Plan::generate_plans only if no optimistic plans are available. * Updated cost model code to work with ::Plan classes instead of ::Algebra classes. * Logging code now uses sse serialization as log keys (because of expressions that can't be serialized as SPARQL). * Logging object can now be passed to RDF::Query constructor. * Updated logging of algebra execution to use SPARQL serialization as logging key. * MISC: * SPARQL parser now constructs ::Algebra::Project objects (had been in RDF::Query::execute). * Updated RDF::Query to require version 0.108 of RDF::Trine. * Added new bloom:filter function variant that doesn't use identity reasoning. * Added debugging information when RDFQUERY_THROW_ON_SERVICE is in effect. * Fixed test plan for t/optimizer.t in cases where no appropriate model is available. * Updated plan generation code to use ::BGPOptimizer when the model supports node_counts. * Fixed SSE serialization bug in ::Algebra::Sort. * Fixed bugs in RDF::Query and RDF::Query::Expression classes that insisted variables be RDF::Query objects (and not simply RDF::Trine objects). * Fixed propogation of iterator binding_names when pre-bound values are used in ::Algebra execution. * Fixed RDF::Query::Algebra::Triple to correctly set binding_names when pre-binding is used. * Fixed use of pre-binding in execution of RDF::Trine optimized BasicGraphPatterns. * Fixed bug in SQL compilation when restricting left-joins to specific node types (based on functions like isIRI). * Fixed node identity computation based on owl:sameAs. * Fixed bitrotted livejournal example script to work with new 2.000 API. * TESTS: * Added expected result count test in t/34-servicedescription.t. * Added initial tests for algebra subsumes method. * Added logging and costmodel tests. * Added more example capabilities and patterns to the test service descriptions. * Added RDF::Trine::Store::Hexastore to test model construction list in t/models.pl. * Added sparql:pattern data to test service descriptions. * Added sse re-serialization test to t/29-serialize.t. * Added support for sparql:pattern in service description parsing. * Added t/plan-rdftrine.t to test QEP generation optimized for RDF::Trine BGPs. * Added test data for repeat patterns (e.g. { ?a ?a ?b}). * Added tests to t/plan.t for QEP sse serialization. * Cleaned up federation tests. * Commented out in-progress service description tests. * Fixed bug in t/23-model_bridge.t to allow two models from the same model class to be used in testing. * Fixed error handling in t/plan.t. * Fixed t/costmodel-naive.t to provide a serialized query to ::Plan::Service constructor. * Fixed test count in algebra-bgp.t. * Fixed test in t/31-service.t that relied on identity-reasoning support in bloom filters (which is now off by default). * Fixed use of RDFQUERY_NETWORK_TESTS in 31-service.t. * Improved use of temporary RDF::Trine stores in RDF::Query tests. * Marked tests TODO for federated query serialization. * Removed what looks like an accidentally pasted test in t/plan.t. * SERVICE tests involving bloom filter handling marked as TODO. * ServiceDescription parsing now gets entire sparql:patterns and not just arcs to depth one. * Silenced unfinished test debugging in t/logging.t. * Updated args to roqet call in failing_earl_tests.sh. * Updated costmodel and logging test expected results that changed due to changes to ::Plan::Join code. * Updated dawg-eval.t regex to recognize RDFTrine blank nodes. * Updated DBPedia test query in t/34-servicedescription.t to reflect new source data. * Updated expected cardinalities in t/logging.t for current plan choice. * Updated logging tests to use the new sparql serialization keys. * Updated SERVICE test in t/plan.t (still broken, but only runs when dev env var RDFQUERY_NETWORK_TESTS in effect). * Updated t/plan.t to be less dependent on the specific state of the kasei.us service endpoint. * Updated test service description RDF for new tests. * Updates to improve logging and test coverage. * EXAMPLES and DOCS: * Added examples/query.pl to show a simple example of loading data and executing a query. * Added examples/create_query_api.pl for generating queries programatically (based on request from KjetilK). * Added bin/graph-bgp.pl to produce a png of a query's BGP variable connectivity graph. * Added bin/graph-query.pl to graph the (one chosen) QEP tree. * Added bin/graph-qeps.pl to vizualize all QEPs of a query with GraphViz. * Updated bin/parse.pl to emit the SSE serialization of the query algebra tree. * Updated bin/rdf_parse_turtle.pl to warn on any parser error. Version 2.002 (2008-04-25) * Updated Bloom::Filter required version in RDF-Query's Makefile.PL. * Fixed bug in get_function() when called as a class method. * Updated sparql:logical-* functions to accept more than two operands. * Added code to make loading of Bloom::Filter optional. * Added Bloom::Filter to list of recommended modules. Version 2.001 (2008-04-19) * Fixed use of "DESCRIBE " (instead of "DESCRIBE ?var"). * Fixed SPARQL serialization of queries with DISTINCT. * Added ::Algebra::subpatterns_of_type method for retrieving all subpatterns of a particular type. * Moved sort_rows() into new ::Algebra classes Sort, Limit, Offset and Distinct. * Updated SQL compiler to handle new ::Algebra classes. * Bumped required RDF::Trine version to 0.106. * Added methods to RDF::Query for retrieving lists of supported SPARQL extensions and extension functions. * RDF::Trine pattern optimization now holds onto the original BGP object for forwarding of calls to as_sparql(). * Removed use of Storable module. Query execution no longer clones parse tree before running. * Simplified project operation on query stream in RDF::Query::execute(). * fixup() method in ::Algebra and ::Expression now passed the query object as an argument. * Replaced ::RDFTrine::unify_bgp with more general fixup() implementation. * ::Algebra classes now defer to the bridge during fixup() to allow model-specific optimizations. * RDF::Trine::Iterator::smap now allows overriding default construct_args (e.g. binding names). * sparql:str now throws a TypeError if argument isn't bound. * Fixed referenced_variables in RDF::Query::Expression. * Fixed COUNT function to only count bound variables. * Fixed aggregation to work with expressions. * Added support for GROUP BY clauses to aggregation queries. * Removed now unused ::Algebra::OldFilter class. * Added serialization tests for aggregate and union patterns. * Moved as_sparql methods from RDF::Trine:: to RDF::Query:: classes. * Removed context- (quad-) specific code from RDF::Query::Algebra::Triple. * Fixed serialization of BOUND filter functions. * Fixed serialization of unary expressions. * Fixed call to join_streams to use ::Iterator::Bindings instead of ::Iterator. * var_or_expr_value now takes bridge object as an argument. * var_or_expr_value will now accept any RDF::Query::Expression object as an argument. * Added test for using AS syntax for variable renaming: "(?var AS ?newvar)". * Added support for MIN, MAX, COUNT and COUNT-DISTINCT aggregate operators to the SPARQLP parser. * Added COUNT DISTINCT variant aggregate operator. * Aggregates (MIN, MAX, COUNT, COUNT-DISTINCT) now return xsd:decimal values (this shouldn't really happen for non-numeric operands to MIN and MAX...) * Added as_sparql submethod to RDF::Query::Node::Literal to serialize numeric types unquoted. * Added var_or_expr_value method in RDF::Query. * Removed unused _true and _false methods in RDF::Query. * Fixed existing aggregates code and tests. * Removed unused (and bitrotted) t/05-stress.t test. * Made all tests that access the network opt-in with the RDFQUERY_NETWORK_TESTS environment variable. * Updated POD for ::Expression::Alias. * Added tests for select expressions. * Added initial support for selecting expression values. Version 2.000 (2008-03-19) * RDF::Query now uses RDF::Trine package for common classes (nodes, statements, iterators). * Moved RDF::Query::Stream into RDF::Trine::Iterator namespace. * Reshuffled SPARQL parsers tests. * Added support for RDF::Trine::Model. * RDF::Trine::Iterator can serialize to XML and JSON natively; Updated tests accordingly. * rdf namespace is only added by default for RDQL. * Fixed bug where the wrong bridge object would be returned for queries with FROM clauses. * Moved query_more code to Algebra classes. * Added RDF::Query::pattern method to return the GGP algebra pattern for the query. * Added RDF::Query::as_sparql method to serialize query as SPARQL string. * Removed unused RDF::Query::set_named_graph_query method. * Fixed bug where triples from a FROM clause were loaded into the underlying persistent store. * Added POD to RDF::Query::Node classes. * Added equal method to RDF::Query::Node classes. * RDF::Query::Node::Blank constructor now optionally produces identifiers. * Merged tSPARQL parsing code into new SPARQLP parser. * Added definite_variables method to RDF::Query::Algebra classes. * Triple class now serializes rdf:type shortcut 'a' appropriately. * Removed 'VAR' type from ::Node::Variable object structure. * Updated code to always expect a HASH reference from ::Bindings iterator. * Added more (sparql and see) serialization tests. * Removed old fixup code, replaced by ::Algebra fixup methods. * Moved FROM clause data loading to its own method. * Started removing old code (RDF::Base, direct DBI use, AUTOLOAD, profiling). * Moved general count_statements() method into bridge superclass. * Fixed SQL compiler to work with ::Algebra and ::Node classes. * Added as_native method to bridge superclass for converting ::Node objects to bridge-native objects. * Updated document namespace declaration for SPARQL XML Results. * Added support for SERVICE queries (ala ARQ) with Bloom filter semijoins. * Moved Expression classes out of the Algebra hierarchy and into their own space (RDF::Query::Expression). Version 1.501 (2007-11-15) * Fixed CONSTRUCT+OPTIONAL bug. * Added as_sparql methods to Algebra and Node classes to serialize patterns in SPARQL syntax. * Added deparse script. * Fixed jena:sha1sum tests when Digest::SHA1 isn't available. Version 1.500 (2007-11-13) * Query Engine: * URIs are now properly qualified with a BASE when present. * Base URI passed to constructor is now used if no BASE clause exists in the query. * Fixed BASE qualification when an IRI contains Unicode characters (Emulating IRI support with the URI module). * NAMED graph data is now seperated from the default model into new (temporary) models. * NAMED graphs now work with RDF::Core. * Added new RDF::Query::Algebra:: classes that are used to represent query ASTs. * Added new RDF::Query::Node:: classes that are used to represent RDF Nodes and Variables. * Major refactoring to RDF::Query::query_more() to enhance extensibility. * Added RDF::Query::query_more_triple() and RDF::Query::query_more_bgp() for triple and bgp matching. * Improved support of GGP pattern matching. * Added sgrep, smap, swatch and concat methods to RDF::Query::Stream class. * Refactored query_more() variants and sort_rows() to use new stream methods sgrep, smap, and concat. * Continued to fix bugs to more closely align with DAWG tests. * Updated DAWG tests to run with the RDF::Core backend. * Any DAWG tests with mf:requires are now automatically marked TODO. * DAWG tests graph equality is now punted to user verification. * Fixed bNode merging code in DAWG tests. * query_more() variants and sort_rows() now all return RDF::Query::Stream objects. * query_more() (and everything it calls) now expects bridge object as a method argument (to ensure NAMED graph querying works). * Added join_streams() to perform netsted-loop natural joins on two Stream objects. * Filters: * Added call_function() method to abstract the generation of ['FUNCTION',...] blocks. * FILTER operator != is now negative for unknown datatypes. * Fixed exception handling in check_constraints(). * Fixed type-promotion in arithmetic operations and added recognized xsd numeric types. * API Chnage: extension functions now take a bridge object as their second argument (after the query object). * Fixed equals() method in RDF::Core bridge to properly use the RDF::Core API. * Javascript function makeTerm now accepts language and datatype parameters. * toString() javascript funtion now returns just the literal value (not including language or datatype). * sop:str now will stringify blank nodes properly. * sparql:langmatches now properly differentiates between no language tag and an empty language tag. * Parsers: * Parsers now generate ASTs using the Algebra and Node classes. * Fixed bugs in SPARQL tokenizer for some Unicode strings (with combining accents). * RDF::Query::Parser::new_literal() now canonicalizes language tags (to lowercase). * Fixed GGP verification in RDF::Query::Parser::SPARQL::fixup(). * Merged GGPAtom changes from tSPARQL to SPARQL grammar. * Backends: * Fixed bug in RDF::Query::Model::RDFCore::equals() when comparing two blank nodes. * Changed RDF::Query::Model::RDFCore::as_string to return strings where node type is identifiable (like Redland, URIs as [...], literal \"...\", bnodes (...)). * Model methods literal_value, literal_datatype and literal_value_langauge now return undef if argument isn't a literal. * Model methods uri_value and blank_identifier now return undef unless argument is of correct type. * Model add_string methods now normalize Unicode input. * Blank node prefix is now scoped to bridge, not lexically in RDF::Query::Model::RDFCore. * RDF::Query::Model::Redland::new_literal now forces argument to a string (XS code breaks on non-PVOK scalars). * RDF::Query::Model::Redland::add_uri now uses LWP instead of relying on Redland to fetch content. * Redland model class now recognizes DateTime objects as literals. * Updated add_uri() method in RDF::Core bridge to support named graphs (only one name per model for now). * Miscellaneous: * Added rudimentary profiling code (Devel::DProf seems to crash on RDF::Query) * Added initial code for supporting property functions (using time:inside as an example). * Removed multi_get code. * Removed code for MULTI patterns. Will replace with algebra-based optimization in the future. * RDF::Query::Stream constructor now accepts an ARRAY reference. * Stopped using the peephole optimizers. Will replace with an algebra-based optimizer in the future. Version 1.044 (2007-09-13) * DAWG tests now emit EARL reports. * Added test harness and temporal data importing scripts to new bin/ directory. * Added support for temporal queries in RDF::Query and optimizers. * Added support for CONSTRUCT queries in new YAPP-based SPARQL parsers. * Added TIMED graph tests for new SPARQL temporal extensions. * Added tests to SPARQL parser for bugs that were discovered with temporal extensions. * Added POD to new SPARQL parsers. * Added new Parse::Yapp-based SPARQL parser in preparation for temporal SPARQL extensions. * Added stub functions for temporal extension support. * Added model_as_stream() method to Redland bridge class. * Addded debug() method to RDF::Query::Model. * Added UNION tests. * Added a javascript debugging flag to RDF::Query. * Added RDF::Query::groupgraphpattern() for handling GGPs. * Added xsd:integer casting function. * Added as a FILTER tee that warns to STDERR. * Added Eyapp SPARQL grammer file and replaced parser with auto-generated code from eyapp. * Marked some SPARQL parser tests as TODO (due to incompatability with new YAPP-based parser). * Network tests are now run against all available models. * Stream tests are now run against all available models. * Query parser initialization code cleaned up (and now supports beta tSPARQL parser). * net_filter_function() now properly accesses nodes with bridge methods. * RDF::Query::Parser::new_blank now assigns default names if none is supplied. * Updated error messages in SPARQL parser to align with new YAPP-based parser. * SPARQL parse_predicate_object() fixed to support tripleNodes. * SPARQL parse_object() and parse_predicate() fixed for whitespace insensitivity. * Moved old hand-written SPARQL parser to now unused SPARQL-RD.pm. * Moved new YAPP-based SPARQL parser to SPARQL.pm. * Copied YAPP-based SPARQL parser for new temporal extension tSPARQL.pm. * Moved existing tests for hand-written SPARQL parser to 01-sparql_parser.t.rd-old. * Moved new YAPP-based SPARQL tests to t/01-sparql_parser.t. * Removed bad-syntax queries from optimizer and SQL compiler tests. * Fixed bad-syntax queries in stream and hook tests. * Made output of coverage tests easier to read by adding test names. * Fixed SPARQL parsers to allow keywords as variable names. * Fixed SPARQL parsers to allow dashes and underscores in variable names. * Fixed bad parser constructor call in SQL compiler tests. * Suppressed RDF::Base loading (again) during RDF::Query startup. * RDF::Query::Model::Redland::debug now shows contexts during iterator debugging. * Moved the old (hand written) SPARQL parser out of the way to make test harnesses happy. * SPARQL parser now respects spec regarding the reuse of blank node labels across BGPs. * SPARQL parser now does the right thing with NIL (the empty list). * SPARQL parser now handles embedded GGPs correctly. * Updated SPARQL parser and tests to pass (almost all) DAWG tests. * Fixed bug where results couldn't be sorted by a non-selected variable. * Removed the unused RDF::Query::timed_graph() method. * RDF::Query::qualify_uri is now more liberal in what it accepts. * Fixed xsd:boolean casting of integer values. * RDF::Query::Parser::new_variable can now generate private variable names. * Fixed test suite to work properly on systems without Redland. * Disabled loading of tSPARQL parser for release. * Removed function prototype definitions in Model base class (was causing loading problems when models are missing). Version 1.043 (2007-06-14) * Fixed broken MANIFEST causing MakeMaker code to break. Version 1.042 (2007-06-13) * Added support for Javascript URL-based extension functions. * Added GPG signing support for Javascript functions. * Added meta methods to model classes. * Added count_statements methods to model classes. * Added default format for stringifying query result streams. * Added SPARQL syntax coverage tests. * Added stream tests. * ISIRI() now returns a sop:isIRI function IRI. * Redland bridge add_uri strips off URI fragment from parser base URI. * Underlying models aren't loaded if turned off by environment variables. * Stopped using the (currently broken) multi-get code. * Model classes aren't used if turned off by environment variable. * Standardized node testing to is_node (replacing older isa_node). * GPG verification failure now throws exceptions. * GPG fingerprint handling is now whitespace insensitive. * Removed (currently) unused multiget tests. * Redland bridge uri_value returns undef if not called with a node. Version 1.041 (2006-11-26) * Removed unwanted '+' signs on stringified bigints when running under perl 5.6.x. * Fixed unicode errors when running under perl 5.6.x. * Added tests for model bridge classes. * Fixed bugs in SQL bridge class. * RDFCore and Redland bridge classes now throw error when passed a bad model object. * Bridge class support() method now responds to feature requests for 'temp_model'. * Fixed bug in RDF::Query::Model::RDFCore::isa_resource returning true for blank nodes. * Fixed code in RDF::Query::Model::::SQL::equals(). * Added partial support for new RDF::Storage::DBI storage class. * Added support for RDF::Query:::Model::SQL models in RDF::Query bridge code. * Removed RDF::Query::Model::SQL::* code that's now in RDF::Storage::DBI. * Added tests for backend bridge classes (RDF::Query::Model::*). * Added checks for which backends support XML serialization. * Fixed naive peephole tests in cases where model supports cost-based analysis. * Fixed bugs in tests that were relying on C to act like C. * Added RDF::Base support. * Fixed bug in C that prevented running queries against multiple models. * Added SimpleQueryPatternError exception class for future work on multi-get optimizations. * Removed forced dependence on Digest::SHA1. * Makefile.PL now recommends Digest::SHA1 (for jena function) and Geo::Distance (for 07-filters.t). Version 1.040 (2006-07-21) * Added support for BOUND() fiters in SQL compilation. * SQL compiler now produces valid SQL when query variable names are SQL reserved keywords. * Moved SPARQL parser test data into YAML. * Added YAML as a build-time prerequisite. * Fixed SPARQL parsing bug for blank nodes as statement objects. * Added peephole optimizer code with naive and cost-analysis strategies. * Added add_string method to RDF::Query::Model::Redland. * Added node_count method to RDF::Query::Model::Redland (only used for testing at this point). * RDF::Query::execute() now uses the peephole optimizer. * Removed punting code in RDF::Query::execute that tried to do JIT optimization. * Removed calls to getLabel() on model objects in test files. * Fixed dawg tests (was dying because of multiple plans being printed). * Fixed cost-based peephole optimizer tests (by forcing Redland to do node counting). Version 1.039 (2006-07-14) * Removed dawg tests from distribution. Only used as developer tests now. * Updated package to use Module::Install instead of ExtUtils::MakeMaker. * Fixed a spurious uninitialized warning in RDF::Query::get_bridge. Version 1.038 (2006-07-09) * Fixed DBI case-sensitivity for PostgreSQL support. * Cleaned up SQL syntax for easier debugging. * Removed extra parens in SQL that were causing postgresql to break. * Reference test foaf file using File::Spec->catfile() instead of directly. * Fixed SPARQL parsing bug for anonymous nodes in FILTER expressions. * Fixed major SQL compilation bugs for testing equality in FILTER expressions. * Fixed bug in hashing code for blank nodes in SQL compiler. Version 1.037 (2006-07-06) * execute() method now takes optional 'bind' parameter for pre-binding query variables. * Updated code to support basic FILTERs in SQL compilation. * Fixed bug in SQL compilation where no WHERE clause was needed. * Fixed bug in SQL compilation when using model-specific Statements tables. * Added Storable to the list of required modules (was missing in the list). * Fixed typos in metadata about past versions in DOAP Changes.ttl. Version 1.036 (2006-06-26) * Fixed memory leak in RDF::Query::Stream that resulted in too many database handles. * Initial support for OPTIONALs in SQL compiler. * Removed LWP requirement for systems without libwww. * Added support for class variable to hold parsing errors. (Beware under threads.) * RDF::Query now sets error variable upon parsing error. (Access with C.) * Fixed POD errors, and added tests for POD coverage. * Added model API methods to SQL model class. * Added C method to RDF::Query::Stream. Version 1.035 (2006-06-04) * Added DAWG tests and harness. * Rewrote core logic in OPTIONAL handling code. * Comparisons on literals now respect numeric datatypes. * Fixed outdated calling conventions in casting functions. * Added custom functions: + jena:sha1sum + jena:now + jena:langeq + jena:listMember + ldodds:Distance * Added new model methods: equals, subject, predicate, object. * Relocated external http-based test data to .Mac URLs. Version 1.034 (2006-05-01) * Added JSON serialization for bindings and boolean queries. * Initial support for compiling RDF queries to SQL queries using the Redland schema. * Added to_string method to query results Stream class. * Model objects now store the query parse tree for access to data needed in serialization. * Unquoted number and boolean literals in SPARQL are now datatyped appropriately. * Fixed crashing bug when RDF::Query::Model::Redland::as_string was called with an undefined value. * Fixed bug parsing queries with predicate starting with 'a' (confused with { ?subj a ?type}). * Fixed bug parsing queries whose triple pattern ended with the optional dot. Version 1.033 (2006-03-08) * Updated test suite to work if one of the model classes is missing. * Data-typed literals are now cast appropriately when used in a FILTER. * Added support for xsd:dateTime datatypes using the DateTime module. * Added support for LANG(), LANGMATCHES() and DATATYPE() built-in functions. * Updated TODO list. * Added more exception types to RDF::Query::Error. * Added POD coverage. * Fixed SPARQL parsing bug for logical operators <= and >=. Version 1.032 (2006-03-03) * Replaced the Parse::RecDescent SPARQL parser with a much faster hand-written one. * Updated SPARQL parsing rules to be better about URI and QName character sets. * FILTER equality operator now '=', not '==' (to match SPARQL spec). * Initial support for FILTER constraints as part of the triple pattern structure (Will allow for nested FILTERs). * Implemented support for ordering query results by an expression. * Fixed bug in expresion handling of unary minus. * Fixed bug in Redland NAMED GRAPH parsing. * Fixed bug in RDF::Core parsing code where blank nodes would be accidentally smushed. Version 1.031 (2006-02-08) * Added support for NAMED graphs. Version 1.030 (2006-01-13) * Added support for SELECT * in SPARQL queries. * Added support for default namespaces in SPARQL queries. * Added tests for querying RDF collections in SPARQL (1 ?x 3) * Added tests for triple patterns of the form { ?a ?a ?b . } * Added tests for default namespaces in SPARQL. * Added tests for SELECT * SPARQL queries. * Bugfix where one of two identical triple variables would be ignored ({ ?a ?a ?b }). Version 1.028 (2005-11-18) * Added SPARQL functions: BOUND, isURI, isBLANK, isLITERAL. * Updated SPARQL REGEX syntax. * Updated SPARQL FILTER syntax. * Added SPARQL RDF Collections syntactic forms. * Fixed FILTER support in OPTIONAL queries. * Added binding_value_by_name method to Query results stream class. * Added isa_blank methods to RDF::Redland and RDF::Core model classes. * Fixed RDF literal datatyping when using Redland versions >= 1.00_02. * Updated SPARQL grammar to make 'WHERE' token optional. * Added directives to SPARQL grammar. * Updated SPARQL 'ORDER BY' syntax to use parenthesis. * Fixed SPARQL FILTER logical-and support for more than two operands. * Fixed SPARQL FILTER equality operator syntax to use '=' instead of '=='. * Now requires Test::More 0.52 because of changes to is_deeply(). Version 1.027 (2005-07-28) * Updated to follow SPARQL Draft 2005.07.21: + ORDER BY arguments now use parenthesis. + SPARQL parser now supports ORDER BY operands: variable, expression, or function call. * Added binding_value_by_name() method to query result streams. Version 1.026 (2005-06-05) * Added new DBI model bridge (accesses Redland's mysql storage directly). * Added built-in SPARQL functions and operators (not connected to grammar yet). * Added bridge methods for accessing typed literal information. Version 1.024 (2005-06-02) * Added missing SPARQL OFFSET grammar rules. * Added XML Results support for graph and boolean queries (DESCRIBE, CONSTRUCT, ASK). * Fixed major bugs in RDF::Core bridge: + Bridge wasn't using passed model. + Literal construction was broken. + Blank node construction was broken when no identifier was specified. * Stream::bindings_count now returns the right number even if there is no data. * XML Result format now works with RDF::Core models. * The Model bridge object is now passed to the Stream constructor. * Internal code now uses the variables method. * Removed redundant code from ORDER BY/LIMIT/OFFSET handling. * Removed unused count method. * Removed unused delegating AUTOLOAD. * Removed unused parse_files method. * Removed usused add_file method. * Removed duplicate net test file. * Added test file for local file-based SPARQL 'FROM' queries. * Added test file for SPARQL Result Forms (LIMIT, ORDER BY, OFFSET, DISTINCT). * Added test file for SPARQL Protocol for RDF (XML Results). * Added new tests based on Devel::Cover results. * Some test files now run against both Redland and RDF::Core: + 00-simple.t + 03-coverage.t + 10-sparql_protocol.t * All debugging is now centrally located in the _debug method. * Moved Stream class to lib/RDF/Query/Stream.pm. * Fixed tests that broke with previous fix to CONSTRUCT queries. * Fixed tests that broke with previous change to ASK query results. Version 1.021 (2005-06-01) * Added SPARQL UNION support. * Broke OPTIONAL handling code off into a seperate method. * Added new debugging code to trace errors in the twisty web of closures. Version 1.020 (2005-05-18) * Added support for SPARQL OPTIONAL graph patterns. * Calling bindings_count on a stream now returns 0 with no data. * Added Stream methods: + is_bindings + binding_name + binding_values + binding_names * Added as_xml method to Stream class for XML Binding Results format. Version 1.016 (2005-05-08) * Added initial support for SPARQL ASK, DESCRIBE and CONSTRUCT queries. + Added new test files for new query types. * Added methods to bridge classes for creating statements and blank nodes. * Added as_string method to bridge classes for getting string versions of nodes. * Broke out triple fixup code into fixup_triple_bridge_variables(). * Updated FILTER test to use new Geo::Distance API. Version 1.015 (2005-05-03) * Fixes to the arguments passed to FILTERs. * Filter tests now show example of two custom filters: + Transitive subClassOf testing. + Logical comparison operators (range testing lat/lon values). * Added literal_value, uri_value, and blank_identifier methods to bridges. * Redland bridge now calls sources/arcs/targets when only one field is missing. * Fixes to stream code. Iterators are now destroyed in a timely manner. + Complex queries no longer max out mysql connections under Redland. * Cleaned up node sorting code. + Removed dependency on Sort::Naturally. + Added new node sorting function ncmp(). * check_constraints now calls ncmp() for logical comparisons. * Added get_value method to make bridge calls and return a scalar value. * Fixed node creation in Redland bridge. * Moved DISTINCT handling code to occur before LIMITing. * Added variables method to retrieve bound variable names. * Added binding_count and get_all methods to streams. * get_statments bridge methods now return RDF::Query::Stream objects. Version 1.014 (2005-04-26) * Made FILTERs work in SPARQL. * Added initial SPARQL support for custom function constraints. * SPARQL variables may now begin with the '$' sigil. * Added direction support for ORDER BY (ascending/descending). * Added 'next', 'current', and 'end' to Stream API. Version 1.012 (2005-04-24) * Stream objects now handle being constructed with an undef coderef. * Streams are now objects usinig the Redland QueryResult API. * RDF namespace is now always available in queries. * row() now uses a stream when calling execute(). * Added ORDER BY support to RDQL parser. * SPARQL constraints now properly use the 'FILTER' keyword. * SPARQL constraints can now use '&&' as an operator. * SPARQL namespace declaration is now optional. * Updated tests. Version 1.010 (2005-04-21) * execute now returns an iterator. * Added core support for DISTINCT, LIMIT, OFFSET. * Added initial core support for ORDER BY (only works on one column right now). * Broke out the query parser into it's own RDQL class. * Added initial support for a SPARQL parser. + Added support for blank nodes. + Added lots of syntactic sugar (with blank nodes, multiple predicates and objects). + Added SPARQL support for DISTINCT and ORDER BY. * Moved model-specific code into RDF::Query::Model::*. * Moving over to redland's query API (pass in the model when query is executed). COPYRIGHT Copyright (C) 2005-2010 Gregory Williams. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. AUTHOR Gregory Todd Williams RDF-Query-2.910/README.html000644 000765 000024 00000263241 12173312100 015153 0ustar00gregstaff000000 000000 RDF::Query

RDF::Query - A SPARQL 1.1 Query implementation for use with RDF::Trine.

RDF::Query allows RDQL and SPARQL queries to be run against an RDF model, returning rows of matching results.

Requirements

To install RDF::Query you'll need the following perl modules installed:

  • Data::UUID
  • Digest::SHA
  • DateTime
  • DateTime::Format::W3CDTF
  • Error
  • File::Spec
  • File::Temp
  • I18N::LangTags
  • JSON
  • List::Util
  • LWP
  • Parse::RecDescent
  • RDF::Trine
  • Scalar::Util
  • Set::Scalar
  • Storable
  • URI

The following additional modules are recommended for some functionality:

  • Geo::Distance

Installing RDF::Query

To install, run:

perl Makefile.PL
make
make test
make install

Version History

Version 2.910 (2012-07-22)

  • Bug Fixes
    • Updated RDQL parser to work with recent RDF::Trine releases.

Version 2.909 (2012-11-24)

  • Bug Fixes
    • Fixed bad sparql serialization of filters with equality tests; was using '==' instead of '=' (github issue 53).
    • Fixed bug in RDF::Query::Algebra::Service->referenced_variables.
    • Fixed bug that wasn't passing the active graph to EXISTS filter evaluation.
    • Fixed RDF::Query prepare and execute methods to properly localize the model object.
    • Fixed bug in RDQL parser that mistakenly required a USING clause (github issue 70).
    • Fixed handling of aggregates over empty groups.
  • New Features
    • Added support for VALUES graph pattern (in-place BINDINGS).
    • Added support for UUID() and STRUUID() functions.
    • Added is_update() and specifies_update_dataset() methods to RDF::Query.
    • Accept common typo of SEPARATOR in SPARQL 1.1 parser ("SEPERATOR") with constructor arg 'allow_typos' in RDF::Query::Parser::SPARQL11.
  • Enhancements
    • Fixed SPARQL 1.1 parsing to enforce not using shared bnode labels between update data operations.
    • Improved SPARQL 1.1 parser detection of invalid use of BIND() (when binding already in-scope variables).
    • Fixed bug in SPARQL 1.1 parser to recognize legal Update operations.
    • Updated SPARQL 1.1 parser to allow colon in local part of a prefixname.
    • Updated STRBEFORE() and STRAFTER() implementations to track SPARQL 1.1 standard.
    • Updated property path implementation to track W3C standard (changed counting semantics and dropped {m,n} form).
    • Added support to passthrough query eval to the model if supported and the 'allow_passthrough' option is set on the query object.
    • Added 'canonicalize' option to RDF::Query constructor to canonicalize literal values.
  • Other
    • Updated handling of BIND() in the SPARQL 1.1 parser to match the latest spec semantics.
    • Added ability to run tests of type mf:CSVResultFormatTest.
    • Fixed config handling in rqsh to allow the use of hexastore backends.
    • Merged Log4perl initialization cleanup patches (from github/kba).
    • Use $plan->explain instead of $plan->sse for "explain" rqsh command.
    • Updated EARL IRIs in bin/failing_earl_tests.sh and bin/passing_earl_tests.sh.
    • Removed RDF::Redland recommendation in Makefile.PL.
    • Added doap:implements statements, and updated release data to doap.rdf.
    • Updated RDF::Query::Util::cli_parse_args to allow no-argument setup.
    • Updated xt/dawg/earl.pl to use new EARL IRIs earl:passed and earl:failed.
    • Added POD to bin/rqsh.
    • Updated DAWG test harnesses to support expected query bindings results in RDF/XML format.
    • Fixed xt/dawg-eval11.t to emit TAP failures when query parsing fails.
    • Require RDF::Endpoint 0.05 in xt/dawg-eval11.t.
    • Removed values from test directory list in xt/dawg-eval11.t.
    • Added values to list of test directories in xt/dawg-eval11.t.
    • Added exists test directory to xt/dawg-eval11.t.
    • Added test case confirming bad sparql serialization of equality testing filters (github issue 53).
    • Removed tests for {m,n} property path forms.
    • Fixed RDF::Query::Algebra::Project to throw exception on bad constructor arguments.
    • Added bugtracker info to Makefile.PL.
    • Added POD marking modules as unstable: RDF::Query::BGPOptimizer, RDF::Query::Compiler::SQL, RDF::Query::Federate, RDF::Query::Federate::Plan
    • Improved expected/actual results output when encountering failing tests.
    • Removed old bloom-filter federation code.
    • Removed RDF::Query::ExecutionContext->base method.
    • Fix POD in RDF::Query::Algebra::Table.

Version 2.908 (2012-01-31)

  • Bug Fixes
    • Fixed SPARQL serialization of expressions using && and ||.
    • Fixed SPARQL 1.1 parser to support "GRAPH<iri>" without whitespace.
    • Fixed bug resulting in false positive error when projecting expressions with aggregates.
    • Fixed aggregate evaluation to result in unbound variables on error (instead of dropping the result).
    • Fixed numeric divide operation to return xsd:decimal when operands are xsd:integers.
    • Fixed RDF::Query::Expression::Binary->evaluate to properly throw on div-by-zero.
    • Fixed RDF::Query::Expression::Function->evaluate to propogate type errors in IF().
    • Fixed bin/rqsh to handle queries that use BASE.
    • Fixed bug in RDF::Query::Plan::Join::PushDownNestedLoop that produced invalid results when the RHS was a subselect.
    • Fixed RDF::Query::Algebra::Filter->as_sparql to handle variation of serialization of the child pattern.
    • Fixed bug in SPARQL 1.1 parser that mistakenly introduced aggregate operations in non-aggregate queries.
  • New Features
    • Added support for Service as a binary op (allowing variable-endpoint SERVICE blocks).
    • Added implementations for functions STRBEFORE, STRAFTER, and REPLACE.
    • Added RDF::Query->prepare_with_named_graphs method.
    • Added support for COPY and MOVE operations.
    • Allow percent encoding and backslash escaping in prefix names.
  • Enhancements
    • Fixed RDF::Query::Expression::Binary to canonicalize numeric literal results.
    • Added syntax support for SILENT form of LOAD.
    • Added support for SILENT and variable endpoint handling for SERVICE patterns.
    • Added syntax support for optional GRAPH keyword on SPARQL 1.1 update shortcuts.
    • Updated RDF::Query::Plan::Extend to copy variable bindings instead of using the existing reference.
    • Added RDF::Query::Plan::Extend->explain.
    • Changed explain() syntax of plan quads.
    • Updated plan classes to optionally register intermediate results with a execution delegate object.
    • Made RDF::Query::Plan::Construct uniq the returned triples.
    • Added custom RDF::Query::Plan::Construct->explain method.
    • Normalize language tags used in SPARQL query syntax to lowercase.
    • Modularize RDF::Query::Plan::Service to allow mock testing.
    • Added exception handling in RDF::Query->set_error.
    • Added subplans_of_type method to Plan classes.
    • Fixed use of '__DEFAULT__' sentinel value in RDF::Query::Plan, RDF::Query::Plan::Service, RDF::Query::Node::Resource->as_sparql, and RDF::Query->as_sparql.
    • Force the planner to avoid using a bind-join when the RHS contains a Service subplan (to avoid a DOS attack on the remote endpoint).
    • Updated DATATYPE() to return rdf:langString on language-tagged literals (per RDF 1.1 change).
    • Fixed sse serialization in RDF::Query::Algebra::Service to handle binary op (variable endpoint) form.
    • Croak rather than die in some places, confess and use logdie in one place
    • Allow aggregates in ORDER BY clause.
  • Other
    • Added examples/query_url.pl.
    • Added RDF::Trine::Error::UnimplementedError exception class.
    • Updated required version of RDF::Trine to 0.138.

Version 2.907 (2011-06-04)

  • Bug Fixes
    • Fixed bug in SPARQL 1.1 parser for DESCRIBE queries without a WHERE clause.
    • Fixed join ordering bug for queries with a BINDINGS clause and several joins.
    • Fixed RDF::Query->as_sparql for DESCRIBE queries which project URI terms, not variables.
  • Other
    • Fixed expected test results for DESCRIBE queries without a WHERE clause.
    • examples/query.pl now emits a warning if the query object cannot be constructed.

Version 2.906 (2011-05-14)

  • API Changes
    • Globally changed 'base' to 'base_uri' in code and API.
  • Bug Fixes
    • Fixed SPARQL serialization in RDF::Query::Plan generation of SERVICE blocks to always include braces around the query body.
    • Fixed SPARQL 1.1 parsing bug in property paths using alternatives ('|').
    • Fixed SPARQL 1.1 bug in parsing prefixnames where a graph term is expected.
    • Fixed bug in ZeroOrMore reverse path handling (patterns matching { var path* term }).
    • Fixed bug in RDF::Query::Node::Literal::_cmp that was causing wrong node comparison results.
    • Fixed bug in parsing solution modifiers in CONSTRUCT and DESCRIBE queries.
    • Fixed bug in handling of unbound variables in delete templates in RDF::Query::Plan::Update.
    • Fixed bug in calls to RDF::Query->var_or_expr_value that was preventing use of EXISTS filters in project expressions.
  • New Features
    • Updated RDF::Query::Plan::Iterator->new to allow passing in a callback that will return an iterator, instead of an iterator directly.
    • Updated RDF::Query->query_plan to delegate entire queries to the RDF::Trine::Model when possible.
    • Updated bin/deparse.pl to allow specifying an endpoint url for SPARQL-backed models (which can affect the query plan).
    • Added support for empty SPARQL 1.1 Update sequence operations (allowing no-op updates).
    • Added options to RDF::Query->new to allow forcing RDF::Query to disable query delegation to the underlying store.
    • Added option to RDF::Query::Util::cli_parse_args to specify arbitrary options to be supplied to the RDF::Query->new constructor.
    • Added new, cleaner line-based "explain" format for serializing query Plan and Algebra objects.
    • Added bindings accessor and is_unit method to RDF::Query::Plan::Constant.
    • Added ability to forcibly remove a particular join algorithm from consideration in plan generation.
    • Added 'execute' command in rqsh to allow loading a query from a file or URL.
  • Enhancements
    • Updated SPARQL 1.1 parser to prevent bnode use in DELETE blocks.
    • Updated rqsh to associate common filename extensions with media types (helps in loading local files).
    • Updated RDF::Query::Plan::Path to align with new ALP algorithm from the spec text.
    • Added some simple statistics generation code in RDF::Query::Plan::Join subclasses.
    • Added missing implementation for property paths using {n,} modifier syntax.
    • Added code to guard against mishandling unbound/blank/variable nodes in DELETE templates in RDF::Query::Plan::Update.
  • Other
    • Updated tests to use new RDF::Trine::Iterator->seen_count method instead of deprecated count method.
    • Updated serialization text for zero-length paths in RDF::Query::Plan::Path->plan_node_data.
    • Removed warning of unknown options passed to RDF::Query->new.
    • Removed unused projection code in RDF::Query->execute_plan.
    • Removed Digest::SHA1 from prereq modules, and updated code to use Digest::SHA instead.
    • Remove the meaningless "All rights reserved" copyright text.
    • Fixed test count in t/sparql11-propery_paths.t.
    • Commented out noisy debugging output in RDF::Query::Plan::Path.
    • Added trace debugging in RDF::Query::Plan::Sort.
    • Added options instance variable to RDF::Query::ExecutionContext.
    • Added Module::Pluggable to list of required modules in Makefile.PL.
    • Added mappings from file extensions to media types in dawg test scripts.
    • Various updates to SPARQL 1.1 dawg test harness scripts.

Version 2.905 (2011-02-18)

  • Bug Fixes
    • Fixed mistaken case sensitivity of COALESCE, BNODE, CONCAT, and SUBSTR keywords in SPARQL 1.1 parser.
    • Fixed parsing ambiguity between MIN aggregate and MINUTES function.
    • Fixed Xpath fn:timezone-from-dateTime function return for UTC timezones.
    • Fixed RDF::Query::Algebra::Project->as_sparql handling of binary select expressions.
    • Fixed RDF::Query::Algebra::Construct->sse serialization to include construct triples.
    • Updated handling of BIND() to not close group scope (which was applying filters in the wrong place).
    • Fixed RDF::Query::Parser::SPARQL11 to handle whitespace between tokens in 'INSERT DATA' and 'DELETE DATA'.
    • Fixed RDF::Query::Parser::SPARQL11 to handle empty ModifyTemplates.
    • Fixed SPARQL 1.1 parser to properly set relevant datasets for update operations.
    • Fixed plan code for zero-length paths to return RDF::Query::Node-based results (not Trine-based).
    • Fixed bug in RDF::Query::Plan::Clear when attempting to clear the default graph.
    • Fixed exception throwing on numeric binary expression eval without numeric literal arguments.
    • Updated RDF::Query::Plan to handle update operations with different datasets for matching (USING clause).
  • New Features
    • Added SPARQL 1.1 numeric functions: ABS, CEIL, FLOOR, ROUND, RAND
    • Added SPARQL 1.1 string functions: CONCAT, SUBSTR, STRLEN, UCASE, LCASE, ENCODE_FOR_URI, CONTAINS, STRSTARTS, STRENDS
    • Added SPARQL 1.1 date functions: NOW, TIMEZONE, TZ, YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS
    • Added SPARQL 1.1 hashing functions: MD5, SHA1, SHA224, SHA256, SHA384, and SHA512
    • Added RDF::Query::Functions->install_function method, and implementations for fn:compare, fn:concat, and fn:substring.
    • Updated RDF::Query->execute_with_named_graphs to accept optional arguments to be passed to execute().
    • Added support for CONSTRUCT WHERE syntax shortcut.
    • Added support for SILENT modifier to SERVICE patterns.
    • Added SILENT flag in RDF::Query::Algebra::Service.
    • Added initial code to create SPIN serializations of queries.
    • Added RDF::Query::Node::Literal->type_list method.
  • Enhancements
    • Updated xt/dawg-syntax11.t to find more syntax queries in the dawg test data.
    • SPARQL 1.1 parser now throws errors for more classes of syntactically invalid queries (repeated expression aliases, wrong number of BINDINGS values, SELECT * with use of grouping).
    • Added support for non-strict comparisons of xsd:strings in RDF::Query::Node::Literal (based on $LAZY_COMPARISONS variable).
  • Other
    • Major refactor of xt/dawg-eval11.t test harness.
    • Updated required version of Test::More to 0.88 (0.86 doesn't have done_testing() used in t/store-config.t).
    • Updated RDF::Query::Expression::Function->evaluate to throw an error if no function coderef is found.

Version 2.904 (2010-11-23)

  • Added 'set prefix' and 'time' commands to rqsh.
  • Added string concatenation overloading of binary +.
  • Fixed bug in property path evaluation on encountering blank path nodes.
  • Fixed use of binary '*' in FILTER without numeric data in t/resultforms.t.
  • Fixed bin/dawg11-status.pl to only load SPARQL 1.1 earl result files.
  • Fixed RDF::Query::Plan::Update to ignore non-ground triples (unbound variables don't act as wildcards).
  • Fixed handling of named graph data in insert/delete patterns of update operations.
  • Updated RDF::Query::Util to use RDF::Trine::Model->temporary_model and guess an appropriate parser based on filenames.
  • Updated namespace URI in RDF::Query::Functions::Jena to <http://jena.hpl.hp.com/ARQ/function#>.
  • Updated namespace URI in RDF::Query::Functions::Xpath to <http://www.w3.org/2005/xpath-functions#>.
  • Updated xt/dawg-eval11.t to align with updated test case manifest schemas.
  • Updated xt/dawg-eval11.t to prevent test cases with missing files from emitting failures (now skipped).
  • Added xpath function implementations.
  • Added SPARQL 1.1 Update shortcut syntax support for COPY, MOVE, and ADD graph operations.
  • Fixed bug in RDF::Query::Plan::Construct that wasn't giving each result a new set of blank nodes (RT 63155).
  • Updated SYNOPSIS and execute() and execute_plan() methods POD (RT 63153 comment from Ivan Shmakov).
  • Updated documentation to explicitly discuss inherited methods due to subclassing.
  • Fixed RDF::Query::Algebra::Sequence->sse to serialize operations in proper order.
  • Added $silent argument to RDF::Query::Algebra::Clear constructor.

Version 2.903 (2010-11-03)

  • SPARQL 1.1
    • New features
      • Added support for BIND(expr AS ?var) operator.
      • Added isNumeric built-in function, checking for proper datatype and valid lexical forms for numerics.
    • Updates
      • Updated SPARQL 1.1 parser to handle WITH clauses with PrefixedNames.
      • SPARQL 1.1 parser now sets error and returns if no query string is supplied.
      • Added more exception handling in RDF::Query::Parser::SPARQL11.
      • Updated CLEAR and DROP handling in SPARQL 1.1 parser and engine to handle 'NAMED' and 'ALL' variants.
      • Renamed 'binding_variables' methods to 'potentially_bound' to align with current SPARQL 1.1 terminology.
      • Results returned by property path plans are now RDF::Query::VariableBindings objects and are properly projected.
      • The query planner now interprets simple paths as basic graph patterns.
      • Removed Algebra and Plan classes for Not and Exists operations (now handled in other classes).
      • Updated RDF::Query::Plan::Extend to drop the extended variable binding if evaluating the extension yields an error.
      • Updated RDF::Query::Plan::Update to accept delete patterns with variables.
      • Updated subquery plan objects to use a sub-plan, not a full query object.
    • Bug fixes
      • Fixed SPARQL 1.1 parser bug that was returning partial data from a failed parse call.
      • Fixed SPARQL 1.1 parser bug for queries with multiple FILTERs in a GGP including an EXISTS filter.
      • Fixed SPARQL 1.1 parser bug for DROP/CLEAR queries.
      • Fixed SPARQL 1.1 parser typo of 'SEPARATOR' argument to GROUP_CONCAT aggregate.
      • Fixed SPARQL 1.1 parser to handle FITLERS to the left of MINUS/OPTIONAL and allow projecting '*' with other vars/epressions.
      • Fixed SPARQL 1.1 parser case sensitivity of project expression 'AS' token.
      • Fixed SPARQL 1.1 parser handling of of subqueries within GRAPH blocks.
      • Fixed miscellaneous bugs and improved performance of parsing SPARQL 1.1 queries.
      • Made parsing of very large triple blocks non-recursive in SPARQL 1.1 parser.
      • Fixed COALESCE function to handle errors properly.
      • Fixed RDF::Query::Plan::Aggregate to extend results using AliasExpressions from HAVING clauses.
      • Fixed bug that was ignoring GROUP BY clauses when no aggregate operations were present.
      • Fixed bad looping on start node of unbounded property paths.
      • Fixed bug in query planning of '^' property paths.
      • Fixed zero-length property path evaluation within GRAPH blocks.
      • Removed plan duplication of zero-length case in '*' property paths.
      • Fixed handling of named graphs within subqueries (changes to active graph handling in the query planner).
      • Fixed type-promotion and handling of numeric types in aggregate handling.
      • Fixed RDF::Query::Algebra::Update->as_sparql to handle INSERT/DELETE updates properly.
      • Fixed RDF::Query::Plan::Aggregate->sse to allow handling of (non-blessed, scalar) '*' columns.
  • Query Engine
    • Added ability to pass through arguments to the query planner in RDF::Query->prepare.
    • Updated RDF::Query::Node::Literal::_cmp to handle literals that fail to parse as DateTime objects.
    • Updated RDF::Query::Plan::Limit to use the limit accessor internally.
    • RDF::Query::Plan::NamedGraph now uses lazy iterator for graph names.
    • Updated RDF::Query::VariableBindings to subclass the new RDF::Trine::VariableBindings.
    • RDF::Query::Algebra::Project now implements bind_variables().
    • Updated RDF::Query->describe to use $model->bounded_description instead of simply outgoing arcs.
    • Fixed bug in evaluation of function expressions in which the execution context object was lost (causing EXISTS filters to fail).
    • Fixed optimization of COUNT(*) over 1-triple BGPs to keep the variable binding for the pseudo-variable ?COUNT(*).
    • Fixed sse serialization in RDF::Query::Algebra::Distinct.
    • Fixed RDF::Query::Plan::Join::PushDownNestedLoop to close sub-plans in close() method.
  • rqsh - RDF::Query Shell
    • Added ability to show the parsed algebra with 'parse' command.
    • Added abiility to show the query execution plan with the 'explain' command.
    • Added ability to change the RDF serializer with the 'serializer' command.
    • Added ability to initialize new stores with the 'init' command.
    • Added ability to change underlying store with the 'use' command.
    • Added 'help' command.
    • Updated packaging to install rqsh script (renamed from old bin/rqsh.pl).
    • Updated to catch errors thrown while trying to create new storage backends (e.g. DBI->connect failing).
    • rqsh now defaults to read-write models.
    • rqsh now sets a base URI for the current working directory.
    • Password input now doesn't echo to the terminal.
    • Filename input now autocompletes via Term::ReadLine.
  • Warning and Error Handling
    • Replaced calls to die with throwing exceptions.
    • RDF::Query->new now warns about unrecognized options.
    • DATATYPE() now throws an exception unless passed a valid node object.
    • RDF::Query::Expresion::Binary now throws exceptions on numeric comparisions with non-numeric operands.
    • Fixed error message in RDF::Query::Plan::NamedGraph.
    • Added trace debugging to RDF::Query::Plan::Union.
    • Added trace debugging to RDF::Query::Plan::Project.
    • Added trace debugging to RDF::Query::Plan::Path.
  • Tests and Miscellany
    • Added SPARQL 1.1 syntax and eval test harnesses.
    • Fixed bug in xt/dawg-eval11.t parsing of srx files containing literal values that evaluate to false in perl (e.g. '0').
    • Fixed turtle output for rdfs:comments in xt/dawg/earl.pl.
    • Moved developer tests from t/ to xt/.
    • Refactored RDF::Query::Functions into several auto-loaded sub-packages via Module::Pluggable (patch from tobyink).
    • Removed references to the now-deprecated mac.com in network-based tests.
    • Updated expected values in t/queryform-describe.t for results using bounded descriptions.
    • Updated RDF::Query::Util to look for .prefix-cmd cache of common namespace names.
    • Updated RDF::Query::Util::cli_parse_args to accept '-r' as a read-only flag.
    • Updated required version of RDF::Trine to 0.126.
    • Updated to use URI 1.52 to allow direct usage of IRIs without unicode workarounds.
    • Made code updates to improve performance based on profiling.

Version 2.902 (2010-07-02)

  • Fixed bug in requiring prerequisite modules.

Version 2.901 (2010-06-29)

  • Fixed named graph handling.
  • Fixed bug in handling of * aggregates.
  • Simplified code to create new temporary models when FROM clauses dereference graph data.
  • Added support for SPARQL 1.1 CREATE/DROP GRAPH update operations.
  • Fixed infinite loop bug on parsing certain invalid SPARQL 1.1 update queries.
  • Fixed handling of BGPs within named graphs in RDF::Query::Plan::BasicGraphPattern.
  • Updated syntax error exception messages in SPARQL parsers.
  • Fixed property path evaluation within named graphs.
  • Converted Makefile.PL to properly use Module::Install.

Version 2.900 (2010-06-23)

  • Major Changes
    • SPARQL 1.1 is now the default query parser.
    • RDF::Core is no longer supported as a backend triplestore.
    • Redland is still supported as a backend store, but updated handling of default and named graphs means that existing triples stored in Redland without a context (named graph) will not be accessible from RDF::Query.
  • SPARQL Language Updates
    • Added support for SPARQL 1.1 Query features:
      • Basic Query Federation with BINDINGS and UNDEF support.
      • Functions: IF, STRDT, STRLANG, IRI, URI, BNODE.
      • Infix operators IN and NOT IN.
      • Aggregates COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, and SAMPLE.
      • Property paths.
      • Negation with MINUS graph patterns and NOT EXISTS filters.
    • Added support for SPARQL 1.1 Update features:
      • INSERT/DELETE DATA, INSERT/DELETE WHERE, LOAD, and CLEAR
      • Added support for multi-statement updates.
  • API Updates
    • Removed all bridge classes (RDF::Query::Model::*).
    • Removed RDF::Query::Logger class.
    • Removed net filter function code from RDF::Query.
    • Added as_hash method to Query, Algebra, and Node classes.
    • Removed SPARUL and SPARQLP parsers.
    • RDF::Query no longer loads URIs in FROM and FROM NAMED clauses with SPARQL 1.1 by default.
    • Added ability for RDF::Query::Plan::Iterator to fire a callback when execute() is called.
    • Added new RDF::Query::Plan::ComputedStatement class.
    • Added new RDF::Query::Plan::Iterator class.
  • Bugfixes
    • Fixed whitespace unescaping bug in SPARQL (1.0 and 1.1) parsers.
  • Scripts
    • Added bin/json.pl to print a JSON-formatted representation of the query algebra.
    • Added bin/rqsh.pl CLI interface to executing updates and queries.
    • Added ability to serialize algebra and plan in bin/deparse.pl.
    • bin/deparse.pl now catches parsing errors and prints stacktrace.
  • Optimizations
    • Implemented optimization for COUNT(*) over a single triplepattern.

Version 2.202 (2010-05-22)

  • Added initial SPARQL 1.1 syntax and eval support for select expressions, subqueries, negation, aggregates, subqueries, and basic federation.
  • Added RDF::Query::VariableBindings.pm->set method.
  • Added RDF::Query::_new constructor without any sanity or setup code (used for subquery construction).
  • Added RDF::Query::Node::Blank::new constructor to avoid RDF::Trine's N-Triples syntax requirements.
  • Added SAMPLE and GROUP_CONCAT aggregate support.
  • Added shortcut functions for constructing RDF::Query::Node objects and Algebra Triples, BGPs and GGPs.
  • Added warning to RDF::Query::Algebra::Project constructor for improper variable lists.
  • Updated DAWG tests to use SPARQL 1.1 parser.
  • Removed SPARQLP aggregate tests that don't align with new SPARQL 1.1 semantics.
  • Updated DAWG eval tests to skip non-approved tests.
  • Fixed handling of unicode decoding in DAWG eval tests.
  • Bumped required version of RDF::Trine to 0.123.
  • Updated t/models.pl to use RDF::Trine::Parser->parse_url_into_model method and redland's 'guess' parser.
  • Fixed logging key name in RDF::Query::Plan::Exists.
  • Fixed sse and as_sparql methods in RDF::Query::Algebra::Exists.
  • Implemented RDF::Query::Algebra::Exists::binding_variables.
  • Updated BGPOptimizer to estimate selectivity directly instead of using costmodel code.
  • Removed costmodel code.
  • Removed unused fixup and execute methods in Algebra classes.
  • Updated RDF::Query to only instantiate DateTime parser and LWP::UserAgent if needed.
  • Changed DBPedia network test to align with recent DBPedia update in t/dev-service-description.
  • Updated SPARQLP parser tests to align with internal changes in RDF::Query::Algebra::Aggregate.
  • Updated RDF::Query::Algebra::Aggregate and RDF::Query::Plan::Aggregate to syntactically handle HAVING clauses.
  • Fixed bin/graph-bgp.pl (had bitrotted).
  • Changed bnodes to named variables in examples/queries/sparql-bgp-people-knows.rq.
  • RDF::Query::Util::cli_make_query now defaults the 'class' parameter to 'RDF::Query'.
  • Removed dependency list and added perlrdf link to POD in RDF::Trine and RDF::Query.

Version 2.201 (2010-01-30)

  • Added benchmark/lubm.pl.
  • Added examples/queries/sparql-ask-people.rq.
  • Added RDFa tests.
  • Added Data::UUID prerequisite to META.yml and Makefile.PL.
  • Updated ::Model::RDFTrine::add_uri and ::add_string to use new RDF::Trine::Parser methods.
  • Updated as_sparql and sse code to work with more recent RDF::Trine versions.
  • Removed as_sparql caching in RDF::Query::Algebra::Triple.
  • Renamed RDF::Query test files to remove old-style numbering.
  • Updated parser tests to track new RDF::Trine::Node::Literal internal structure.
  • Updated prereq version of RDF::Trine to 0.114.
  • Fixed NAME POD section in RDF::Query::ServiceDescription (RT52264 from KjetilK).

Version 2.200 (2009-08-06)

  • Federation / Service Descriptions
    • Rewrote the optimistic plan generator in RDF::Query::Federate::Plan.
    • Added threshold timeout argument to RDF::Query::Plan::ThresholdUnion and support for it in RDF::Query::Federate::Plan.
    • Simplified logging in RDF::Query::Federate::Plan (now only logs to category 'rdf.query.federate.plan').
    • RDF::Query::ServiceDescription now adds an 'origin' label annotations to RDF::Query::Algebra::Triple objects.
    • Removed check of the sd:definitive property in RDF::Query::ServiceDescription (was based on wrong assumptions).
    • Updated RDF::Query::ServiceDescription::answers_triple_pattern() to recognize wildcard capabilities, constructor now adds wildcard by default.
    • Added new RDF::Query::ServiceDescription::new_with_model constructor.
    • RDF::Query::ServiceDescription::computed_statement_generator now returns empty iterators when passed triple patterns with bound blank nodes.
    • Added test data in data/federation_data/.
    • RDF::Query::Plan::Service now adds an 'origin' label annotation to the RDF::Query::VariableBindings object.
    • Added 'optimistic_threshold_time' query flag to RDF::Query::ExecutionContext and RDF::Query constructor.
    • RDF::Query::Federate::add_service() now adds the appropriate computed statement generators to the query object.
    • Removed optimistic query rewriting test from t/34-servicedescription.t (now covered by t/federate.t).
    • Added t/federate.t with tests for optimistic federated query optimization using ::Plan::ThresholdUnion and RDF::Endpoint::Server.
  • Aggregates
    • Fixed serialization quoting issue in RDF::Query::Algebra::Aggregate::sse().
    • RDF::Query::Plan::Aggregate now attempts plain string comparisons for MIN,MAX when strict_errors is not set.
    • Added support for average aggregates, and fixed datatype support in aggregates.
  • Command Line Inferface
    • Added bin/query.pl, a fork of examples/query.pl, to support simple query execution.
    • Added RDF::Query::Util as home to helper functions (added CLI args parsing functions to create queries and models).
    • Simplified CLI argument parsing in bin/ and examples/ programs.
  • Refactoring, Code Cleanup, and Documentation
    • Added and updated POD to RDF::Query::Util, RDF::Query::Node and RDF::Query::Plan and RDF::Query::Federate::Plan.
    • Added check for RDFQUERY_THROW_ON_SERVICE environment variable in RDF::Query::Plan::Service.
    • Cleaned up code in RDF::Query::Model::get_computed_statements().
    • Updated SSE formatting and uninitialized warning in RDF::Query::Plan, RDF::Query::Algebra::Filter, RDF::Query::Algebra::Distinct and RDF::Query::Algebra::Sort.
    • Moved shared RDF::Trine-related model methods into RDF::Query::Model from subclasses.
    • Raised required RDF::Trine version to 0.111 which brings RDF::Trine::Graph support.
    • RDF::Query::Model::RDFTrine::BasicGraphPattern::execute now returns $self.
    • Removed dependency on Test::JSON, List::MoreUtils, and XML::Parser.
    • Removed TODO on test in t/29-serialize.t.
    • Removed unnecessary code in RDF::Query::Plan subclasses, bin/query.pl, bin/graph-bgp.pl and bin/graph-query.pl.
  • Logging
    • Added logging in RDF::Query::Plan::ThresholdUnion, and RDF::Query::Model.
    • Changed logging level from debug to trace in RDF::Query::Plan::Triple, RDF::Query::Plan::Project, RDF::Query::Plan::Filter, RDF::Query::Plan::Join::NestedLoop, RDF::Query::Plan::PushDownNestedLoop, and RDF::Query::Model::RDFTrine.
    • Added calls to Log::Log4perl::is_debug to eliminate unnecessary serialization of logging when not in use.
    • Made error message more useful when SERVICE calls fail in RDF::Query::Plan::Service.
  • Bugfixes
    • Fixed bug in RDF::Query::Plan::Offset in cases where the offset was beyond the end of the result set.
    • Fixed testing for Bloom::Filter in t/31-service.t.
    • Fixed test expectations when making remote DBPedia query in t/34-servicedescription.t.
    • Fixed check of $ENV{RDFQUERY_NETWORK_TESTS} to test boolean value, not just existence.
    • Fixed (bad) expected serializations in t/29-serialize.t.
    • Fixed bug in RDF::Query::Plan::ThresholdUnion attempting to close an iterator twice.
    • Fixed sse serialization issues in RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Project.
    • Fixed bug in RDF::Query::Node::from_trine() that up-cast blank nodes to variables.
    • Fixed quoting issue in RDF::Query::Algebra::Service::sse().
    • Fixed parameter handling in bin/graph-qeps.pl:prune_plans().
    • Fixed handling of 'GRAPH ?g {}' (empty GraphGraphPatterns) to return all graph names.
    • Added check for ref($node) in RDF::Query::VariableBindings::new (broke code after previous removal of blessed() check).
    • Added use of defined() in code that had been testing boolean value of objects (and causing expensive string overloading).
  • Miscellaneous Changes
    • Added examples of queries and service descriptions in examples/.
    • Updated dawg-eval.t to actually test graph equivalence.
    • Clarified graph labels in RDF::Query::Model::RDFTrine::BasicGraphPattern::graph().
    • Added the ability to add label annotations to RDF::Query::VariableBindings, RDF::Query::Algebra::Triple and RDF::Query::Algebra::Quad objects.
    • RDF::Query::Plan::Quad and RDF::Query::Plan::Triple now add an 'origin' label annotation to the RDF::Query::VariableBindings object if the underlying statement has one.
    • RDF::Query::Plan::prune_plans now uses a stable sort when comparing plan costs.
    • RDF::Query::Algebra::Triple::new now up-casts to RDF::Query node objects, if necessary.
    • RDF::Query::Model::RDFTrine::generate_plans now respects the force_no_optimization query flag.
    • RDF::Query::Algebra::BasicGraphPattern::sse() now sorts triples for output.
    • Added distinguish_bnode_variables method to RDF::Query::Algebra::Quad and RDF::Query::Algebra::Triple.
    • Added RDF::Query::Node::Blank::make_distinguished_variable method.
    • Added caching of sparql serializations to RDF::Query::Algebra::BasicGraphPattern and RDF::Query::Algebra::Triple.
    • Added code to RDF::Query::VariableBindings::new to up-cast any RDF::Trine::Node objects to their RDF::Query equivalents.
    • RDF::Query::new() now looks for $args{ defines }, and adds them to the internal %options HASH.
    • Added hook in RDF::Query::execute_plan() to print the query plan to STDERR if $options{plan} is set (can be set if defines).
    • Updated RDF::Query::Plan to only consider model-specific plans (if there are any).

Version 2.100 (2009-03-18)

  • API
    • Added ::Algebra::BasicGraphPattern::connected method.
    • Added 'node_counts' as a recognized key to ::Model::RDFTrine::suports().
    • Added ::Model::node_count method.
    • Added ::Model::RDFTrine::count_statements() and ::Model::RDFTrine::node_count() methods.
    • Added 'store' field to the data returned by the meta() method in the ::Model::* classes.
    • Added a peek method to ::Iterator to support execution deferment like in ::Algebra::Service.
    • Added ability to force SERVICE calls to abort using $ENV{RDFQUERY_THROW_ON_SERVICE} and RequestedInterruptError.
    • Added bf methods to ::Triple and ::BasicGraphPattern to describe the BGP in terms of bound/free.
    • Added bind_variables method to RDF::Query::Algebra.
    • Added caching to ::Algebra::Service::_names_for_node.
    • Added code to count (and warn) the rate of false positives from a bloomjoin.
    • Added cost model hooks in RDF::Query and ::Algebra::BasicGraphPattern.
    • Added FeDeRate BINDINGS to the list of supported extensions.
    • Added get_basic_graph_pattern to ::Model::RDFTrine (not used yet).
    • Added is_solution_modifier() methods to ::Algebra classes.
    • Added labels and common patterns to service description template.
    • Added more logic and debugging to aggregating triples into BGPs for SERVICE calls.
    • Added optional restriction argument to ::Algebra::subpatterns_of_type.
    • Added RDF::Query::Model::RDFTrine::BasicGraphPattner::graph() method.
    • Added RDF::Query::Node::compare for sorting (either Trine or Query) node objects.
    • Added RDF::Query::plan_class so that ::Federate can overload calls to ::Plan methods.
    • Added support for computed statement generators (like ARQ's computed property support [e.g. list:member]).
    • Added trial subsumes() methods to ::Algebra::BasicGraphPattern and ::Algebra::Triple.
    • Algebra classes now call RDF::Query::algebra_fixup for optimization before falling back on normal fixup code.
    • Allow equality test and disjunction filters in patterns that can be compiled to SQL.
    • Fixed ::Algebra::GroupGraphPattern to use join_bnode_streams in cases where the bloom filter wasn't automatically created but instead provided by the user.
    • Fixed ::Model::RDFTrine::as_string use with Quads.
    • Fixed argument list for RDF::Query::Algebra::Service::_names_for_node called from bloom function.
    • Fixed bug so ::Model::RDFTrine::meta may be called as a class method.
    • Fixed RDF::Query::algebra_fixup to ignore service capabilities that would result in empty BGPs.
    • Modified code for aggregate queries (though they are currently broken).
    • Moved construction of bloom-filter-optimized patterns to RDF::Query::Algebra::GroupGraphPattern::_bloom_optimized_pattern().
    • Moved initial federated service code into RDF::Query::fixup and ::algebra_fixup.
    • Moved QEP generation code to RDF::Query::query_plan so it can be overloaded.
    • Moved RDF::Query::add_service() to RDF::Query::Federate::add_service().
    • Parsing service descriptions now more forgiving in the face of missing values.
    • RDF::Query::Algebra::Triple::new now adds RDF::Query::Node::Variable objects for undefined nodes.
    • RDF::Query::fixup() now only returns a construct pattern (the query pattern now being returned by RDF::Query::query_plan()).
    • RDF::Query::Model::get_computed_statements now doesn't die if there's no query object present.
    • RDF::Query::new() now accepts two-argument form with just $query and \%options.
    • RDF::Query::Node::Literal objects now can compare as equal when they're of numeric type but the lexical values aren't numeric.
    • RDF::Query::prune_plans now takes ExecutionContext as an argument, and in turn calls ::Plan::prune_plans.
    • RDF::Query::query_plan() now returns all possible query plans when called in list context.
    • RDF::Query::query_plans now calls RDF::Query::prune_plans to select from the list of possible QEPs.
    • RDF::Trine::Node::Resource now escapes unicode in base URIs (now just relative URI part) before calling URI->new_abs.
    • Removed now unused RDF::Query::construct() and RDF::Query::fixup().
    • Removed old execute() code from ::Algebra classes.
    • Removed unused redland fallback code from RDF::Query::Model::RDFTrine.
    • Split RDF::Query::execute into prepare() and execute_plan() methods.
    • Converted RDF::Query::execute() to use ::Plan classes.
    • Updated ::Compiler::SQL to recognize ::Algebra::Project objects.
    • Updates to RDF::Query::execute() to support explicit pre-binding lists (instead of just scalar values).
    • ::Algebra::GroupGraphPattern now throws an exception if passed unblessed values as patterns.
    • ::Federate now labels nodes in the QEP tree with applicable services.
    • ::Model::debug() now shows data from the named graph model.
    • ::Model::RDFTrine::add_uri now decodes resulting content as utf8 before proceeding.
    • ::Model::RDFTrine::meta() probes the underlying store object to declare the proper 'store' class.
    • ::Service::_names_for_node updated to use ::Plan classes for query execution (fixes use of the k:bloom filter).
  • Classes
    • Added algebra classes for solution modifiers and query forms (construct, project).
    • Added code and tests for Query Execution Plan classes RDF::Query::Plan::*.
    • Added RDF::Query::Federate::Plan for federation-specific code.
    • Added RDF::Query::BGPOptimizer implementing a basic optimizer for basic selectivity-based join ordering.
    • Added RDF::Query::CostModel classes for computing/estimating the cost of executing a specific pattern.
    • Added RDF::Query::ExecutionContext to hold all necessary information for query execution (query, model, bound variables).
    • Added RDF::Query::ServiceDescription for parsing DARQ-style service descriptions.
    • Added RDF::Query::VariableBindings to wrap existing HASH-based variable binding structure.
    • Added stub ::Plan::ThresholdUnion class for running optimistic queries.
    • Added workaround to RDFCore bridge so that RDF::Core doesn't die if getStmts is called with a Literal in the subj or pred position.
    • Added a RequestedInterruptError exception class.
    • Added code for RDF-Trine specific BGP query plans.
    • Changed debugging in RDF::Query modules to use Log::Log4perl.
    • Made SERVICE work again by rolling back streaming socket work (now commented out).
    • Moved federation code to new RDF::Query::Federate class.
    • Plan generation now includes any plans the model object can provide.
    • RDF::Query now always uses a cost model (defaulting to ::Naive).
    • RDF::Query::Federate now defaults to SPARQLP parser.
    • Removed logging/warn calls from forked process in ::Service (was screwing up the parent-child IO pipe).
    • Removed use of "DISTINCT" queries in SERVICE calls (for pipelining).
    • ServiceDescription now only runs sofilter if ?subject/?object are bound.
    • Started work on a more holistic approach to supporting service descriptions (instead of using add_computed_statement_generator()).
    • Updated ::Algebra::Service to fork and use LWP's callback mechanism for concurrency.
    • ::Algebra::Service now defers loading of content until the first result is requested.
    • ::Model::RDFTrine now only produces BGP plans if there is no get_computed_statement_generators in the query object.
    • Code now materializes all node identities before creating the Bloom filter (so capacity arg is accurate).
    • Bloom filter use is now only attempted on SERVICE blocks that don't immediately contain a FILTER.
    • Re-ordered conditionals so that the service-bloom-filter try block is called less frequently.
    • SERVICE execution now uses non-identity reasoning Bloom filter function.
  • Syntax and Serialization
    • Added from_sse method to ::Statement, ::Node.
    • Added initial code for ARQ-style property paths.
    • Added initial parser code for SPARQL Update (SPARUL) extension.
    • Added new 'UNSAID' syntax for SPARQLP, implementing negation.
    • Added parse_expr method to RDF::Query::Parser::SPARQL.
    • Added RDF::Query::Algebra::Quad::bf() method.
    • Fixed RDQL parser to qualify URIs before returning from parse().
    • Fixed SSE serialization of Aggregates using '*' instead of a variable as the column.
    • Removed (now unused) parser generator script.
    • SPARQL parser now always adds a ::Algebra::Project (even when the query selects '*').
    • SPARQL parser now creates ::Algebra::Construct objects for CONSTRUCT queries.
    • SPARQL parser now puts ::Distinct above ::Sort algebras in the parse tree.
    • SPARQLP parser now creates ::Project object as parent of any ::Aggregate.
    • Turtle parser now doesn't modify the lexical value of numeric typed literals.
    • Turtle parser now makes universal IDs for all blank node (even those with given IDs like _:xyz).
    • Updated ::Algebra SSE serializations to conform to Jena's serialization syntax.
    • Updated ::Algebra::Limit::sse to emit 'slice' serialization if its child is a ::Algebra::Offset.
    • Updated as_sparql() methods to support the new ::Construct classes.
    • Updated RDF::Query::sse to emit base and prefix serializations.
    • Updated SPARQL parser and serializer tests to always assume an ::Algebra::Project on SELECT queries.
    • Updated SSE serialization of ::Join::PushDownNestedLoop to use 'bind-join' terminology.
    • Updates to SPARQLP parser to support FeDeRate BINDINGS keyword.
    • ::Algebra::Distinct now does proper serialization in as_sparql().
    • ::GroupGraphPattern::sse() updated to output the '(join ...)' only if the GGP has more than one pattern.
  • Optimizer
    • Added benchmark/plans.pl to show the runtimes of the available QEPs for a query.
    • Added benchmark/costmodel.pl for testing the RDF::Query::CostModel.
    • Added logging for execution time (time to construct iterator) of Triples, BGPs, GGPs and sorting.
    • Added logging of cardinalities in ::Algebra::Triple, ::Algebra::BasicGraphPattern and ::Algebra::Service.
    • Added logging to plan classes ::NestedLoop, ::Service, ::Triple.
    • Naive plan is used in ::Federate::Plan::generate_plans only if no optimistic plans are available.
    • Updated cost model code to work with ::Plan classes instead of ::Algebra classes.
    • Logging code now uses sse serialization as log keys (because of expressions that can't be serialized as SPARQL).
    • Logging object can now be passed to RDF::Query constructor.
    • Updated logging of algebra execution to use SPARQL serialization as logging key.
  • Miscellaneous
    • SPARQL parser now constructs ::Algebra::Project objects (had been in RDF::Query::execute).
    • Updated RDF::Query to require version 0.108 of RDF::Trine.
    • Added new bloom:filter function variant that doesn't use identity reasoning.
    • Added debugging information when RDFQUERY_THROW_ON_SERVICE is in effect.
    • Fixed test plan for t/optimizer.t in cases where no appropriate model is available.
    • Updated plan generation code to use ::BGPOptimizer when the model supports node_counts.
    • Fixed SSE serialization bug in ::Algebra::Sort.
    • Fixed bugs in RDF::Query and RDF::Query::Expression classes that insisted variables be RDF::Query objects (and not simply RDF::Trine objects).
    • Fixed propogation of iterator binding_names when pre-bound values are used in ::Algebra execution.
    • Fixed RDF::Query::Algebra::Triple to correctly set binding_names when pre-binding is used.
    • Fixed use of pre-binding in execution of RDF::Trine optimized BasicGraphPatterns.
    • Fixed bug in SQL compilation when restricting left-joins to specific node types (based on functions like isIRI).
    • Fixed node identity computation based on owl:sameAs.
    • Fixed bitrotted livejournal example script to work with new 2.000 API.
  • Tests
    • Added expected result count test in t/34-servicedescription.t.
    • Added initial tests for algebra subsumes method.
    • Added logging and costmodel tests.
    • Added more example capabilities and patterns to the test service descriptions.
    • Added RDF::Trine::Store::Hexastore to test model construction list in t/models.pl.
    • Added sparql:pattern data to test service descriptions.
    • Added sse re-serialization test to t/29-serialize.t.
    • Added support for sparql:pattern in service description parsing.
    • Added t/plan-rdftrine.t to test QEP generation optimized for RDF::Trine BGPs.
    • Added test data for repeat patterns (e.g. { ?a ?a ?b}).
    • Added tests to t/plan.t for QEP sse serialization.
    • Cleaned up federation tests.
    • Commented out in-progress service description tests.
    • Fixed bug in t/23-model_bridge.t to allow two models from the same model class to be used in testing.
    • Fixed error handling in t/plan.t.
    • Fixed t/costmodel-naive.t to provide a serialized query to ::Plan::Service constructor.
    • Fixed test count in algebra-bgp.t.
    • Fixed test in t/31-service.t that relied on identity-reasoning support in bloom filters (which is now off by default).
    • Fixed use of RDFQUERY_NETWORK_TESTS in 31-service.t.
    • Improved use of temporary RDF::Trine stores in RDF::Query tests.
    • Marked tests TODO for federated query serialization.
    • Removed what looks like an accidentally pasted test in t/plan.t.
    • SERVICE tests involving bloom filter handling marked as TODO.
    • ServiceDescription parsing now gets entire sparql:patterns and not just arcs to depth one.
    • Silenced unfinished test debugging in t/logging.t.
    • Updated args to roqet call in failing_earl_tests.sh.
    • Updated costmodel and logging test expected results that changed due to changes to ::Plan::Join code.
    • Updated dawg-eval.t regex to recognize RDFTrine blank nodes.
    • Updated DBPedia test query in t/34-servicedescription.t to reflect new source data.
    • Updated expected cardinalities in t/logging.t for current plan choice.
    • Updated logging tests to use the new sparql serialization keys.
    • Updated SERVICE test in t/plan.t (still broken, but only runs when dev env var RDFQUERY_NETWORK_TESTS in effect).
    • Updated t/plan.t to be less dependent on the specific state of the kasei.us service endpoint.
    • Updated test service description RDF for new tests.
    • Updates to improve logging and test coverage.
  • Examples and Documentation
    • Added examples/query.pl to show a simple example of loading data and executing a query.
    • Added examples/create_query_api.pl for generating queries programatically (based on request from KjetilK).
    • Added bin/graph-bgp.pl to produce a png of a query's BGP variable connectivity graph.
    • Added bin/graph-query.pl to graph the (one chosen) QEP tree.
    • Added bin/graph-qeps.pl to vizualize all QEPs of a query with GraphViz.
    • Updated bin/parse.pl to emit the SSE serialization of the query algebra tree.
    • Updated bin/rdf_parse_turtle.pl to warn on any parser error.

Version 2.002 (2008-04-25)

  • Updated Bloom::Filter required version in RDF-Query's Makefile.PL.
  • Fixed bug in get_function() when called as a class method.
  • Updated sparql:logical-* functions to accept more than two operands.
  • Added code to make loading of Bloom::Filter optional.
  • Added Bloom::Filter to list of recommended modules.

Version 2.001 (2008-04-19)

  • Fixed use of "DESCRIBE <resource>" (instead of "DESCRIBE ?var").
  • Fixed SPARQL serialization of queries with DISTINCT.
  • Added ::Algebra::subpatterns_of_type method for retrieving all subpatterns of a particular type.
  • Moved sort_rows() into new ::Algebra classes Sort, Limit, Offset and Distinct.
  • Updated SQL compiler to handle new ::Algebra classes.
  • Bumped required RDF::Trine version to 0.106.
  • Added methods to RDF::Query for retrieving lists of supported SPARQL extensions and extension functions.
  • RDF::Trine pattern optimization now holds onto the original BGP object for forwarding of calls to as_sparql().
  • Removed use of Storable module. Query execution no longer clones parse tree before running.
  • Simplified project operation on query stream in RDF::Query::execute().
  • fixup() method in ::Algebra and ::Expression now passed the query object as an argument.
  • Replaced ::RDFTrine::unify_bgp with more general fixup() implementation.
  • ::Algebra classes now defer to the bridge during fixup() to allow model-specific optimizations.
  • RDF::Trine::Iterator::smap now allows overriding default construct_args (e.g. binding names).
  • sparql:str now throws a TypeError if argument isn't bound.
  • Fixed referenced_variables in RDF::Query::Expression.
  • Fixed COUNT function to only count bound variables.
  • Fixed aggregation to work with expressions.
  • Added support for GROUP BY clauses to aggregation queries.
  • Removed now unused ::Algebra::OldFilter class.
  • Added serialization tests for aggregate and union patterns.
  • Moved as_sparql methods from RDF::Trine:: to RDF::Query:: classes.
  • Removed context- (quad-) specific code from RDF::Query::Algebra::Triple.
  • Fixed serialization of BOUND filter functions.
  • Fixed serialization of unary expressions.
  • Fixed call to join_streams to use ::Iterator::Bindings instead of ::Iterator.
  • var_or_expr_value now takes bridge object as an argument.
  • var_or_expr_value will now accept any RDF::Query::Expression object as an argument.
  • Added test for using AS syntax for variable renaming: "(?var AS ?newvar)".
  • Added support for MIN, MAX, COUNT and COUNT-DISTINCT aggregate operators to the SPARQLP parser.
  • Added COUNT DISTINCT variant aggregate operator.
  • Aggregates (MIN, MAX, COUNT, COUNT-DISTINCT) now return xsd:decimal values (this shouldn't really happen for non-numeric operands to MIN and MAX...)
  • Added as_sparql submethod to RDF::Query::Node::Literal to serialize numeric types unquoted.
  • Added var_or_expr_value method in RDF::Query.
  • Removed unused _true and _false methods in RDF::Query.
  • Fixed existing aggregates code and tests.
  • Removed unused (and bitrotted) t/05-stress.t test.
  • Made all tests that access the network opt-in with the RDFQUERY_NETWORK_TESTS environment variable.
  • Updated POD for ::Expression::Alias.
  • Added tests for select expressions.
  • Added initial support for selecting expression values.

Version 2.000 (2008-03-19)

  • RDF::Query now uses RDF::Trine package for common classes (nodes, statements, iterators).
  • Moved RDF::Query::Stream into RDF::Trine::Iterator namespace.
  • Reshuffled SPARQL parsers tests.
  • Added support for RDF::Trine::Model.
  • RDF::Trine::Iterator can serialize to XML and JSON natively; Updated tests accordingly.
  • rdf namespace is only added by default for RDQL.
  • Fixed bug where the wrong bridge object would be returned for queries with FROM clauses.
  • Moved query_more code to Algebra classes.
  • Added RDF::Query::pattern method to return the GGP algebra pattern for the query.
  • Added RDF::Query::as_sparql method to serialize query as SPARQL string.
  • Removed unused RDF::Query::set_named_graph_query method.
  • Fixed bug where triples from a FROM clause were loaded into the underlying persistent store.
  • Added POD to RDF::Query::Node classes.
  • Added equal method to RDF::Query::Node classes.
  • RDF::Query::Node::Blank constructor now optionally produces identifiers.
  • Merged tSPARQL parsing code into new SPARQLP parser.
  • Added definite_variables method to RDF::Query::Algebra classes.
  • Triple class now serializes rdf:type shortcut 'a' appropriately.
  • Removed 'VAR' type from ::Node::Variable object structure.
  • Updated code to always expect a HASH reference from ::Bindings iterator.
  • Added more (sparql and see) serialization tests.
  • Removed old fixup code, replaced by ::Algebra fixup methods.
  • Moved FROM clause data loading to its own method.
  • Started removing old code (RDF::Base, direct DBI use, AUTOLOAD, profiling).
  • Moved general count_statements() method into bridge superclass.
  • Fixed SQL compiler to work with ::Algebra and ::Node classes.
  • Added as_native method to bridge superclass for converting ::Node objects to bridge-native objects.
  • Updated document namespace declaration for SPARQL XML Results.
  • Added support for SERVICE queries (ala ARQ) with Bloom filter semijoins.
  • Moved Expression classes out of the Algebra hierarchy and into their own space (RDF::Query::Expression).

Version 1.501 (2007-11-15)

  • Fixed CONSTRUCT+OPTIONAL bug.
  • Added as_sparql methods to Algebra and Node classes to serialize patterns in SPARQL syntax.
  • Added deparse script.
  • Fixed jena:sha1sum tests when Digest::SHA1 isn't available.

Version 1.500 (2007-11-13)

  • Query Engine
    • URIs are now properly qualified with a BASE when present.
    • Base URI passed to constructor is now used if no BASE clause exists in the query.
    • Fixed BASE qualification when an IRI contains Unicode characters (Emulating IRI support with the URI module).
    • NAMED graph data is now seperated from the default model into new (temporary) models.
    • NAMED graphs now work with RDF::Core.
    • Added new RDF::Query::Algebra:: classes that are used to represent query ASTs.
    • Added new RDF::Query::Node:: classes that are used to represent RDF Nodes and Variables.
    • Major refactoring to RDF::Query::query_more() to enhance extensibility.
    • Added RDF::Query::query_more_triple() and RDF::Query::query_more_bgp() for triple and bgp matching.
    • Improved support of GGP pattern matching.
    • Added sgrep, smap, swatch and concat methods to RDF::Query::Stream class.
    • Refactored query_more() variants and sort_rows() to use new stream methods sgrep, smap, and concat.
    • Continued to fix bugs to more closely align with DAWG tests.
    • Updated DAWG tests to run with the RDF::Core backend.
    • Any DAWG tests with mf:requires are now automatically marked TODO.
    • DAWG tests graph equality is now punted to user verification.
    • Fixed bNode merging code in DAWG tests.
    • query_more() variants and sort_rows() now all return RDF::Query::Stream objects.
    • query_more() (and everything it calls) now expects bridge object as a method argument (to ensure NAMED graph querying works).
    • Added join_streams() to perform netsted-loop natural joins on two Stream objects.
  • Filters
    • Added call_function() method to abstract the generation of ['FUNCTION',...] blocks.
    • FILTER operator != is now negative for unknown datatypes.
    • Fixed exception handling in check_constraints().
    • Fixed type-promotion in arithmetic operations and added recognized xsd numeric types.
    • API Chnage: extension functions now take a bridge object as their second argument (after the query object).
    • Fixed equals() method in RDF::Core bridge to properly use the RDF::Core API.
    • Javascript function makeTerm now accepts language and datatype parameters.
    • toString() javascript funtion now returns just the literal value (not including language or datatype).
    • sop:str now will stringify blank nodes properly.
    • sparql:langmatches now properly differentiates between no language tag and an empty language tag.
  • Parsers
    • Parsers now generate ASTs using the Algebra and Node classes.
    • Fixed bugs in SPARQL tokenizer for some Unicode strings (with combining accents).
    • RDF::Query::Parser::new_literal() now canonicalizes language tags (to lowercase).
    • Fixed GGP verification in RDF::Query::Parser::SPARQL::fixup().
    • Merged GGPAtom changes from tSPARQL to SPARQL grammar.
  • Backends
    • Fixed bug in RDF::Query::Model::RDFCore::equals() when comparing two blank nodes.
    • Changed RDF::Query::Model::RDFCore::as_string to return strings where node type is identifiable (like Redland, URIs as [...], literal \"...\", bnodes (...)).
    • Model methods literal_value, literal_datatype and literal_value_langauge now return undef if argument isn't a literal.
    • Model methods uri_value and blank_identifier now return undef unless argument is of correct type.
    • Model add_string methods now normalize Unicode input.
    • Blank node prefix is now scoped to bridge, not lexically in RDF::Query::Model::RDFCore.
    • RDF::Query::Model::Redland::new_literal now forces argument to a string (XS code breaks on non-PVOK scalars).
    • RDF::Query::Model::Redland::add_uri now uses LWP instead of relying on Redland to fetch content.
    • Redland model class now recognizes DateTime objects as literals.
    • Updated add_uri() method in RDF::Core bridge to support named graphs (only one name per model for now).
  • Miscellaneous
    • Added rudimentary profiling code (Devel::DProf seems to crash on RDF::Query)
    • Added initial code for supporting property functions (using time:inside as an example).
    • Removed multi_get code.
    • Removed code for MULTI patterns. Will replace with algebra-based optimization in the future.
    • RDF::Query::Stream constructor now accepts an ARRAY reference.
    • Stopped using the peephole optimizers. Will replace with an algebra-based optimizer in the future.

Version 1.044 (2007-09-13)

  • DAWG tests now emit EARL reports.
  • Added test harness and temporal data importing scripts to new bin/ directory.
  • Added support for temporal queries in RDF::Query and optimizers.
  • Added support for CONSTRUCT queries in new YAPP-based SPARQL parsers.
  • Added TIMED graph tests for new SPARQL temporal extensions.
  • Added tests to SPARQL parser for bugs that were discovered with temporal extensions.
  • Added POD to new SPARQL parsers.
  • Added new Parse::Yapp-based SPARQL parser in preparation for temporal SPARQL extensions.
  • Added stub functions for temporal extension support.
  • Added model_as_stream() method to Redland bridge class.
  • Addded debug() method to RDF::Query::Model.
  • Added UNION tests.
  • Added a javascript debugging flag to RDF::Query.
  • Added RDF::Query::groupgraphpattern() for handling GGPs.
  • Added xsd:integer casting function.
  • Added <http://kasei.us/2007/09/functions/warn> as a FILTER tee that warns to STDERR.
  • Added Eyapp SPARQL grammer file and replaced parser with auto-generated code from eyapp.
  • Marked some SPARQL parser tests as TODO (due to incompatability with new YAPP-based parser).
  • Network tests are now run against all available models.
  • Stream tests are now run against all available models.
  • Query parser initialization code cleaned up (and now supports beta tSPARQL parser).
  • net_filter_function() now properly accesses nodes with bridge methods.
  • RDF::Query::Parser::new_blank now assigns default names if none is supplied.
  • Updated error messages in SPARQL parser to align with new YAPP-based parser.
  • SPARQL parse_predicate_object() fixed to support tripleNodes.
  • SPARQL parse_object() and parse_predicate() fixed for whitespace insensitivity.
  • Moved old hand-written SPARQL parser to now unused SPARQL-RD.pm.
  • Moved new YAPP-based SPARQL parser to SPARQL.pm.
  • Copied YAPP-based SPARQL parser for new temporal extension tSPARQL.pm.
  • Moved existing tests for hand-written SPARQL parser to 01-sparql_parser.t.rd-old.
  • Moved new YAPP-based SPARQL tests to t/01-sparql_parser.t.
  • Removed bad-syntax queries from optimizer and SQL compiler tests.
  • Fixed bad-syntax queries in stream and hook tests.
  • Made output of coverage tests easier to read by adding test names.
  • Fixed SPARQL parsers to allow keywords as variable names.
  • Fixed SPARQL parsers to allow dashes and underscores in variable names.
  • Fixed bad parser constructor call in SQL compiler tests.
  • Suppressed RDF::Base loading (again) during RDF::Query startup.
  • RDF::Query::Model::Redland::debug now shows contexts during iterator debugging.
  • Moved the old (hand written) SPARQL parser out of the way to make test harnesses happy.
  • SPARQL parser now respects spec regarding the reuse of blank node labels across BGPs.
  • SPARQL parser now does the right thing with NIL (the empty list).
  • SPARQL parser now handles embedded GGPs correctly.
  • Updated SPARQL parser and tests to pass (almost all) DAWG tests.
  • Fixed bug where results couldn't be sorted by a non-selected variable.
  • Removed the unused RDF::Query::timed_graph() method.
  • RDF::Query::qualify_uri is now more liberal in what it accepts.
  • Fixed xsd:boolean casting of integer values.
  • RDF::Query::Parser::new_variable can now generate private variable names.
  • Fixed test suite to work properly on systems without Redland.
  • Disabled loading of tSPARQL parser for release.
  • Removed function prototype definitions in Model base class (was causing loading problems when models are missing).

Version 1.043 (2007-06-14)

  • Fixed broken MANIFEST causing MakeMaker code to break.

Version 1.042 (2007-06-13)

  • Added support for Javascript URL-based extension functions.
  • Added GPG signing support for Javascript functions.
  • Added meta methods to model classes.
  • Added count_statements methods to model classes.
  • Added default format for stringifying query result streams.
  • Added SPARQL syntax coverage tests.
  • Added stream tests.
  • ISIRI() now returns a sop:isIRI function IRI.
  • Redland bridge add_uri strips off URI fragment from parser base URI.
  • Underlying models aren't loaded if turned off by environment variables.
  • Stopped using the (currently broken) multi-get code.
  • Model classes aren't used if turned off by environment variable.
  • Standardized node testing to is_node (replacing older isa_node).
  • GPG verification failure now throws exceptions.
  • GPG fingerprint handling is now whitespace insensitive.
  • Removed (currently) unused multiget tests.
  • Redland bridge uri_value returns undef if not called with a node.

Version 1.041 (2006-11-26)

  • Removed unwanted '+' signs on stringified bigints when running under perl 5.6.x.
  • Fixed unicode errors when running under perl 5.6.x.
  • Added tests for model bridge classes.
  • Fixed bugs in SQL bridge class.
  • RDFCore and Redland bridge classes now throw error when passed a bad model object.
  • Bridge class support() method now responds to feature requests for 'temp_model'.
  • Fixed bug in RDF::Query::Model::RDFCore::isa_resource returning true for blank nodes.
  • Fixed code in RDF::Query::Model::::SQL::equals().
  • Added partial support for new RDF::Storage::DBI storage class.
  • Added support for RDF::Query:::Model::SQL models in RDF::Query bridge code.
  • Removed RDF::Query::Model::SQL::* code that's now in RDF::Storage::DBI.
  • Added tests for backend bridge classes (RDF::Query::Model::*).
  • Added checks for which backends support XML serialization.
  • Fixed naive peephole tests in cases where model supports cost-based analysis.
  • Fixed bugs in tests that were relying on as_string() to act like literal_value().
  • Added RDF::Base support.
  • Fixed bug in fixup() that prevented running queries against multiple models.
  • Added SimpleQueryPatternError exception class for future work on multi-get optimizations.
  • Removed forced dependence on Digest::SHA1.
  • Makefile.PL now recommends Digest::SHA1 (for jena function) and Geo::Distance (for 07-filters.t).

Version 1.040 (2006-07-21)

  • Added support for BOUND() fiters in SQL compilation.
  • SQL compiler now produces valid SQL when query variable names are SQL reserved keywords.
  • Moved SPARQL parser test data into YAML.
  • Added YAML as a build-time prerequisite.
  • Fixed SPARQL parsing bug for blank nodes as statement objects.
  • Added peephole optimizer code with naive and cost-analysis strategies.
  • Added add_string method to RDF::Query::Model::Redland.
  • Added node_count method to RDF::Query::Model::Redland (only used for testing at this point).
  • RDF::Query::execute() now uses the peephole optimizer.
  • Removed punting code in RDF::Query::execute that tried to do JIT optimization.
  • Removed calls to getLabel() on model objects in test files.
  • Fixed dawg tests (was dying because of multiple plans being printed).
  • Fixed cost-based peephole optimizer tests (by forcing Redland to do node counting).

Version 1.039 (2006-07-14)

  • Removed dawg tests from distribution. Only used as developer tests now.
  • Updated package to use Module::Install instead of ExtUtils::MakeMaker.
  • Fixed a spurious uninitialized warning in RDF::Query::get_bridge.

Version 1.038 (2006-07-09)

  • Fixed DBI case-sensitivity for PostgreSQL support.
  • Cleaned up SQL syntax for easier debugging.
  • Removed extra parens in SQL that were causing postgresql to break.
  • Reference test foaf file using File::Spec->catfile() instead of directly.
  • Fixed SPARQL parsing bug for anonymous nodes in FILTER expressions.
  • Fixed major SQL compilation bugs for testing equality in FILTER expressions.
  • Fixed bug in hashing code for blank nodes in SQL compiler.

Version 1.037 (2006-07-06)

  • execute() method now takes optional 'bind' parameter for pre-binding query variables.
  • Updated code to support basic FILTERs in SQL compilation.
  • Fixed bug in SQL compilation where no WHERE clause was needed.
  • Fixed bug in SQL compilation when using model-specific Statements tables.
  • Added Storable to the list of required modules (was missing in the list).
  • Fixed typos in metadata about past versions in DOAP Changes.ttl.

Version 1.036 (2006-06-26)

  • Fixed memory leak in RDF::Query::Stream that resulted in too many database handles.
  • Initial support for OPTIONALs in SQL compiler.
  • Removed LWP requirement for systems without libwww.
  • Added support for class variable to hold parsing errors. (Beware under threads.)
  • RDF::Query now sets error variable upon parsing error. (Access with error().)
  • Fixed POD errors, and added tests for POD coverage.
  • Added model API methods to SQL model class.
  • Added close() method to RDF::Query::Stream.

Version 1.035 (2006-06-04)

  • Added DAWG tests and harness.
  • Rewrote core logic in OPTIONAL handling code.
  • Comparisons on literals now respect numeric datatypes.
  • Fixed outdated calling conventions in casting functions.
  • Added custom functions:
    • jena:sha1sum
    • jena:now
    • jena:langeq
    • jena:listMember
    • ldodds:Distance
  • Added new model methods: equals, subject, predicate, object.
  • Relocated external http-based test data to .Mac URLs.

Version 1.034 (2006-05-01)

  • Added JSON serialization for bindings and boolean queries.
  • Initial support for compiling RDF queries to SQL queries using the Redland schema.
  • Added to_string method to query results Stream class.
  • Model objects now store the query parse tree for access to data needed in serialization.
  • Unquoted number and boolean literals in SPARQL are now datatyped appropriately.
  • Fixed crashing bug when RDF::Query::Model::Redland::as_string was called with an undefined value.
  • Fixed bug parsing queries with predicate starting with 'a' (confused with { ?subj a ?type}).
  • Fixed bug parsing queries whose triple pattern ended with the optional dot.

Version 1.033 (2006-03-08)

  • Updated test suite to work if one of the model classes is missing.
  • Data-typed literals are now cast appropriately when used in a FILTER.
  • Added support for xsd:dateTime datatypes using the DateTime module.
  • Added support for LANG(), LANGMATCHES() and DATATYPE() built-in functions.
  • Updated TODO list.
  • Added more exception types to RDF::Query::Error.
  • Added POD coverage.
  • Fixed SPARQL parsing bug for logical operators <= and >=.

Version 1.032 (2006-03-03)

  • Replaced the Parse::RecDescent SPARQL parser with a much faster hand-written one.
  • Updated SPARQL parsing rules to be better about URI and QName character sets.
  • FILTER equality operator now '=', not '==' (to match SPARQL spec).
  • Initial support for FILTER constraints as part of the triple pattern structure (Will allow for nested FILTERs).
  • Implemented support for ordering query results by an expression.
  • Fixed bug in expresion handling of unary minus.
  • Fixed bug in Redland NAMED GRAPH parsing.
  • Fixed bug in RDF::Core parsing code where blank nodes would be accidentally smushed.

Version 1.031 (2006-02-08)

  • Added support for NAMED graphs.

Version 1.030 (2006-01-13)

  • Added support for SELECT * in SPARQL queries.
  • Added support for default namespaces in SPARQL queries.
  • Added tests for querying RDF collections in SPARQL (1 ?x 3)
  • Added tests for triple patterns of the form { ?a ?a ?b . }
  • Added tests for default namespaces in SPARQL.
  • Added tests for SELECT * SPARQL queries.
  • Bugfix where one of two identical triple variables would be ignored ({ ?a ?a ?b }).

Version 1.028 (2005-11-18)

  • Added SPARQL functions: BOUND, isURI, isBLANK, isLITERAL.
  • Updated SPARQL REGEX syntax.
  • Updated SPARQL FILTER syntax.
  • Added SPARQL RDF Collections syntactic forms.
  • Fixed FILTER support in OPTIONAL queries.
  • Added binding_value_by_name method to Query results stream class.
  • Added isa_blank methods to RDF::Redland and RDF::Core model classes.
  • Fixed RDF literal datatyping when using Redland versions >= 1.00_02.
  • Updated SPARQL grammar to make 'WHERE' token optional.
  • Added <commit> directives to SPARQL grammar.
  • Updated SPARQL 'ORDER BY' syntax to use parenthesis.
  • Fixed SPARQL FILTER logical-and support for more than two operands.
  • Fixed SPARQL FILTER equality operator syntax to use '=' instead of '=='.
  • Now requires Test::More 0.52 because of changes to is_deeply().

Version 1.027 (2005-07-28)

  • Updated to follow SPARQL Draft 2005.07.21:
    • ORDER BY arguments now use parenthesis.
    • SPARQL parser now supports ORDER BY operands: variable, expression, or function call.
  • Added binding_value_by_name() method to query result streams.

Version 1.026 (2005-06-05)

  • Added new DBI model bridge (accesses Redland's mysql storage directly).
  • Added built-in SPARQL functions and operators (not connected to grammar yet).
  • Added bridge methods for accessing typed literal information.

Version 1.024 (2005-06-02)

  • Added missing SPARQL OFFSET grammar rules.
  • Added XML Results support for graph and boolean queries (DESCRIBE, CONSTRUCT, ASK).
  • Fixed major bugs in RDF::Core bridge:
    • - Bridge wasn't using passed model.
    • - Literal construction was broken.
    • - Blank node construction was broken when no identifier was specified.
  • Stream::bindings_count now returns the right number even if there is no data.
  • XML Result format now works with RDF::Core models.
  • The Model bridge object is now passed to the Stream constructor.
  • Internal code now uses the variables method.
  • Removed redundant code from ORDER BY/LIMIT/OFFSET handling.
  • Removed unused count method.
  • Removed unused delegating AUTOLOAD.
  • Removed unused parse_files method.
  • Removed usused add_file method.
  • Removed duplicate net test file.
  • Added test file for local file-based SPARQL 'FROM' queries.
  • Added test file for SPARQL Result Forms (LIMIT, ORDER BY, OFFSET, DISTINCT).
  • Added test file for SPARQL Protocol for RDF (XML Results).
  • Added new tests based on Devel::Cover results.
  • Some test files now run against both Redland and RDF::Core:
    • - 00-simple.t
    • - 03-coverage.t
    • - 10-sparql_protocol.t
  • All debugging is now centrally located in the _debug method.
  • Moved Stream class to lib/RDF/Query/Stream.pm.
  • Fixed tests that broke with previous fix to CONSTRUCT queries.
  • Fixed tests that broke with previous change to ASK query results.

Version 1.021 (2005-06-01)

  • Added SPARQL UNION support.
  • Broke OPTIONAL handling code off into a seperate method.
  • Added new debugging code to trace errors in the twisty web of closures.

Version 1.020 (2005-05-18)

  • Added support for SPARQL OPTIONAL graph patterns.
  • Calling bindings_count on a stream now returns 0 with no data.
  • Added Stream methods:
    • is_bindings
    • binding_name
    • binding_values
    • binding_names
  • Added as_xml method to Stream class for XML Binding Results format.

Version 1.016 (2005-05-08)

  • Added initial support for SPARQL ASK, DESCRIBE and CONSTRUCT queries.
    • Added new test files for new query types.
  • Added methods to bridge classes for creating statements and blank nodes.
  • Added as_string method to bridge classes for getting string versions of nodes.
  • Broke out triple fixup code into fixup_triple_bridge_variables().
  • Updated FILTER test to use new Geo::Distance API.

Version 1.015 (2005-05-03)

  • Fixes to the arguments passed to FILTERs.
  • Filter tests now show example of two custom filters:
    • Transitive subClassOf testing.
    • Logical comparison operators (range testing lat/lon values).
  • Added literal_value, uri_value, and blank_identifier methods to bridges.
  • Redland bridge now calls sources/arcs/targets when only one field is missing.
  • Fixes to stream code. Iterators are now destroyed in a timely manner.
    • Complex queries no longer max out mysql connections under Redland.
  • Cleaned up node sorting code.
    • Removed dependency on Sort::Naturally.
    • Added new node sorting function ncmp().
  • check_constraints now calls ncmp() for logical comparisons.
  • Added get_value method to make bridge calls and return a scalar value.
  • Fixed node creation in Redland bridge.
  • Moved DISTINCT handling code to occur before LIMITing.
  • Added variables method to retrieve bound variable names.
  • Added binding_count and get_all methods to streams.
  • get_statments bridge methods now return RDF::Query::Stream objects.

Version 1.014 (2005-04-26)

  • Made FILTERs work in SPARQL.
  • Added initial SPARQL support for custom function constraints.
  • SPARQL variables may now begin with the '$' sigil.
  • Added direction support for ORDER BY (ascending/descending).
  • Added 'next', 'current', and 'end' to Stream API.

Version 1.012 (2005-04-24)

  • Stream objects now handle being constructed with an undef coderef.
  • Streams are now objects usinig the Redland QueryResult API.
  • RDF namespace is now always available in queries.
  • row() now uses a stream when calling execute().
  • Added ORDER BY support to RDQL parser.
  • SPARQL constraints now properly use the 'FILTER' keyword.
  • SPARQL constraints can now use '&&' as an operator.
  • SPARQL namespace declaration is now optional.
  • Updated tests.

Version 1.010 (2005-04-21)

  • execute now returns an iterator
  • Added core support for DISTINCT, LIMIT, OFFSET
  • Added initial core support for ORDER BY (only works on one column right now)
  • Broke out the query parser into it's own RDQL class
  • Added initial support for a SPARQL parser
    • Added support for blank nodes
    • Added lots of syntactic sugar (with blank nodes, multiple predicates and objects)
    • Added SPARQL support for DISTINCT and ORDER BY
  • Moved model-specific code into RDF::Query::Model::*
  • Moving over to redland's query API (pass in the model when query is executed)

Copyright

Copyright © 2005–2010 Gregory Williams. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

RDF-Query-2.910/SIGNATURE000644 000765 000024 00000037450 12173312225 014625 0ustar00gregstaff000000 000000 This file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.70. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA1 1cab68532c15690008faa7656b774d19fefbb0ad Changes.ttl SHA1 8963ea8452cff037bfc791e300c4df1bce9424af MANIFEST SHA1 891eee30430cb6a3dab5c036682d2c5c5b6c8a5b META.yml SHA1 323531f05c7dd3b76d12de9a04a691054cfac6b1 Makefile.PL SHA1 2c60a0555451c00cc6a082f04ee67dd59e869430 README SHA1 d7407f184778b72236825684cbf15b989d8e1c6b README.html SHA1 1c422fda233ab5813719189e2a3cb3fa158b3c9e bin/dawg11-status.pl SHA1 308673ec0fe0342dc63a288ec985d35759d07829 bin/deparse.pl SHA1 3bd85b6d54fae8af8796763c3975e1247259dcec bin/failing_earl_tests.sh SHA1 9bd6ff51968039db7bf2cfa8fce226da9cbed2d2 bin/fedoptimize.pl SHA1 f95665268fd85d4e90b0819ecdbdb295b8f63624 bin/fedquery.pl SHA1 919c5cc34b01e18e96e4a9dfcf5b133e615faa5b bin/graph-bgp.pl SHA1 ebd0bbb693ba3754a530a24dc588c9755e90ddf2 bin/graph-qeps.pl SHA1 d8252589cef68d9bcb97cf55e6757b18381fa0e7 bin/graph-query.pl SHA1 8679520f8270540f77e117f0d3bf98b3d8050b59 bin/hexastore_query.pl SHA1 19a74ccbfab23519fbcb3ba98c84264a51e99bae bin/json.pl SHA1 2d771a00dd890acbe2d6207b920f1a8cb8ce9d7b bin/open-test-files.pl SHA1 e8f22cc48a9b718cd47d85b3efd8ba37b5a76bb2 bin/parse.pl SHA1 0b7d2af82ec39047f6a43d23f18daa45fd00b56e bin/parse_profile.pl SHA1 1d08a6cfc90665744d5e6000584f1b5da796e46b bin/passing_earl_tests.sh SHA1 0eaedd5c43ae75f997158027bd6d223c00609d2b bin/query.pl SHA1 d62f1bd71ec49242259314c41a56ecd0397cd2d9 bin/rqsh SHA1 ca395ffddde2ad5b289df84130f0b1f4642c564f bin/rqsh-server.pl SHA1 14c583933ced0bdc27a81bb7d5cd9e7ef2d9a520 data/Flower-2.rdf SHA1 80c7ba4b112d5173878b1ba6c8968275cc7873d1 data/about.xrdf SHA1 8825e353c65215df9c96ac8ada3615d940d5f0ab data/bnode-person.rdf SHA1 fdecdfedfafd8fdf32db81864d317c3a4c228063 data/federation_data/alice.rdf SHA1 5b9d419a370f50a465bd91a4a80dcfc6a89780f3 data/federation_data/alice_bob.rdf SHA1 73212ebbc3040623f25c5acc1ff21baeec5fbf17 data/federation_data/bob.rdf SHA1 d9e2e3af6a9d74d58c966d1947cdf68161e4d639 data/foaf.xrdf SHA1 c376e4aaa018f642c291aec9df27d7768038b8c1 data/greenwich.rdf SHA1 636a3d5fb8e4a45a3de2aea4a986bacc2ab0b2cb data/named_graphs/alice.rdf SHA1 59e840e1598987e991e527ec45d6576f5053dd21 data/named_graphs/alice.ttl SHA1 b4e13d1fee09c406cdffd6e93d8f10f95d806cc0 data/named_graphs/bob.rdf SHA1 e3a79398e61eed62efca7367dabfddf89c2338d6 data/named_graphs/bob.ttl SHA1 f6499f4b7a5c39ebc1e30c301c2a47ab95be69bf data/named_graphs/meta.rdf SHA1 39fbdd47c713d59167ed03301ad794963c8837f0 data/named_graphs/meta.ttl SHA1 215f259428f7b2661517f33808f55b09d28e3b74 data/named_graphs/repeats1.rdf SHA1 8216059aa8dc09d808bab127c16d41008fc69d92 data/named_graphs/repeats2.rdf SHA1 39477849861b33e741e148f256c91557f416fc14 data/rdfa-test.xhtml SHA1 02797e9814c7d00ff60397ee41a57e83a2344380 data/service-kasei.ttl SHA1 18c24a093059f5f9f07e7218bc9f66b56421b5ec data/service.ttl SHA1 0613e1c2acc90c14e2fe5a87cec209b7d92362e1 data/t-sparql11-aggregates-1.rdf SHA1 8141df2762aba4205d2735e6bb7e7b247b3002c9 data/temporal.rdf SHA1 e899c3ed8de9f756124939af2f09d939a4f380e1 doap.rdf SHA1 37df7c10adcc5341db64c1f70838e76776a16e95 examples/create_query_api.pl SHA1 683386fe0ba1cbe732e2e8b504addfca70e4643d examples/livejournal.pl SHA1 814ff9b3e6fc419d4110e5320ae6b148695fba23 examples/queries/sparql-ask-people.rq SHA1 b14ddff044b912ad758db092f7044e1bebe912be examples/queries/sparql-bgp-people-knows.rq SHA1 450005973a9e5afee2584aacd08368f090784eab examples/queries/sparql-bgp-people.rq SHA1 f247c9312537ae848560ef4b6dc011a020ee5935 examples/queries/sparql-ggp-person-opt-knows.rq SHA1 5b173f4dc339e3a27081a8808bfee84b01adc707 examples/queries/sparqlp-service-people-names.rq SHA1 ff3eb005a55aad140f373c0ae5bfdb3f5b2f5672 examples/query.pl SHA1 2bcfd50de8ed8df4915f9a694619cf94a8573373 examples/query_url.pl SHA1 143ff69d0711f2c19b35cd80fcfc5c9d751e192a examples/service_descriptions/dbpedia_org.ttl SHA1 84fbf8384d6764a7c0a94d1a23eaeb3f41685189 examples/service_descriptions/kasei_us.ttl SHA1 8a924add836b60fb23b25c8506d45945e02f42f4 inc/Module/Install.pm SHA1 d001b4b9a48395a8c4134b234a0e1789138427c5 inc/Module/Install/AuthorTests.pm SHA1 2d0fad3bf255f8c1e7e1e34eafccc4f595603ddc inc/Module/Install/Base.pm SHA1 f0e01fff7d73cd145fbf22331579918d4628ddb0 inc/Module/Install/Can.pm SHA1 7328966e4fda0c8451a6d3850704da0b84ac1540 inc/Module/Install/Fetch.pm SHA1 b62ca5e2d58fa66766ccf4d64574f9e1a2250b34 inc/Module/Install/Makefile.pm SHA1 1aa925be410bb3bfcd84a16985921f66073cc1d2 inc/Module/Install/Metadata.pm SHA1 4d793c044726e06fe35d8d129b76da2803377f92 inc/Module/Install/Scripts.pm SHA1 e4196994fa75e98bdfa2be0bdeeffef66de88171 inc/Module/Install/Win32.pm SHA1 c3a6d0d5b84feb3280622e9599e86247d58b0d18 inc/Module/Install/WriteAll.pm SHA1 52e22046c4a187d23b49a045fa64dbb8838a10ea lib/RDF/Query.pm SHA1 ab6446e6c920621bcecb5a6a6f873b2eaa4ea64e lib/RDF/Query/Algebra.pm SHA1 247cc6d4b804637b37a6dbb778567c837427f007 lib/RDF/Query/Algebra/Aggregate.pm SHA1 8c06840fd7574ce48c7e249c6434f868e4fe9fa7 lib/RDF/Query/Algebra/BasicGraphPattern.pm SHA1 5cf1666aa2fcbfc7e43249c65edef4fb06f0f8ec lib/RDF/Query/Algebra/Clear.pm SHA1 25b25d15ea97040ca08e70e993fe99b12bd0e09d lib/RDF/Query/Algebra/Construct.pm SHA1 0ca9770750de04ffa9beae74f8bc8e1cdfc775a2 lib/RDF/Query/Algebra/Copy.pm SHA1 ba9e872a4b78f1c90177255da6f31777e2425a93 lib/RDF/Query/Algebra/Create.pm SHA1 045bd4c9a95dc3d728ec05d8f54e62285088ebe6 lib/RDF/Query/Algebra/Distinct.pm SHA1 214b265d7398ef79d082a9c0fa90777dc113501f lib/RDF/Query/Algebra/Extend.pm SHA1 9821ab7a335750f5dd2a364b6617f393e5e5e7fd lib/RDF/Query/Algebra/Filter.pm SHA1 ee80eb73615b3692944655bf59db77f6ebefa6bd lib/RDF/Query/Algebra/GroupGraphPattern.pm SHA1 7b28e1c1a4eb80a4413b98ac4b216ac83eb69f26 lib/RDF/Query/Algebra/Limit.pm SHA1 503f076d6fec4b4092c652d731713768899300e0 lib/RDF/Query/Algebra/Load.pm SHA1 6ee82a15ae5d5972e9a4584bee162b75e29c1e1c lib/RDF/Query/Algebra/Minus.pm SHA1 a3c041fc28cda2ca0170222631a4080cad93e64c lib/RDF/Query/Algebra/Move.pm SHA1 669ab6a4066aebc8140f7393689009ba2ae34156 lib/RDF/Query/Algebra/NamedGraph.pm SHA1 6cc72cf959e1d902a623bcfa104ca74384ba0f45 lib/RDF/Query/Algebra/Offset.pm SHA1 3dc6c4676dcee4db060b9ed5346c579d9126630d lib/RDF/Query/Algebra/Optional.pm SHA1 4b8b93c25ad398f3bc470f2133eb7315ab7ab76e lib/RDF/Query/Algebra/Path.pm SHA1 25aba84adbf80a1099402c64d3eea84eecbd5148 lib/RDF/Query/Algebra/Project.pm SHA1 ebe9c55fad66035595f39b9e3502e331e3e8e511 lib/RDF/Query/Algebra/Quad.pm SHA1 5799027f355f90dc79fb592cf512590d4486227e lib/RDF/Query/Algebra/Sequence.pm SHA1 6c3d5500a32048d149fa32864aa9f29885aca951 lib/RDF/Query/Algebra/Service.pm SHA1 9db06d3aa5e000f038f4b7279871e966608a76b9 lib/RDF/Query/Algebra/Sort.pm SHA1 daf3c069e07f8aca304c956515d35f0abe550bca lib/RDF/Query/Algebra/SubSelect.pm SHA1 bfff37bf1904773293aaafd854603259cac33921 lib/RDF/Query/Algebra/Table.pm SHA1 4b099f13d1ffdcbdefd38a8e100967ed844842a0 lib/RDF/Query/Algebra/TimeGraph.pm SHA1 c5447cc8635bdba28eb99e6c61c49d4bec284f08 lib/RDF/Query/Algebra/Triple.pm SHA1 cb0003caa946652a7eb4070f0d0527f4f259c966 lib/RDF/Query/Algebra/Union.pm SHA1 67b6ac50e508d4bd7b64d9ade76c095ba0d5bd98 lib/RDF/Query/Algebra/Update.pm SHA1 18ba77900269dbf743d3ecd7cc83817fe797e3b1 lib/RDF/Query/BGPOptimizer.pm SHA1 005a7434b4e16f18452de3da0756f380f7ec919f lib/RDF/Query/Compiler/SQL.pm SHA1 0bbd4144f2ef419972b5096c6de73ec5858af996 lib/RDF/Query/Error.pm SHA1 2aeb7b24326a1bbce26c30d1c7f457c25d9bc377 lib/RDF/Query/ExecutionContext.pm SHA1 2b46117ea64470505d067babb7bf9f2268c5b8f8 lib/RDF/Query/Expression.pm SHA1 ff893f5d356cc7428513df6a2d3c6763f729f39d lib/RDF/Query/Expression/Alias.pm SHA1 81cd0034584a7411168af04bc9ace842e63add0c lib/RDF/Query/Expression/Binary.pm SHA1 944ab8668526306aa26f1224aec2860d88617a7a lib/RDF/Query/Expression/Function.pm SHA1 1dcf061cd83a1219e5a3fc04055b8927233726c2 lib/RDF/Query/Expression/Nary.pm SHA1 9489d9ed1a765d3369174da3f1b4fcccd29e4f96 lib/RDF/Query/Expression/Unary.pm SHA1 7c3a5199c5821f473415ac6c45d098e355320932 lib/RDF/Query/Federate.pm SHA1 2c712ae5134e9ec2f0edfbbd61bae7cac7799b2e lib/RDF/Query/Federate/Plan.pm SHA1 58b6c167d87520bba474f9b1c777944ab0e1665a lib/RDF/Query/Functions.pm SHA1 8e9a4e65dbf396c640c0e1d6d1f8fe10b902f07b lib/RDF/Query/Functions/Geo.pm SHA1 45a73f419b4f9c934dd90c9a6042235f6b148206 lib/RDF/Query/Functions/Jena.pm SHA1 8e51443bf2e1d689c4c509cc9200fd37613d44f7 lib/RDF/Query/Functions/Kasei.pm SHA1 7542010ae09e93d1b6b8138d1e03333246dbf927 lib/RDF/Query/Functions/SPARQL.pm SHA1 e11a608fc2a0a99829b6da5506e06ed878df382d lib/RDF/Query/Functions/Xpath.pm SHA1 3aa906dc40ca024c2774f4d7d24cf33f8904ab34 lib/RDF/Query/Node.pm SHA1 8a665851a34184b9fa8e7b679276f7547b7d9ad2 lib/RDF/Query/Node/Blank.pm SHA1 4b1815365ee400d79c93c2ee430608f670b092a7 lib/RDF/Query/Node/Literal.pm SHA1 6f711da8b250cd5c72d702eaac76f5b35e2639f1 lib/RDF/Query/Node/Resource.pm SHA1 50bd250905cf69c05d47926487416982f5b6e582 lib/RDF/Query/Node/Variable.pm SHA1 c099046b57a8a76ca77b288fa89e1996e5ebac8f lib/RDF/Query/Parser.pm SHA1 0a4627c5e4068c57483d2fbcb1b88d851a759a80 lib/RDF/Query/Parser/RDQL.pm SHA1 4dd94d886243841f9c9c46edf12325d020fdc887 lib/RDF/Query/Parser/SPARQL.pm SHA1 da845ef96b5a75dd51f7c83a503f8486f130241e lib/RDF/Query/Parser/SPARQL11.pm SHA1 c77c432ec8c0c313c0397d9903957e72294b57a8 lib/RDF/Query/Plan.pm SHA1 b84752b3335f4603703b1c0ccd0f9d60d121767c lib/RDF/Query/Plan/Aggregate.pm SHA1 34e7d2626c1f2f006647cf1a9d85281cbf514838 lib/RDF/Query/Plan/BasicGraphPattern.pm SHA1 748e060c79234c47c08ce4111979ee29214a5c87 lib/RDF/Query/Plan/Clear.pm SHA1 64d5d4a8782b49516205d046d11e2ec9f8a5f56b lib/RDF/Query/Plan/ComputedStatement.pm SHA1 5f0fc944b72dcdcaa8435fbc8f9c00d906c79b84 lib/RDF/Query/Plan/Constant.pm SHA1 abc0646b54407fcc59e6cb342d43c4ce2e704800 lib/RDF/Query/Plan/Construct.pm SHA1 76bb16ee3858f9b9a6cabad03c756f311424132e lib/RDF/Query/Plan/Copy.pm SHA1 eaed24793cad4b8bfd8289e9f07fa7ea6b00335b lib/RDF/Query/Plan/Distinct.pm SHA1 e3673df36957f6325b94c4dea7e7c5411aa58a2d lib/RDF/Query/Plan/Extend.pm SHA1 f4a774989472e4f59fd7072b07bc9354b3083e78 lib/RDF/Query/Plan/Filter.pm SHA1 b4df96e09a5d4be48707f16e6afeb2498d90c83f lib/RDF/Query/Plan/Iterator.pm SHA1 e4b64159f8c7f5b647a6fad9ddf1538378db2259 lib/RDF/Query/Plan/Join.pm SHA1 0b88c9a6c91e4b8dd3166aa96763005f724265e5 lib/RDF/Query/Plan/Join/NestedLoop.pm SHA1 4db21b532c059eede0f7c176221840ee4a4a50ae lib/RDF/Query/Plan/Join/PushDownNestedLoop.pm SHA1 ded01ac10a947572f22e85e7aac2ad07fd18f906 lib/RDF/Query/Plan/Limit.pm SHA1 0a5ff470efcf89e5d50f1fbafefe5e6ee4d4aa69 lib/RDF/Query/Plan/Load.pm SHA1 6e1e88541944cba357eafc5b6e165df769c97c17 lib/RDF/Query/Plan/Minus.pm SHA1 18bd05b4acc62a3816b76274c297aa4d16cdd764 lib/RDF/Query/Plan/Move.pm SHA1 d8724b2a5b435a0647891605f62ef3a85efe5e1d lib/RDF/Query/Plan/NamedGraph.pm SHA1 7ded67eecf2029da9aa390267241d836a8201c67 lib/RDF/Query/Plan/Offset.pm SHA1 e80a1e055ea8ce9f1f5592f92769435ffcaf2a26 lib/RDF/Query/Plan/Path.pm SHA1 05e51006c19b517755814786261955b644022ed7 lib/RDF/Query/Plan/Project.pm SHA1 cc7aae952d127f30c86a2a88f33b1e86dc8b69c2 lib/RDF/Query/Plan/Quad.pm SHA1 25e063047e81dac7006ba7f80cc93c7cd3051354 lib/RDF/Query/Plan/Sequence.pm SHA1 9c4127dcca0307bab90a29521cf15dc3c6e5c080 lib/RDF/Query/Plan/Service.pm SHA1 4be1a0d95dd41b06ca6989c36cc9eb7d54871b7d lib/RDF/Query/Plan/Sort.pm SHA1 d05ffe9137c3ddc904c182cfb2869bd6be4d6161 lib/RDF/Query/Plan/SubSelect.pm SHA1 266cb24b557dbe3b1da9a502409171f82a0e4eec lib/RDF/Query/Plan/ThresholdUnion.pm SHA1 a794535af1f260b104cf93082b7fdf180f0cc137 lib/RDF/Query/Plan/Triple.pm SHA1 c96d778e32d7bae7521cd2f05d24e8b085dd1ce5 lib/RDF/Query/Plan/Union.pm SHA1 b38f33be6555459a414e41e6c1436fbade5e84e8 lib/RDF/Query/Plan/Update.pm SHA1 e2d384516a4c7880da78e2fff9efeec88b26b73f lib/RDF/Query/ServiceDescription.pm SHA1 d01bfb165e60d66dee5d9bc6f2481dbcc0475cf3 lib/RDF/Query/Temporal.pm SHA1 4a21b8420c80e2845d9df7c479d644d0ca9db962 lib/RDF/Query/Util.pm SHA1 4b062200f3dbebc6bc4522e9ff95af3147fbbb99 lib/RDF/Query/VariableBindings.pm SHA1 bfe5703c947ea35d0c49cb2289983742b45c3800 t/00-simple.t SHA1 86971b58a6b23f8249221b7caf6bbd6ed5fa91f2 t/01-coverage.t SHA1 ab12d90f584b327ae456cbfd2e2507ffdc20e8e0 t/algebra-bgp.t SHA1 9669c898af95f3e1806aad07d98c4318ac8b8fb3 t/algebra-expr.t SHA1 72263c09ee8648df56193e74bd39eaca6da10889 t/algebra-func.t SHA1 fee8f2980898f59af2fe70c4ea9d6902c3de0ddd t/algebra-subsumption.t SHA1 83f66d3633030fa7e8bf7b111c0a3d8ca8f26a3b t/algebra.t SHA1 d6193b69cdacd9253427e557db9d586908429faa t/constraints.t SHA1 c401b58904f2d090ede11b435f4daadf1678a932 t/dataset-from-file.t SHA1 f18e5ad5a6f495088a391879ac3d0c6cd836ba1c t/dataset-from-net.t SHA1 4f9b6b628330df74fb18222c04e4dc2106eb7056 t/dev-computed-statements.t SHA1 bcca0b7492c687cc4876b098abf810d9f03cfc86 t/distance.js SHA1 19d0a171174be7da15190c4e6c09ccd178636a80 t/ext-select-expr.t SHA1 410da1d8056eccb53d4f08c6d0500d63b53a9077 t/filters.t SHA1 e1fccc14061a0f6dfca9e6ac833abaf1d2fe03b6 t/functions.rdf SHA1 2235b1eb33974c0cbae712db79eb93e2321f2b1c t/functions.t SHA1 a6254754bcedb09d0323a9e09de74bc4697bedda t/hooks.t SHA1 763863cb3df2ac2deb1e38644648e6063afb65c6 t/iterators.t SHA1 93d88c229b9c67a98b34cc00ddfe64c24bc463b7 t/literals.t SHA1 6522468ecacfb323374e04c15c49a3d17e14cb2f t/models.pl SHA1 713dd760d28c6c430e65394482127453c6400b6b t/named-graphs.t SHA1 25fdde34ce93015d8ce236125e8cb8086d7834b9 t/optional.t SHA1 ac5ee18f11cda06c07f97323348ede52dff743af t/plan.t SHA1 516c329ba6f8fbb48cc7cc394dab6398fc9778f1 t/protocol-serialization.t SHA1 18b363989532fc035682f1c22346154272f93caa t/queryform-ask.t SHA1 6544cb65886b29bb74435f7cb48f46ee8ee5521d t/queryform-construct.t SHA1 45f465af9a7ae6f9ccabb58699a2d9b3a0674306 t/queryform-describe.t SHA1 3e59655d685085cd381482d63602e9cdce67e1b2 t/rdql.t SHA1 e39a6763e233f9ac27a73575e224f94269ca9de2 t/resultforms.t SHA1 1082b74b807165ab66bf42ab9449705bd6f91d72 t/serialize.t SHA1 03182a941d101a6708aee8e9a9d98e12c265eb75 t/sha1.js SHA1 0a85e863f2e60959cb0060e6ac45f36b191f6ff9 t/sha1.js.asc SHA1 7b8b0601f4dfc117d824656e4bc4263dee5e9324 t/sparql11-aggregates.t SHA1 cb9fa42264a9b5099dc8888a748294c0351ff817 t/sparql11-federation.t SHA1 11788ae786b60eb7dfa1b4aba5f52c69a701e6a3 t/sparql11-negation.t SHA1 b3560bbea8467d790a70a0850a5d05ce5898e2d7 t/sparql11-propery_paths.t SHA1 564f67b96c442baf944da3b0b2ae0be84e5c54d9 t/sparql11-select_expressions.t SHA1 3aa6f2b3bf870a84dea274a9381ca763635259a2 t/sparql11-subselect.t SHA1 c81750cd3555e90da5dee62971aadea0e83209c0 t/sparql11-update.t SHA1 e270da56a8441264efe37bb100a32edbd7a31a3c t/streams.t.deprecated SHA1 0c9f58cb4dd695c37f1b13d579c769de8530e5d8 t/syntactic_forms.t SHA1 895d6dcdd3270c9372069c65c80bdff91efcf879 t/union.t SHA1 45b2d5319ae5a1f24f342b0862952f70c21bb7da xt/dawg-eval10.t SHA1 4b4fedcef50c49562b95d8a24a7771df47220065 xt/dawg-eval11.t SHA1 b9d5fa84687e4d40848a3da44fc4ff628ee0d69c xt/dawg-syntax11.t SHA1 e23eefde6260e859023f23c9d6cbb46431ff3233 xt/dev-service-description.t SHA1 4c478722ae3cbf2fc27d61f60d0175d8a632e96f xt/dev-sql-compiler.t SHA1 b49395bb932125e104252820b6c19f0cea86c196 xt/dev-time-intervals.t SHA1 754e7320cbb77a7140361ab31b58e25dfdeb6b6a xt/federate.t SHA1 cc0a48f12adb41b4bdbb837c08a34bb211ffe77c xt/parser-rdql.t SHA1 72e81513cd5bb4b3944af063e9d7fceebb8679d7 xt/parser-sparql.t SHA1 6c0741e96059e30ad81c97c00d25bd1a14d9eb0f xt/parser-sparql11.t SHA1 6387af7a5fde0747be5bc9ad2ec15ca9759bd95a xt/pod.t SHA1 9115f7ffe1933ad736dc292f3c89595b36ed6aa2 xt/pod_coverage.t SHA1 1f942d1e122fde92928c57d053ab0fb89c79761c xt/sparql11-federation.t -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin) Comment: GPGTools - http://gpgtools.org iEYEARECAAYFAlHtlJMACgkQhPK6VMqoyC1JXgCffBBFLfV5usolHXHI+QCL0+qT s28AoIEKNvjrB5cRtM7RlWWxKxnKZueA =HY9T -----END PGP SIGNATURE----- RDF-Query-2.910/t/000755 000765 000024 00000000000 12173312223 013571 5ustar00gregstaff000000 000000 RDF-Query-2.910/xt/000755 000765 000024 00000000000 12173312223 013761 5ustar00gregstaff000000 000000 RDF-Query-2.910/xt/dawg-eval10.t000755 000765 000024 00000066111 12065422135 016172 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Encode qw(encode); use URI::file; use Test::More; use File::Temp qw(tempfile); use Scalar::Util qw(blessed reftype); use Storable qw(dclone); use Algorithm::Combinatorics qw(permutations); use LWP::MediaTypes qw(add_type); use Text::CSV; use Regexp::Common qw /URI/; add_type( 'application/rdf+xml' => qw(rdf xrdf rdfx) ); add_type( 'text/turtle' => qw(ttl) ); add_type( 'text/plain' => qw(nt) ); add_type( 'text/x-nquads' => qw(nq) ); add_type( 'text/json' => qw(json) ); add_type( 'text/html' => qw(html xhtml htm) ); use RDF::Query; use RDF::Query::Node qw(iri blank literal variable); use RDF::Trine qw(statement); use RDF::Trine::Error qw(:try); use RDF::Trine::Graph; use RDF::Trine::Namespace qw(rdf rdfs xsd); use RDF::Trine::Iterator qw(smap); use RDF::Endpoint 0.05; use Carp; use HTTP::Request; use HTTP::Response; use HTTP::Message::PSGI; $RDF::Query::Plan::PLAN_CLASSES{'service'} = 'Test::RDF::Query::Plan::Service'; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.service = TRACE, Screen # # log4perl.category.rdf.query.plan.join.pushdownnestedloop = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ our $debug = 0; our $STRICT_APPROVAL = 0; if ($] < 5.007003) { plan skip_all => 'perl >= 5.7.3 required'; exit; } use Data::Dumper; require XML::Simple; plan qw(no_plan); require "xt/dawg/earl.pl"; my $PATTERN = ''; my %args; while (defined(my $opt = shift)) { if ($opt eq '-v') { $debug++; } elsif ($opt =~ /^-(.*)$/) { $args{ $1 } = 1; } else { $PATTERN = $opt; } } $ENV{RDFQUERY_THROW_ON_SERVICE} = 1; no warnings 'once'; if ($PATTERN) { # $debug = 1; } warn "PATTERN: ${PATTERN}\n" if ($PATTERN and $debug); my $model = RDF::Trine::Model->temporary_model; my @manifests = map { $_->as_string } map { URI::file->new_abs( $_ ) } map { glob( "xt/dawg/data-r2/$_/manifest.ttl" ) } qw( algebra ask basic bnode-coreference bound cast construct dataset distinct expr-builtin expr-equals expr-ops graph i18n open-world optional optional-filter reduced regex solution-seq sort triple-match type-promotion ); foreach my $file (@manifests) { warn "Parsing manifest $file\n" if $debug; RDF::Trine::Parser->parse_url_into_model( $file, $model, canonicalize => 1 ); } warn "done parsing manifests" if $debug; my $earl = init_earl( $model ); my $rs = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/result-set#'); my $mf = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#'); my $ut = RDF::Trine::Namespace->new('http://www.w3.org/2009/sparql/tests/test-update#'); my $rq = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-query#'); my $dawgt = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-dawg#'); { my @manifests = $model->subjects( $rdf->type, $mf->Manifest ); foreach my $m (@manifests) { warn "Manifest: " . $m->as_string . "\n" if ($debug); my ($list) = $model->objects( $m, $mf->entries ); unless (blessed($list)) { warn "No mf:entries found for manifest " . $m->as_string . "\n"; } my @tests = $model->get_list( $list ); foreach my $test (@tests) { my $et = $model->count_statements($test, $rdf->type, $mf->QueryEvaluationTest); my $ct = $model->count_statements($test, $rdf->type, $mf->CSVResultFormatTest); if ($et + $ct) { my ($name) = $model->objects( $test, $mf->name ); unless ($test->uri_value =~ /$PATTERN/) { next; } warn "### query eval test: " . $test->as_string . " >>> " . $name->literal_value . "\n" if ($debug); query_eval_test( $model, $test, $earl ); } if ($model->count_statements($test, $rdf->type, $ut->UpdateEvaluationTest) or $model->count_statements($test, $rdf->type, $mf->UpdateEvaluationTest)) { my ($name) = $model->objects( $test, $mf->name ); unless ($test->uri_value =~ /$PATTERN/) { next; } warn "### update eval test: " . $test->as_string . " >>> " . $name->literal_value . "\n" if ($debug); update_eval_test( $model, $test, $earl ); } } } } open( my $fh, '>', 'earl-eval-10.ttl' ) or die $!; print {$fh} earl_output( $earl ); close($fh); ################################################################################ sub update_eval_test { my $model = shift; my $test = shift; my $earl = shift; my ($action) = $model->objects( $test, $mf->action ); my ($result) = $model->objects( $test, $mf->result ); my ($req) = $model->objects( $test, $mf->requires ); my ($approved) = $model->objects( $test, $dawgt->approval ); my ($queryd) = $model->objects( $action, $ut->request ); my ($data) = $model->objects( $action, $ut->data ); my @gdata = $model->objects( $action, $ut->graphData ); if ($STRICT_APPROVAL) { unless ($approved) { warn "- skipping test because it isn't approved\n" if ($debug); return; } if ($approved->equal( $dawgt->NotClassified)) { warn "- skipping test because its approval is dawgt:NotClassified\n" if ($debug); return; } } my $uri = URI->new( $queryd->uri_value ); my $filename = $uri->file; my (undef,$base,undef) = File::Spec->splitpath( $filename ); $base = "file://${base}"; warn "Loading SPARQL query from file $filename" if ($debug); my $sparql = do { local($/) = undef; open(my $fh, '<', $filename) or do { fail("$!: $filename; " . $test->as_string); return }; binmode($fh, ':utf8'); <$fh> }; my $q = $sparql; $q =~ s/\s+/ /g; if ($debug) { warn "### test : " . $test->as_string . "\n"; warn "# sparql : $q\n"; warn "# data : " . $data->as_string . "\n" if (blessed($data)); warn "# graph data : " . $_->as_string . "\n" for (@gdata); warn "# result : " . $result->as_string . "\n"; warn "# requires : " . $req->as_string . "\n" if (blessed($req)); } print STDERR "constructing model... " if ($debug); my ($test_model) = RDF::Trine::Model->temporary_model; try { if (blessed($data)) { add_to_model( $test_model, $data->uri_value ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; } except { my $e = shift; die $e->text; } otherwise { warn '*** failed to construct model'; }; foreach my $gdata (@gdata) { my ($data) = ($model->objects( $gdata, $ut->data ))[0] || ($model->objects( $gdata, $ut->graph ))[0]; my ($graph) = $model->objects( $gdata, $rdfs->label ); my $uri = $graph->literal_value; try { warn "test data file: " . $data->uri_value . "\n" if ($debug); RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $test_model, context => RDF::Trine::Node::Resource->new($uri), canonicalize => 1 ); } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; }; } my ($result_status) = $model->objects( $result, $ut->result ); my @resgdata = $model->objects( $result, $ut->graphData ); my $expected_model = RDF::Trine::Model->temporary_model; my ($resdata) = $model->objects( $result, $ut->data ); try { if (blessed($resdata)) { RDF::Trine::Parser->parse_url_into_model( $resdata->uri_value, $expected_model, canonicalize => 1 ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; }; foreach my $gdata (@resgdata) { my ($data) = ($model->objects( $gdata, $ut->data ))[0] || ($model->objects( $gdata, $ut->graph ))[0]; my ($graph) = $model->objects( $gdata, $rdfs->label ); my $uri = $graph->literal_value; my $return = 0; if ($data) { try { warn "expected result data file: " . $data->uri_value . "\n" if ($debug); RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $expected_model, context => RDF::Trine::Node::Resource->new($uri), canonicalize => 1 ); } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; $return = 1; }; return if ($return); } } if ($debug) { warn "Dataset before update operation:\n"; warn $test_model->as_string; } my $ok = 0; eval { my $query = RDF::Query->new( $sparql, { lang => 'sparql11', update => 1, canonicalize => 1 } ); unless ($query) { warn 'Query error: ' . RDF::Query->error; fail($test->as_string); return; } my ($plan, $ctx) = $query->prepare( $test_model ); $query->execute_plan( $plan, $ctx ); my $test_graph = RDF::Trine::Graph->new( $test_model ); my $expected_graph = RDF::Trine::Graph->new( $expected_model ); my $eq = $test_graph->equals( $expected_graph ); $ok = is( $eq, 1, $test->as_string ); unless ($ok) { warn $test_graph->error; warn "Got model:\n" . $test_model->as_string; warn "Expected model:\n" . $expected_model->as_string; } }; if ($@ or not($ok)) { if ($@) { fail($test->as_string); } earl_fail_test( $earl, $test, $@ ); print "# failed: " . $test->as_string . "\n"; } else { earl_pass_test( $earl, $test ); } print STDERR "ok\n" if ($debug); } sub query_eval_test { my $model = shift; my $test = shift; my $earl = shift; my ($action) = $model->objects( $test, $mf->action ); my ($result) = $model->objects( $test, $mf->result ); my ($req) = $model->objects( $test, $mf->requires ); my ($approved) = $model->objects( $test, $dawgt->approval ); my ($queryd) = $model->objects( $action, $rq->query ); my ($data) = $model->objects( $action, $rq->data ); my @gdata = $model->objects( $action, $rq->graphData ); my @sdata = $model->objects( $action, $rq->serviceData ); if ($STRICT_APPROVAL) { unless ($approved) { warn "- skipping test because it isn't approved\n" if ($debug); return; } if ($approved->equal($dawgt->NotClassified)) { warn "- skipping test because its approval is dawgt:NotClassified\n" if ($debug); return; } } my $uri = URI->new( $queryd->uri_value ); my $filename = $uri->file; my (undef,$base,undef) = File::Spec->splitpath( $filename ); $base = "file://${base}"; warn "Loading SPARQL query from file $filename" if ($debug); my $sparql = do { local($/) = undef; open(my $fh, '<', $filename) or do { warn("$!: $filename; " . $test->as_string); return }; binmode($fh, ':utf8'); <$fh> }; my $q = $sparql; $q =~ s/\s+/ /g; if ($debug) { warn "### test : " . $test->as_string . "\n"; warn "# sparql : $q\n"; warn "# data : " . $data->as_string if (blessed($data)); warn "# graph data : " . $_->as_string for (@gdata); warn "# result : " . $result->as_string; warn "# requires : " . $req->as_string if (blessed($req)); } # warn 'service data: ' . Dumper(\@sdata); foreach my $sd (@sdata) { my ($url) = $model->objects( $sd, $rq->endpoint ); print STDERR "setting up remote endpoint $url...\n" if ($debug); my ($data) = $model->objects( $sd, $rq->data ); my @gdata = $model->objects( $sd, $rq->graphData ); if ($debug) { warn "- data : " . $data->as_string if (blessed($data)); warn "- graph data : " . $_->as_string for (@gdata); } my $model = RDF::Trine::Model->new(); if ($data) { RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $model ); } $Test::RDF::Query::Plan::Service::service_ctx{ $url->uri_value } = $model; } print STDERR "constructing model... " if ($debug); my ($test_model) = RDF::Trine::Model->temporary_model; try { if (blessed($data)) { add_to_model( $test_model, $data->uri_value ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; } except { my $e = shift; die $e->text; } otherwise { warn '*** failed to construct model'; }; print STDERR "ok\n" if ($debug); my $resuri = URI->new( $result->uri_value ); my $resfilename = $resuri->file; TODO: { local($TODO) = (blessed($req)) ? "requires " . $req->as_string : ''; my $comment; my $ok = eval { if ($debug) { my $q = $sparql; $q =~ s/([\x{256}-\x{1000}])/'\x{' . sprintf('%x', ord($1)) . '}'/eg; warn $q; } print STDERR "getting actual results... " if ($debug); my ($actual, $type) = get_actual_results( $test_model, $sparql, $base, @gdata ); print STDERR "ok\n" if ($debug); print STDERR "getting expected results... " if ($debug); my $expected = get_expected_results( $resfilename, $type ); print STDERR "ok\n" if ($debug); # warn "comparing results..."; compare_results( $expected, $actual, $earl, $test->as_string, \$comment ); }; warn $@ if ($@); if ($ok) { earl_pass_test( $earl, $test ); } else { earl_fail_test( $earl, $test, $comment ); print "# failed: " . $test->as_string . "\n"; } } } exit; ###################################################################### sub add_to_model { my $model = shift; my @files = @_; foreach my $file (@files) { try { RDF::Trine::Parser->parse_url_into_model( $file, $model, canonicalize => 1 ); } catch Error with { my $e = shift; warn "Failed to load $file into model: " . $e->text; }; } } sub get_actual_results { my $model = shift; my $sparql = shift; my $base = shift; my @gdata = @_; my $query = RDF::Query->new( $sparql, { base => $base, lang => 'sparql10', load_data => 1, canonicalize => 1 } ); unless ($query) { warn RDF::Query->error if ($debug or $PATTERN); return; } my $testns = RDF::Trine::Namespace->new('http://example.com/test-results#'); my $rmodel = RDF::Trine::Model->temporary_model; my ($plan, $ctx) = $query->prepare_with_named_graphs( $model, @gdata ); if ($args{plan}) { warn $plan->explain(' ', 0); } my $results = $query->execute_plan( $plan, $ctx ); if ($args{ results }) { $results = $results->materialize; warn "Actual results:\n"; warn $results->as_string; } if ($results->is_bindings) { return (binding_results_data( $results ), 'bindings'); } elsif ($results->is_boolean) { $rmodel->add_statement( statement( $testns->result, $testns->boolean, literal(($results->get_boolean ? 'true' : 'false'), undef, $xsd->boolean) ) ); return ($rmodel->get_statements, 'boolean'); } elsif ($results->is_graph) { return ($results, 'graph'); } else { warn "unknown result type: " . Dumper($results); } } sub get_expected_results { my $file = shift; my $type = shift; my $testns = RDF::Trine::Namespace->new('http://example.com/test-results#'); if ($type eq 'graph') { my $model = RDF::Trine::Model->temporary_model; RDF::Trine::Parser->parse_url_into_model( "file://$file", $model, canonicalize => 1 ); my $results = $model->get_statements(); if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return $results; } elsif ($file =~ /[.](srj|json)/) { my $model = RDF::Trine::Model->temporary_model; my $data = do { local($/) = undef; open(my $fh, '<', $file) or die $!; binmode($fh, ':utf8'); <$fh> }; my $results = RDF::Trine::Iterator->from_json( $data, { canonicalize => 1 } ); if ($results->isa('RDF::Trine::Iterator::Boolean')) { my $value = $results->next; my $bool = ($value ? 'true' : 'false'); $model->add_statement( statement( $testns->result, $testns->boolean, literal($bool, undef, $xsd->boolean) ) ); if ($args{ results }) { warn "Expected result: $bool\n"; } return $model->get_statements; } else { if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return binding_results_data( $results ); } } elsif ($file =~ /[.]srx/) { my $model = RDF::Trine::Model->temporary_model; my $data = do { local($/) = undef; open(my $fh, '<', $file) or die $!; binmode($fh, ':utf8'); <$fh> }; my $results = RDF::Trine::Iterator->from_string( $data, { canonicalize => 1 } ); if ($results->isa('RDF::Trine::Iterator::Boolean')) { $model->add_statement( statement( $testns->result, $testns->boolean, literal(($results->next ? 'true' : 'false'), undef, $xsd->boolean) ) ); return $model->get_statements; } else { if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return binding_results_data( $results ); } } elsif ($file =~ /[.]csv/) { my $csv = Text::CSV->new({binary => 1}); open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $header = $csv->getline($fh); my @vars = @$header; my @data; while (my $row = $csv->getline($fh)) { my %result; foreach my $i (0 .. $#vars) { my $var = $vars[$i]; my $value = $row->[ $i ]; # XXX @@ heuristics that won't always work. # XXX @@ expected to work on the test suite, though if ($value =~ /^_:(\w+)$/) { $value = blank($1); } elsif ($value =~ /$RE{URI}/) { $value = iri($value); } elsif (defined($value) and length($value)) { $value = literal($value); } $result{ $var } = $value; } push(@data, \%result); } if ($args{ results }) { warn "Expected results:\n"; warn Dumper(\@data); } return \@data; } elsif ($file =~ /[.]tsv/) { open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $header = <$fh>; chomp($header); my @vars = split("\t", $header); foreach (@vars) { s/[?]// } my @data; my $parser = RDF::Trine::Parser::Turtle->new(); while (defined(my $line = <$fh>)) { chomp($line); my $row = [ split("\t", $line) ]; my %result; foreach my $i (0 .. $#vars) { my $var = $vars[$i]; my $value = $row->[ $i ]; my $node = length($value) ? $parser->parse_node( $value ) : undef; $result{ $var } = $node; } push(@data, RDF::Query::VariableBindings->new( \%result )); } my $iter = RDF::Trine::Iterator::Bindings->new(\@data); return binding_results_data($iter); } elsif ($file =~ /[.](ttl|rdf)/) { my $model = RDF::Trine::Model->new(); open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $base = 'file://' . File::Spec->rel2abs($file); my $parser = RDF::Trine::Parser->new(($file =~ /[.]ttl/) ? 'turtle' : 'rdfxml'); $parser->parse_file_into_model( $base, $file, $model ); my ($res) = $model->subjects( $rdf->type, $rs->ResultSet ); if (my($b) = $model->objects( $res, $rs->boolean )) { my $bool = $b->literal_value; my $rmodel = RDF::Trine::Model->new(); $rmodel->add_statement( statement( $testns->result, $testns->boolean, literal($bool, undef, $xsd->boolean) ) ); if ($args{ results }) { warn "Expected result: $bool\n"; } return $rmodel->get_statements; } else { my @vars = $model->objects( $res, $rs->resultVariable ); my @sols = $model->objects( $res, $rs->solution ); my @names = map { $_->literal_value } @vars; my @bindings; foreach my $r (@sols) { my %data; my @b = $model->objects( $r, $rs->binding ); foreach my $b (@b) { my ($value) = $model->objects( $b, $rs->value ); my ($var) = $model->objects( $b, $rs->variable ); $data{ $var->literal_value } = $value; } push(@bindings, RDF::Trine::VariableBindings->new( \%data )); } my $iter = RDF::Trine::Iterator::Bindings->new( \@bindings, \@names ); if ($args{ results }) { $iter = $iter->materialize; warn "Got expected results:\n"; warn $iter->as_string; } return binding_results_data($iter); } } else { die "Unrecognized type of expected results: $file"; } } sub compare_results { my $expected = shift; my $actual = shift; my $earl = shift; my $test = shift; my $comment = shift || do { my $foo; \$foo }; my $TODO = shift; my $lossy_cmp = 0; if (reftype($expected) eq 'ARRAY') { # comparison with expected results coming from a lossy format like csv/tsv $lossy_cmp = 1; my %data = (results => [], blank_identifiers => {}); foreach my $row (@$expected) { push(@{ $data{ results } }, $row ); foreach my $key (keys %$row) { my $node = $row->{$key}; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { $data{ blank_identifiers }{ $node->blank_identifier }++; } } } $data{ blanks } = scalar(@{ [ keys %{ $data{ blank_identifiers } } ] }); $expected = \%data; } if (not(ref($actual))) { my $ok = is( $actual, $expected, $test ); return $ok; } elsif (blessed($actual) and $actual->isa('RDF::Trine::Iterator::Graph')) { die "Unexpected Graph result type (was expecting " . ref($expected) . ")" unless (blessed($expected) and $expected->isa('RDF::Trine::Iterator::Graph')); my $act_graph = RDF::Trine::Graph->new( $actual ); my $exp_graph = RDF::Trine::Graph->new( $expected ); # local($debug) = 1 if ($PATTERN); if ($debug) { warn ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"; my $actualxml = $act_graph->get_statements->as_string; warn $actualxml; warn "-------------------------------\n"; my $expectxml = $exp_graph->get_statements->as_string; warn $expectxml; warn "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"; } my $eq = $act_graph->equals( $exp_graph ); unless ($eq) { warn $act_graph->error; } return is( $eq, 1, $test ); } elsif (reftype($actual) eq 'HASH' and reftype($expected) eq 'HASH') { my @aresults = @{ $actual->{ results } }; my @eresults = @{ $expected->{ results } }; my $acount = scalar(@aresults); my $ecount = scalar(@eresults); if ($acount != $ecount) { warn "Result count ($acount) didn't match expected ($ecount)" if ($debug); return fail($test); } # warn Data::Dumper->Dump([\@aresults, \@eresults], [qw(actual expected)]); my ($awith, $awithout) = split_results_with_blank_nodes( @aresults ); my ($ewith, $ewithout) = split_results_with_blank_nodes( @eresults ); # for the results without blanks, just serialize, sort, and compare my @astrings = sort map { result_to_string($_, $lossy_cmp) } @$awithout; my @estrings = sort map { result_to_string($_, $lossy_cmp) } @$ewithout; if ($actual->{ blanks } == 0 and $expected->{ blanks } == 0) { return is_deeply( \@astrings, \@estrings, $test ); } elsif (join("\xFF", @astrings) ne join("\xFF", @estrings)) { warn "triples don't match: " . Dumper(\@astrings, \@estrings); return fail($test); } # compare the results with bnodes my @ka = keys %{ $actual->{blank_identifiers} }; my @kb = keys %{ $expected->{blank_identifiers} }; my $kbp = permutations( \@kb ); MAPPING: while (my $mapping = $kbp->next) { my %mapping; @mapping{ @ka } = @$mapping; warn "trying mapping: " . Dumper(\%mapping) if ($debug); my %ewith = map { result_to_string($_, $lossy_cmp) => 1 } @$ewith; foreach my $row (@$awith) { my %row; foreach my $k (keys %$row) { my $n = $row->{ $k }; next unless (blessed($n)); if ($n->isa('RDF::Trine::Node::Blank')) { my $id = $mapping{ $n->blank_identifier }; warn "mapping " . $n->blank_identifier . " to $id\n" if ($debug); $row{ $k } = RDF::Trine::Node::Blank->new( $id ); } else { $row{ $k } = $n; } } my $mapped_row = result_to_string( RDF::Query::VariableBindings->new( \%row ), $lossy_cmp ); warn "checking for '$mapped_row' in " . Dumper(\%ewith) if ($debug); if ($ewith{ $mapped_row }) { delete $ewith{ $mapped_row }; } else { next MAPPING; } } warn "found mapping: " . Dumper(\%mapping) if ($debug); return pass($test); } warn "failed to find bnode mapping: " . Dumper($awith, $ewith); return fail($test); } else { die "Failed to compare actual and expected results: " . Dumper($actual, $expected); } } sub binding_results_data { my $iter = shift; my %data = (results => [], blank_identifiers => {}); while (my $row = $iter->next) { push(@{ $data{ results } }, $row ); foreach my $key (keys %$row) { my $node = $row->{$key}; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { $data{ blank_identifiers }{ $node->blank_identifier }++; } } } $data{ blanks } = scalar(@{ [ keys %{ $data{ blank_identifiers } } ] }); return \%data; } sub split_results_with_blank_nodes { my (@with, @without); ROW: foreach my $row (@_) { my @keys = grep { ref($row->{ $_ }) } keys %$row; foreach my $k (@keys) { my $node = $row->{ $k }; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { push(@with, $row); next ROW; } } push(@without, $row); } return (\@with, \@without); } sub result_to_string { my $row = shift; my $lossy_cmp = shift; my @keys = grep { ref($row->{ $_ }) } keys %$row; my @results; foreach my $k (@keys) { my $node = $row->{ $k }; if ($node->isa('RDF::Trine::Node::Literal') and $node->has_datatype) { my ($value, $dt); if ($lossy_cmp) { $value = $node->literal_value; $dt = undef; } else { $value = RDF::Trine::Node::Literal->canonicalize_literal_value( $node->literal_value, $node->literal_datatype ); $dt = $node->literal_datatype; } $node = RDF::Query::Node::Literal->new( $value, undef, $dt ); } push(@results, join('=', $k, $node->as_string)); } return join(',', sort(@results)); } package Test::RDF::Query::Plan::Service; use strict; use warnings; use Data::Dumper; use Scalar::Util qw(refaddr); use base qw(RDF::Query::Plan::Service); our %ENDPOINTS; our %service_ctx; sub new { my $class = shift; my $endpoint = shift; my $plan = shift; my $silent = shift; my $sparql = shift; if ($endpoint->isa('RDF::Query::Node::Resource')) { my $uri = $endpoint->uri_value; warn "setting up mock endpoint for $uri" if ($debug); } my $self = $class->SUPER::new( $endpoint, $plan, $silent, $sparql, @_ ); if ($endpoint->isa('RDF::Query::Node::Resource')) { my $uri = $endpoint->uri_value; my $e = URI->new($uri); my $model = $service_ctx{ $uri }; # warn "model for $uri: $model"; if ($model) { my $end = RDF::Endpoint->new( $model, { endpoint => { endpoint_path => $e->path } } ); $ENDPOINTS{ refaddr($self) } = $end; } } return $self; } # sub mock { # my $self = shift; # return; # my $endpoint = shift; # my $data = shift; # my $e = URI->new($endpoint); # # my $model = RDF::Trine::Model->new(); # my ($default, $named) = @$data; # if ($default) { # RDF::Trine::Parser->parse_url_into_model( $default->uri_value, $model ); # my $end = RDF::Endpoint->new( $model, { endpoint => { endpoint_path => $e->path } } ); # $ENDPOINTS{ refaddr($self) } = $end; # } # } sub _request { my $self = shift; my $ua = shift; my $req = shift; my $env = $req->to_psgi; my $end = $ENDPOINTS{ refaddr($self) }; if ($end) { # warn "got mocked endpoint"; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $resp = $end->run( $req ); return $resp->finalize; }; my $data = $app->( $env ); my $resp = HTTP::Response->from_psgi( $data ); return $resp; } else { # warn "no mocked endpoint available"; return HTTP::Response->new(403); } } sub DESTROY { my $self = shift; delete $ENDPOINTS{ refaddr($self) }; $self->SUPER::DESTROY(); } RDF-Query-2.910/xt/dawg-eval11.t000755 000765 000024 00000066062 12171745553 016212 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Encode qw(encode); use URI::file; use Test::More; use File::Temp qw(tempfile); use Scalar::Util qw(blessed reftype); use Storable qw(dclone); use Algorithm::Combinatorics qw(permutations); use LWP::MediaTypes qw(add_type); use Text::CSV; use Regexp::Common qw /URI/; add_type( 'application/rdf+xml' => qw(rdf xrdf rdfx) ); add_type( 'text/turtle' => qw(ttl) ); add_type( 'text/plain' => qw(nt) ); add_type( 'text/x-nquads' => qw(nq) ); add_type( 'text/json' => qw(json) ); add_type( 'text/html' => qw(html xhtml htm) ); use RDF::Query; use RDF::Query::Node qw(iri blank literal variable); use RDF::Trine qw(statement); use RDF::Trine::Error qw(:try); use RDF::Trine::Graph; use RDF::Trine::Namespace qw(rdf rdfs xsd); use RDF::Trine::Iterator qw(smap); use RDF::Endpoint 0.05; use Carp; use HTTP::Request; use HTTP::Response; use HTTP::Message::PSGI; $RDF::Query::Plan::PLAN_CLASSES{'service'} = 'Test::RDF::Query::Plan::Service'; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.service = TRACE, Screen # # log4perl.category.rdf.query.plan.join.pushdownnestedloop = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ our $debug = 0; our $STRICT_APPROVAL = 0; if ($] < 5.007003) { plan skip_all => 'perl >= 5.7.3 required'; exit; } use Data::Dumper; require XML::Simple; plan qw(no_plan); require "xt/dawg/earl.pl"; my $PATTERN = ''; my %args; while (defined(my $opt = shift)) { if ($opt eq '-v') { $debug++; } elsif ($opt =~ /^-(.*)$/) { $args{ $1 } = 1; } else { $PATTERN = $opt; } } $ENV{RDFQUERY_THROW_ON_SERVICE} = 1; no warnings 'once'; if ($PATTERN) { # $debug = 1; } warn "PATTERN: ${PATTERN}\n" if ($PATTERN and $debug); my $model = RDF::Trine::Model->temporary_model; my @manifests = map { $_->as_string } map { URI::file->new_abs( $_ ) } map { glob( "xt/dawg11/$_/manifest.ttl" ) } qw( add aggregates basic-update bind bindings clear construct copy csv-tsv-res delete delete-data delete-insert delete-where drop exists functions grouping json-res move negation project-expression property-path service subquery update-silent ); foreach my $file (@manifests) { warn "Parsing manifest $file\n" if $debug; RDF::Trine::Parser->parse_url_into_model( $file, $model, canonicalize => 1 ); } warn "done parsing manifests" if $debug; my $earl = init_earl( $model ); my $rs = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/result-set#'); my $mf = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#'); my $ut = RDF::Trine::Namespace->new('http://www.w3.org/2009/sparql/tests/test-update#'); my $rq = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-query#'); my $dawgt = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-dawg#'); { my @manifests = $model->subjects( $rdf->type, $mf->Manifest ); foreach my $m (@manifests) { warn "Manifest: " . $m->as_string . "\n" if ($debug); my ($list) = $model->objects( $m, $mf->entries ); unless (blessed($list)) { warn "No mf:entries found for manifest " . $m->as_string . "\n"; } my @tests = $model->get_list( $list ); foreach my $test (@tests) { my $et = $model->count_statements($test, $rdf->type, $mf->QueryEvaluationTest); my $ct = $model->count_statements($test, $rdf->type, $mf->CSVResultFormatTest); if ($et + $ct) { my ($name) = $model->objects( $test, $mf->name ); unless ($test->uri_value =~ /$PATTERN/) { next; } warn "### query eval test: " . $test->as_string . " >>> " . $name->literal_value . "\n" if ($debug); query_eval_test( $model, $test, $earl ); } if ($model->count_statements($test, $rdf->type, $ut->UpdateEvaluationTest) or $model->count_statements($test, $rdf->type, $mf->UpdateEvaluationTest)) { my ($name) = $model->objects( $test, $mf->name ); unless ($test->uri_value =~ /$PATTERN/) { next; } warn "### update eval test: " . $test->as_string . " >>> " . $name->literal_value . "\n" if ($debug); update_eval_test( $model, $test, $earl ); } } } } open( my $fh, '>', 'earl-eval-11.ttl' ) or die $!; print {$fh} earl_output( $earl ); close($fh); ################################################################################ sub update_eval_test { my $model = shift; my $test = shift; my $earl = shift; my ($action) = $model->objects( $test, $mf->action ); my ($result) = $model->objects( $test, $mf->result ); my ($req) = $model->objects( $test, $mf->requires ); my ($approved) = $model->objects( $test, $dawgt->approval ); my ($queryd) = $model->objects( $action, $ut->request ); my ($data) = $model->objects( $action, $ut->data ); my @gdata = $model->objects( $action, $ut->graphData ); if ($STRICT_APPROVAL) { unless ($approved) { warn "- skipping test because it isn't approved\n" if ($debug); return; } if ($approved->equal( $dawgt->NotClassified)) { warn "- skipping test because its approval is dawgt:NotClassified\n" if ($debug); return; } } my $uri = URI->new( $queryd->uri_value ); my $filename = $uri->file; my (undef,$base,undef) = File::Spec->splitpath( $filename ); $base = "file://${base}"; warn "Loading SPARQL query from file $filename" if ($debug); my $sparql = do { local($/) = undef; open(my $fh, '<', $filename) or do { fail("$!: $filename; " . $test->as_string); return }; binmode($fh, ':utf8'); <$fh> }; my $q = $sparql; $q =~ s/\s+/ /g; if ($debug) { warn "### test : " . $test->as_string . "\n"; warn "# sparql : $q\n"; warn "# data : " . $data->as_string . "\n" if (blessed($data)); warn "# graph data : " . $_->as_string . "\n" for (@gdata); warn "# result : " . $result->as_string . "\n"; warn "# requires : " . $req->as_string . "\n" if (blessed($req)); } print STDERR "constructing model... " if ($debug); my ($test_model) = RDF::Trine::Model->temporary_model; try { if (blessed($data)) { add_to_model( $test_model, $data->uri_value ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; } except { my $e = shift; die $e->text; } otherwise { warn '*** failed to construct model'; }; foreach my $gdata (@gdata) { my ($data) = ($model->objects( $gdata, $ut->data ))[0] || ($model->objects( $gdata, $ut->graph ))[0]; my ($graph) = $model->objects( $gdata, $rdfs->label ); my $uri = $graph->literal_value; try { warn "test data file: " . $data->uri_value . "\n" if ($debug); RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $test_model, context => RDF::Trine::Node::Resource->new($uri), canonicalize => 1 ); } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; }; } my ($result_status) = $model->objects( $result, $ut->result ); my @resgdata = $model->objects( $result, $ut->graphData ); my $expected_model = RDF::Trine::Model->temporary_model; my ($resdata) = $model->objects( $result, $ut->data ); try { if (blessed($resdata)) { RDF::Trine::Parser->parse_url_into_model( $resdata->uri_value, $expected_model, canonicalize => 1 ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; }; foreach my $gdata (@resgdata) { my ($data) = ($model->objects( $gdata, $ut->data ))[0] || ($model->objects( $gdata, $ut->graph ))[0]; my ($graph) = $model->objects( $gdata, $rdfs->label ); my $uri = $graph->literal_value; my $return = 0; if ($data) { try { warn "expected result data file: " . $data->uri_value . "\n" if ($debug); RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $expected_model, context => RDF::Trine::Node::Resource->new($uri), canonicalize => 1 ); } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; $return = 1; }; return if ($return); } } if ($debug) { warn "Dataset before update operation:\n"; warn $test_model->as_string; } my $ok = 0; eval { my $query = RDF::Query->new( $sparql, { lang => 'sparql11', update => 1 } ); unless ($query) { warn 'Query error: ' . RDF::Query->error; fail($test->as_string); return; } my ($plan, $ctx) = $query->prepare( $test_model ); $query->execute_plan( $plan, $ctx ); my $test_graph = RDF::Trine::Graph->new( $test_model ); my $expected_graph = RDF::Trine::Graph->new( $expected_model ); my $eq = $test_graph->equals( $expected_graph ); $ok = is( $eq, 1, $test->as_string ); unless ($ok) { warn $test_graph->error; warn "Got model:\n" . $test_model->as_string; warn "Expected model:\n" . $expected_model->as_string; } }; if ($@ or not($ok)) { if ($@) { fail($test->as_string); } earl_fail_test( $earl, $test, $@ ); print "# failed: " . $test->as_string . "\n"; } else { earl_pass_test( $earl, $test ); } print STDERR "ok\n" if ($debug); } sub query_eval_test { my $model = shift; my $test = shift; my $earl = shift; my ($action) = $model->objects( $test, $mf->action ); my ($result) = $model->objects( $test, $mf->result ); my ($req) = $model->objects( $test, $mf->requires ); my ($approved) = $model->objects( $test, $dawgt->approval ); my ($queryd) = $model->objects( $action, $rq->query ); my ($data) = $model->objects( $action, $rq->data ); my @gdata = $model->objects( $action, $rq->graphData ); my @sdata = $model->objects( $action, $rq->serviceData ); if ($STRICT_APPROVAL) { unless ($approved) { warn "- skipping test because it isn't approved\n" if ($debug); return; } if ($approved->equal($dawgt->NotClassified)) { warn "- skipping test because its approval is dawgt:NotClassified\n" if ($debug); return; } } my $uri = URI->new( $queryd->uri_value ); my $filename = $uri->file; my (undef,$base,undef) = File::Spec->splitpath( $filename ); $base = "file://${base}"; warn "Loading SPARQL query from file $filename" if ($debug); my $sparql = do { local($/) = undef; open(my $fh, '<', $filename) or do { warn("$!: $filename; " . $test->as_string); return }; binmode($fh, ':utf8'); <$fh> }; my $q = $sparql; $q =~ s/\s+/ /g; if ($debug) { warn "### test : " . $test->as_string . "\n"; warn "# sparql : $q\n"; warn "# data : " . $data->as_string if (blessed($data)); warn "# graph data : " . $_->as_string for (@gdata); warn "# result : " . $result->as_string; warn "# requires : " . $req->as_string if (blessed($req)); } # warn 'service data: ' . Dumper(\@sdata); foreach my $sd (@sdata) { my ($url) = $model->objects( $sd, $rq->endpoint ); print STDERR "setting up remote endpoint $url...\n" if ($debug); my ($data) = $model->objects( $sd, $rq->data ); my @gdata = $model->objects( $sd, $rq->graphData ); if ($debug) { warn "- data : " . $data->as_string if (blessed($data)); warn "- graph data : " . $_->as_string for (@gdata); } my $model = RDF::Trine::Model->new(); if ($data) { RDF::Trine::Parser->parse_url_into_model( $data->uri_value, $model ); } $Test::RDF::Query::Plan::Service::service_ctx{ $url->uri_value } = $model; } print STDERR "constructing model... " if ($debug); my ($test_model) = RDF::Trine::Model->temporary_model; try { if (blessed($data)) { add_to_model( $test_model, $data->uri_value ); } } catch Error with { my $e = shift; fail($test->as_string); earl_fail_test( $earl, $test, $e->text ); print "# died: " . $test->as_string . ": $e\n"; return; } except { my $e = shift; die $e->text; } otherwise { warn '*** failed to construct model'; }; print STDERR "ok\n" if ($debug); my $resuri = URI->new( $result->uri_value ); my $resfilename = $resuri->file; TODO: { local($TODO) = (blessed($req)) ? "requires " . $req->as_string : ''; my $comment; my $ok = eval { if ($debug) { my $q = $sparql; $q =~ s/([\x{256}-\x{1000}])/'\x{' . sprintf('%x', ord($1)) . '}'/eg; warn $q; } print STDERR "getting actual results... " if ($debug); my ($actual, $type) = get_actual_results( $test_model, $sparql, $base, @gdata ); print STDERR "ok\n" if ($debug); print STDERR "getting expected results... " if ($debug); my $expected = get_expected_results( $resfilename, $type ); print STDERR "ok\n" if ($debug); # warn "comparing results..."; compare_results( $expected, $actual, $earl, $test->as_string, \$comment ); }; warn $@ if ($@); if ($ok) { earl_pass_test( $earl, $test ); } else { earl_fail_test( $earl, $test, $comment ); print "# failed: " . $test->as_string . "\n"; } } } exit; ###################################################################### sub add_to_model { my $model = shift; my @files = @_; foreach my $file (@files) { try { RDF::Trine::Parser->parse_url_into_model( $file, $model, canonicalize => 1 ); } catch Error with { my $e = shift; warn "Failed to load $file into model: " . $e->text; }; } } sub get_actual_results { my $model = shift; my $sparql = shift; my $base = shift; my @gdata = @_; my $query = RDF::Query->new( $sparql, { base => $base, lang => 'sparql11', load_data => 1 } ); unless ($query) { warn RDF::Query->error if ($debug or $PATTERN); return; } my $testns = RDF::Trine::Namespace->new('http://example.com/test-results#'); my $rmodel = RDF::Trine::Model->temporary_model; my ($plan, $ctx) = $query->prepare_with_named_graphs( $model, @gdata ); if ($args{plan}) { warn $plan->explain(' ', 0); } my $results = $query->execute_plan( $plan, $ctx ); if ($args{ results }) { $results = $results->materialize; warn "Actual results:\n"; warn $results->as_string; } if ($results->is_bindings) { return (binding_results_data( $results ), 'bindings'); } elsif ($results->is_boolean) { $rmodel->add_statement( statement( $testns->result, $testns->boolean, literal(($results->get_boolean ? 'true' : 'false'), undef, $xsd->boolean) ) ); return ($rmodel->get_statements, 'boolean'); } elsif ($results->is_graph) { return ($results, 'graph'); } else { warn "unknown result type: " . Dumper($results); } } sub get_expected_results { my $file = shift; my $type = shift; my $testns = RDF::Trine::Namespace->new('http://example.com/test-results#'); if ($type eq 'graph') { my $model = RDF::Trine::Model->temporary_model; RDF::Trine::Parser->parse_url_into_model( "file://$file", $model, canonicalize => 1 ); my $results = $model->get_statements(); if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return $results; } elsif ($file =~ /[.](srj|json)/) { my $model = RDF::Trine::Model->temporary_model; my $data = do { local($/) = undef; open(my $fh, '<', $file) or die $!; binmode($fh, ':utf8'); <$fh> }; my $results = RDF::Trine::Iterator->from_json( $data, { canonicalize => 1 } ); if ($results->isa('RDF::Trine::Iterator::Boolean')) { my $value = $results->next; my $bool = ($value ? 'true' : 'false'); $model->add_statement( statement( $testns->result, $testns->boolean, literal($bool, undef, $xsd->boolean) ) ); if ($args{ results }) { warn "Expected result: $bool\n"; } return $model->get_statements; } else { if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return binding_results_data( $results ); } } elsif ($file =~ /[.]srx/) { my $model = RDF::Trine::Model->temporary_model; my $data = do { local($/) = undef; open(my $fh, '<:encoding(UTF-8)', $file) or die $!; <$fh> }; my $results = RDF::Trine::Iterator->from_string( $data, { canonicalize => 1 } ); if ($results->isa('RDF::Trine::Iterator::Boolean')) { $model->add_statement( statement( $testns->result, $testns->boolean, literal(($results->next ? 'true' : 'false'), undef, $xsd->boolean) ) ); return $model->get_statements; } else { if ($args{ results }) { $results = $results->materialize; warn "Expected results:\n"; warn $results->as_string; } return binding_results_data( $results ); } } elsif ($file =~ /[.]csv/) { my $csv = Text::CSV->new({binary => 1}); open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $header = $csv->getline($fh); my @vars = @$header; my @data; while (my $row = $csv->getline($fh)) { my %result; foreach my $i (0 .. $#vars) { my $var = $vars[$i]; my $value = $row->[ $i ]; # XXX @@ heuristics that won't always work. # XXX @@ expected to work on the test suite, though if ($value =~ /^_:(\w+)$/) { $value = blank($1); } elsif ($value =~ /$RE{URI}/) { $value = iri($value); } elsif (defined($value) and length($value)) { $value = literal($value); } $result{ $var } = $value; } push(@data, \%result); } if ($args{ results }) { warn "Expected results:\n"; warn Dumper(\@data); } return \@data; } elsif ($file =~ /[.]tsv/) { open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $header = <$fh>; chomp($header); my @vars = split("\t", $header); foreach (@vars) { s/[?]// } my @data; my $parser = RDF::Trine::Parser::Turtle->new(); while (defined(my $line = <$fh>)) { chomp($line); my $row = [ split("\t", $line) ]; my %result; foreach my $i (0 .. $#vars) { my $var = $vars[$i]; my $value = $row->[ $i ]; my $node = length($value) ? $parser->parse_node( $value ) : undef; $result{ $var } = $node; } push(@data, RDF::Query::VariableBindings->new( \%result )); } my $iter = RDF::Trine::Iterator::Bindings->new(\@data); return binding_results_data($iter); } elsif ($file =~ /[.](ttl|rdf)/) { my $model = RDF::Trine::Model->new(); open( my $fh, "<:encoding(utf8)", $file ) or die $!; my $base = 'file://' . File::Spec->rel2abs($file); my $parser = RDF::Trine::Parser->new(($file =~ /[.]ttl/) ? 'turtle' : 'rdfxml'); $parser->parse_file_into_model( $base, $file, $model ); my ($res) = $model->subjects( $rdf->type, $rs->ResultSet ); if (my($b) = $model->objects( $res, $rs->boolean )) { my $bool = $b->literal_value; my $rmodel = RDF::Trine::Model->new(); $rmodel->add_statement( statement( $testns->result, $testns->boolean, literal($bool, undef, $xsd->boolean) ) ); if ($args{ results }) { warn "Expected result: $bool\n"; } return $rmodel->get_statements; } else { my @vars = $model->objects( $res, $rs->resultVariable ); my @sols = $model->objects( $res, $rs->solution ); my @names = map { $_->literal_value } @vars; my @bindings; foreach my $r (@sols) { my %data; my @b = $model->objects( $r, $rs->binding ); foreach my $b (@b) { my ($value) = $model->objects( $b, $rs->value ); my ($var) = $model->objects( $b, $rs->variable ); $data{ $var->literal_value } = $value; } push(@bindings, RDF::Trine::VariableBindings->new( \%data )); } my $iter = RDF::Trine::Iterator::Bindings->new( \@bindings, \@names ); if ($args{ results }) { $iter = $iter->materialize; warn "Got expected results:\n"; warn $iter->as_string; } return binding_results_data($iter); } } else { die "Unrecognized type of expected results: $file"; } } sub compare_results { my $expected = shift; my $actual = shift; my $earl = shift; my $test = shift; my $comment = shift || do { my $foo; \$foo }; my $TODO = shift; my $lossy_cmp = 0; if (reftype($expected) eq 'ARRAY') { # comparison with expected results coming from a lossy format like csv/tsv $lossy_cmp = 1; my %data = (results => [], blank_identifiers => {}); foreach my $row (@$expected) { push(@{ $data{ results } }, $row ); foreach my $key (keys %$row) { my $node = $row->{$key}; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { $data{ blank_identifiers }{ $node->blank_identifier }++; } } } $data{ blanks } = scalar(@{ [ keys %{ $data{ blank_identifiers } } ] }); $expected = \%data; } if (not(ref($actual))) { my $ok = is( $actual, $expected, $test ); return $ok; } elsif (blessed($actual) and $actual->isa('RDF::Trine::Iterator::Graph')) { die "Unexpected Graph result type (was expecting " . ref($expected) . ")" unless (blessed($expected) and $expected->isa('RDF::Trine::Iterator::Graph')); my $act_graph = RDF::Trine::Graph->new( $actual ); my $exp_graph = RDF::Trine::Graph->new( $expected ); # local($debug) = 1 if ($PATTERN); if ($debug) { warn ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"; my $actualxml = $act_graph->get_statements->as_string; warn $actualxml; warn "-------------------------------\n"; my $expectxml = $exp_graph->get_statements->as_string; warn $expectxml; warn "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"; } my $eq = $act_graph->equals( $exp_graph ); unless ($eq) { warn $act_graph->error; } return is( $eq, 1, $test ); } elsif (reftype($actual) eq 'HASH' and reftype($expected) eq 'HASH') { my @aresults = @{ $actual->{ results } }; my @eresults = @{ $expected->{ results } }; my $acount = scalar(@aresults); my $ecount = scalar(@eresults); if ($acount != $ecount) { warn "Result count ($acount) didn't match expected ($ecount)" if ($debug); return fail($test); } # warn Data::Dumper->Dump([\@aresults, \@eresults], [qw(actual expected)]); my ($awith, $awithout) = split_results_with_blank_nodes( @aresults ); my ($ewith, $ewithout) = split_results_with_blank_nodes( @eresults ); # for the results without blanks, just serialize, sort, and compare my @astrings = sort map { result_to_string($_, $lossy_cmp) } @$awithout; my @estrings = sort map { result_to_string($_, $lossy_cmp) } @$ewithout; if ($actual->{ blanks } == 0 and $expected->{ blanks } == 0) { return is_deeply( \@astrings, \@estrings, $test ); } elsif (join("\xFF", @astrings) ne join("\xFF", @estrings)) { warn "triples don't match: " . Dumper(\@astrings, \@estrings); return fail($test); } # compare the results with bnodes my @ka = keys %{ $actual->{blank_identifiers} }; my @kb = keys %{ $expected->{blank_identifiers} }; my $kbp = permutations( \@kb ); MAPPING: while (my $mapping = $kbp->next) { my %mapping; @mapping{ @ka } = @$mapping; warn "trying mapping: " . Dumper(\%mapping) if ($debug); my %ewith = map { result_to_string($_, $lossy_cmp) => 1 } @$ewith; foreach my $row (@$awith) { my %row; foreach my $k (keys %$row) { my $n = $row->{ $k }; next unless (blessed($n)); if ($n->isa('RDF::Trine::Node::Blank')) { my $id = $mapping{ $n->blank_identifier }; warn "mapping " . $n->blank_identifier . " to $id\n" if ($debug); $row{ $k } = RDF::Trine::Node::Blank->new( $id ); } else { $row{ $k } = $n; } } my $mapped_row = result_to_string( RDF::Query::VariableBindings->new( \%row ), $lossy_cmp ); warn "checking for '$mapped_row' in " . Dumper(\%ewith) if ($debug); if ($ewith{ $mapped_row }) { delete $ewith{ $mapped_row }; } else { next MAPPING; } } warn "found mapping: " . Dumper(\%mapping) if ($debug); return pass($test); } warn "failed to find bnode mapping: " . Dumper($awith, $ewith); return fail($test); } else { die "Failed to compare actual and expected results: " . Dumper($actual, $expected); } } sub binding_results_data { my $iter = shift; my %data = (results => [], blank_identifiers => {}); while (my $row = $iter->next) { push(@{ $data{ results } }, $row ); foreach my $key (keys %$row) { my $node = $row->{$key}; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { $data{ blank_identifiers }{ $node->blank_identifier }++; } } } $data{ blanks } = scalar(@{ [ keys %{ $data{ blank_identifiers } } ] }); return \%data; } sub split_results_with_blank_nodes { my (@with, @without); ROW: foreach my $row (@_) { my @keys = grep { ref($row->{ $_ }) } keys %$row; foreach my $k (@keys) { my $node = $row->{ $k }; if (blessed($node) and $node->isa('RDF::Trine::Node::Blank')) { push(@with, $row); next ROW; } } push(@without, $row); } return (\@with, \@without); } sub result_to_string { my $row = shift; my $lossy_cmp = shift; my @keys = grep { ref($row->{ $_ }) } keys %$row; my @results; foreach my $k (@keys) { my $node = $row->{ $k }; if ($node->isa('RDF::Trine::Node::Literal') and $node->has_datatype) { my ($value, $dt); if ($lossy_cmp) { $value = $node->literal_value; $dt = undef; } else { $value = RDF::Trine::Node::Literal->canonicalize_literal_value( $node->literal_value, $node->literal_datatype ); $dt = $node->literal_datatype; } $node = RDF::Query::Node::Literal->new( $value, undef, $dt ); } push(@results, join('=', $k, $node->as_string)); } return join(',', sort(@results)); } package Test::RDF::Query::Plan::Service; use strict; use warnings; use Data::Dumper; use Scalar::Util qw(refaddr); use base qw(RDF::Query::Plan::Service); our %ENDPOINTS; our %service_ctx; sub new { my $class = shift; my $endpoint = shift; my $plan = shift; my $silent = shift; my $sparql = shift; if ($endpoint->isa('RDF::Query::Node::Resource')) { my $uri = $endpoint->uri_value; warn "setting up mock endpoint for $uri" if ($debug); } my $self = $class->SUPER::new( $endpoint, $plan, $silent, $sparql, @_ ); if ($endpoint->isa('RDF::Query::Node::Resource')) { my $uri = $endpoint->uri_value; my $e = URI->new($uri); my $model = $service_ctx{ $uri }; # warn "model for $uri: $model"; if ($model) { my $end = RDF::Endpoint->new( $model, { endpoint => { endpoint_path => $e->path } } ); $ENDPOINTS{ refaddr($self) } = $end; } } return $self; } # sub mock { # my $self = shift; # return; # my $endpoint = shift; # my $data = shift; # my $e = URI->new($endpoint); # # my $model = RDF::Trine::Model->new(); # my ($default, $named) = @$data; # if ($default) { # RDF::Trine::Parser->parse_url_into_model( $default->uri_value, $model ); # my $end = RDF::Endpoint->new( $model, { endpoint => { endpoint_path => $e->path } } ); # $ENDPOINTS{ refaddr($self) } = $end; # } # } sub _request { my $self = shift; my $ua = shift; my $req = shift; my $env = $req->to_psgi; my $end = $ENDPOINTS{ refaddr($self) }; if ($end) { # warn "got mocked endpoint"; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $resp = $end->run( $req ); return $resp->finalize; }; my $data = $app->( $env ); my $resp = HTTP::Response->from_psgi( $data ); return $resp; } else { # warn "no mocked endpoint available"; return HTTP::Response->new(403); } } sub DESTROY { my $self = shift; delete $ENDPOINTS{ refaddr($self) }; $self->SUPER::DESTROY(); } RDF-Query-2.910/xt/dawg-syntax11.t000755 000765 000024 00000021605 11760736733 016606 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use RDF::Query; use Test::More; use Scalar::Util qw(blessed); use RDF::Trine::Iterator qw(smap); use RDF::Query::Node qw(iri); use LWP::Simple; use LWP::MediaTypes qw(add_type); add_type( 'application/rdf+xml' => qw(rdf xrdf rdfx) ); add_type( 'text/turtle' => qw(ttl) ); add_type( 'text/plain' => qw(nt) ); add_type( 'text/x-nquads' => qw(nq) ); add_type( 'text/json' => qw(json) ); add_type( 'text/html' => qw(html xhtml htm) ); our $debug = 0; if ($] < 5.007003) { plan skip_all => 'perl >= 5.7.3 required'; exit; } require Encode; require Data::Dumper; plan qw(no_plan); require "xt/dawg/earl.pl"; my $PATTERN = shift(@ARGV) || ''; my @manifests; my $model = new_model( map { glob( "xt/dawg11/$_/manifest.ttl" ) } qw( aggregates construct delete-insert grouping syntax-query syntax-fed syntax-update-1 syntax-update-2 ) ); my $earl = init_earl( $model ); my $type = iri( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ); my $pos_query = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#PositiveSyntaxTest11" ); my $pos_update = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#PositiveUpdateSyntaxTest11" ); my $neg_query = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#NegativeSyntaxTest11" ); my $neg_update = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#NegativeUpdateSyntaxTest11" ); my $mfname = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#name" ); my $mfaction = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action" ); my $mf = RDF::Trine::Namespace->new('http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#'); { # print "# Positive Syntax Tests\n"; my @manifests = $model->subjects( $type, $mf->Manifest ); foreach my $m (@manifests) { warn "Manifest: " . $m->as_string . "\n" if ($debug); my ($list) = $model->objects( $m, $mf->entries ); my @tests = $model->get_list( $list ); foreach my $test (@tests) { unless ($test->uri_value =~ /$PATTERN/) { next; } my $is_pos_query = $model->count_statements($test, $type, $pos_query); my $is_pos_update = $model->count_statements($test, $type, $pos_update); my $is_neg_query = $model->count_statements($test, $type, $mf->NegativeSyntaxTest) + $model->count_statements($test, $type, $mf->NegativeSyntaxTest11); my $is_neg_update = $model->count_statements($test, $type, $mf->NegativeUpdateSyntaxTest) + $model->count_statements($test, $type, $mf->NegativeUpdateSyntaxTest11); if ($is_pos_query or $is_pos_update) { my $name = get_first_literal( $model, $test, $mfname ); my $ok = positive_syntax_test( $model, $test, $is_pos_update ); ok( $ok, $name ); if ($ok) { earl_pass_test( $earl, globalize_uri_filename($test) ); } else { earl_fail_test( $earl, globalize_uri_filename($test) ); warn RDF::Query->error; } } elsif ($is_neg_query or $is_neg_update) { my $name = get_first_literal( $model, $test, $mfname ); my $ok = negative_syntax_test( $model, $test, $is_neg_update ); ok( $ok, $name ); if ($ok) { earl_pass_test( $earl, globalize_uri_filename($test) ); } else { earl_fail_test( $earl, globalize_uri_filename($test) ); } } } } } # { # print "# Negative Syntax Tests\n"; # my @manifests = $model->subjects( $type, $mf->Manifest ); # foreach my $m (@manifests) { # warn "Manifest: " . $m->as_string . "\n" if ($debug); # my ($list) = $model->objects( $m, $mf->entries ); # my @tests = $model->get_list( $list ); # foreach my $test (@tests) { # my $is_neg_query = $model->count_statements($test, $type, $mf->NegativeSyntaxTest) + $model->count_statements($test, $type, $mf->NegativeSyntaxTest11); # my $is_neg_update = $model->count_statements($test, $type, $mf->NegativeUpdateSyntaxTest) + $model->count_statements($test, $type, $mf->NegativeUpdateSyntaxTest11); # if ($is_neg_query or $is_neg_update) { # my $name = get_first_literal( $model, $test, $mfname ); # unless ($test->uri_value =~ /$PATTERN/) { # next; # } # my $ok = negative_syntax_test( $model, $test ); # ok( $ok, $name ); # if ($ok) { # earl_pass_test( $earl, globalize_uri_filename($test) ); # } else { # earl_fail_test( $earl, globalize_uri_filename($test) ); # warn RDF::Query->error; # } # } # } # } # } open( my $fh, '>', 'earl-syntax-11.ttl' ); print {$fh} earl_output( $earl ); close($fh); ################################################################################ sub positive_syntax_test { my $model = shift; my $test = shift; my $update = shift; my $action = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action" ); my $file = get_first_obj( $model, $test, $action ); my $url = $file->uri_value; my $uri = URI->new( relativeize_url( $url ) ); my $filename = localize_uri_filename( $uri->file ); my $sparql = do { local($/) = undef; open(my $fh, '<', $filename); <$fh> }; my @uargs = $update ? (update => 1) : (); my $query = eval { RDF::Query->new( $sparql, { lang => 'sparql11', @uargs } ) }; return 0 if ($@); return blessed($query) ? 1 : 0; } sub negative_syntax_test { my $model = shift; my $test = shift; my $update = shift; my $action = iri( "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action" ); my $file = get_first_obj( $model, $test, $action ); my $url = $file->uri_value; my $uri = URI->new( relativeize_url( $url ) ); my $filename = $uri->file; my $sparql = do { local($/) = undef; open(my $fh, '<', $filename); <$fh> }; my @uargs = $update ? (update => 1) : (); my $query = eval { RDF::Query->new( $sparql, { lang => 'sparql11', @uargs } ) }; # warn RDF::Query->error; return 1 if ($@); warn 'Test expected failure but successfully parsed: ' . Data::Dumper::Dumper($query->{parsed}) if (blessed($query)); # warn $query->error if (blessed($query)); return blessed($query) ? 0 : 1; } exit; ###################################################################### sub new_model { my @files = @_; my $model = RDF::Trine::Model->temporary_model; add_to_model( $model, file_uris(@files) ); return ($model); } sub add_to_model { my $model = shift; my @files = @_; foreach my $file (@files) { my $pclass = RDF::Trine::Parser->guess_parser_by_filename( $file ); my $parser = $pclass->new(); my $rdf = get($file); $parser->parse_into_model( $file, $rdf, $model ); } } sub localize_uri_filename { my $uri = shift; $uri =~ s{^http://www.w3.org/2009/sparql/docs/tests/data-sparql11/}{xt/dawg11/}; return $uri; } sub globalize_uri_filename { my $uri = shift; $uri = $uri->uri_value; $uri =~ s{^.*xt/dawg11/}{http://www.w3.org/2009/sparql/docs/tests/data-sparql11/}; return RDF::Trine::Node::Resource->new($uri); } sub file_uris { my @files = @_; my @uris = map { "$_" } map { URI::file->new_abs( $_ ) } @files; return @uris; } ###################################################################### sub get_first_as_string { my $node = get_first_obj( @_ ); return unless $node; return node_as_string( $node ); } sub node_as_string { my $node = shift; if ($node) { no warnings 'once'; if ($node->isa('RDF::Trine::Node::Resource')) { return $node->uri_value; } elsif ($node->isa_literal) { return Encode::decode('utf8', $node->literal_value); } else { return $node->blank_identifier; } } else { return; } } sub get_first_literal { my $node = get_first_obj( @_ ); return $node ? $node->literal_value : undef; } sub get_all_literal { my @nodes = get_all_obj( @_ ); return map { $_->literal_value } grep { $_->isa('RDF::Trine::Node::Literal') } @nodes; } sub get_first_uri { my $node = get_first_obj( @_ ); return $node ? $node->uri_value : undef; } sub get_all_uri { my @nodes = get_all_obj( @_ ); return map { $_->uri_value } grep { defined($_) and $_->isa('RDF::Trine::Node::Resource') } @nodes; } sub get_first_obj { my $model = shift; my $node = shift; my $uri = shift; my @uris = UNIVERSAL::isa($uri, 'ARRAY') ? @{ $uri } : ($uri); my @preds = map { ref($_) ? $_ : iri( $_ ) } @uris; foreach my $pred (@preds) { my $stream = $model->get_statements( $node, $pred, undef ); while (my $st = $stream->next) { my $node = $st->object; return $node if ($node); } } } sub get_all_obj { my $model = shift; my $node = shift; my $uri = shift; my @uris = UNIVERSAL::isa($uri, 'ARRAY') ? @{ $uri } : ($uri); my @preds = map { ref($_) ? $_ : iri( $_ ) } @uris; my @objs; my @streams; foreach my $pred (@preds) { push(@streams, $model->get_statements( $node, $pred, undef )); } my $stream = shift(@streams); while (@streams) { $stream = $stream->concat( shift(@streams) ); } return map { $_->object } $stream->get_all(); } sub relativeize_url { my $uri = shift; if ($uri =~ /^http:/) { $uri =~ s{^http://www.w3.org/2001/sw/DataAccess/tests/}{xt/dawg/data-r2/}; $uri = 'file://' . File::Spec->rel2abs( $uri ); } return $uri; } __END__ RDF-Query-2.910/xt/dev-service-description.t000644 000765 000024 00000012002 11707542737 020717 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use Test::More; plan skip_all => "QUERY FEDERATION isn't implemented"; # use RDF::Trine::Namespace qw(rdf foaf); # my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#'); # my $dcterms = RDF::Trine::Namespace->new('http://purl.org/dc/terms/'); # # use lib qw(. t); # BEGIN { require "models.pl"; } # # use Test::More; # # my $tests = 22; # my $network_tests = $ENV{RDFQUERY_NETWORK_TESTS} || 0; # if (not exists $ENV{RDFQUERY_DEV_TESTS}) { # plan skip_all => 'Developer tests. Set RDFQUERY_DEV_TESTS to run these tests.'; # return; # } else { # plan tests => $tests; # } # # use_ok( 'RDF::Query::Federate' ); # use_ok( 'RDF::Query::ServiceDescription' ); # # ################################################################################ # # Log::Log4perl::init( \q[ # # log4perl.category.rdf.query.servicedescription = DEBUG, Screen # # log4perl.category.rdf.query.plan.service = DEBUG, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # # log4perl.appender.Screen.stderr = 0 # # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # # ] ); # ################################################################################ # # my $uri = URI::file->new_abs( 'data/service.ttl' ); # my $sd = RDF::Query::ServiceDescription->new_from_uri( $uri ); # isa_ok( $sd, 'RDF::Query::ServiceDescription' ); # # { # is( $sd->label, 'DBpedia', 'expected endpoint label' ); # is( $sd->url, 'http://dbpedia.org/sparql', 'expected endpoint uri' ); # is( $sd->size, 58_787_090, 'expected triple size'); # is( $sd->definitive, 1, 'expected definitive flag'); # # my $o = RDF::Query::Node::Variable->new('object'); # my $expect_p = { # $rdf->type->uri_value => { # pred => RDF::Query::Node::Resource->new( $rdf->type->uri_value ), # sofilter => RDF::Query::Expression::Function->new('sparql:regex', RDF::Query::Expression::Function->new('sparql:str', $o), RDF::Query::Node::Literal->new('http://xmlns.com/foaf/0.1/Person')), # size => RDF::Query::Node::Literal->new('3683409', undef, $xsd->integer->uri_value), # }, # $foaf->name->uri_value => { # pred => RDF::Query::Node::Resource->new( $foaf->name->uri_value ), # sofilter => undef, # size => RDF::Query::Node::Literal->new('18000', undef, $xsd->integer->uri_value), # object_selectivity => RDF::Query::Node::Literal->new('0.02', undef, $xsd->double->uri_value), # }, # $foaf->mbox->uri_value => { # pred => RDF::Query::Node::Resource->new( $foaf->mbox->uri_value ), # sofilter => undef, # size => RDF::Query::Node::Literal->new('18000', undef, $xsd->integer->uri_value), # object_selectivity => RDF::Query::Node::Literal->new('5.5E-5', undef, $xsd->double->uri_value), # }, # $dcterms->spatial->uri_value => { # pred => RDF::Query::Node::Resource->new( $dcterms->spatial->uri_value ), # }, # }; # my $cap = $sd->capabilities; # foreach my $data (grep {exists $_->{pred}} @$cap) { # my $p = $data->{pred}->uri_value; # my $e = delete $expect_p->{ $p }; # isa_ok( $e, 'HASH' ); # is_deeply( $data, $e, "capability for $p" ); # } # is_deeply( $expect_p, {}, 'seen all expected predicate-based capabilities' ); # # my $expect_t = { # 'http://kasei.us/2008/04/sparql#any_triple' => 1, # }; # foreach my $data (grep {exists $_->{type}} @$cap) { # my $type = $data->{type}->uri_value; # my $ok = delete( $expect_t->{ $type } ); # ok( $ok, "expected type-capability: $type" ); # } # is_deeply( $expect_t, {}, 'seen all expected type-based capabilities' ); # } # # SKIP: { # skip "set RDFQUERY_NETWORK_TESTS to run these tests", 3 unless ($network_tests); # my $query = RDF::Query::Federate->new( <<"END" ); # PREFIX foaf: # SELECT ?name # WHERE { foaf:name ?name . FILTER( LANG(?name) = "en" ) } # END # $query->add_computed_statement_generator( $sd->computed_statement_generator ); # my $iter = $query->execute; # my $count = 0; # while (my $row = $iter->next) { # isa_ok( $row, 'HASH' ); # my $name = $row->{name}; # like( $name->literal_value, qr"^Alan.*Turing$", 'execution: expected foaf:name in federation description' ); # $count++; # last; # } # is( $count, 1, 'got results from dbpedia' ); # } # # SKIP: { # skip "set RDFQUERY_NETWORK_TESTS to run these tests", 1 unless ($network_tests); # my $query = RDF::Query::Federate->new( <<"END" ); # PREFIX foaf: # PREFIX dbp: # SELECT ?job # WHERE { dbp:occupation ?job } # END # $query->add_computed_statement_generator( $sd->computed_statement_generator ); # my $iter = $query->execute; # my $count = 0; # while (my $row = $iter->next) { # $count++; # } # is( $count, 0, 'execution: expected dbp:occupation not in federation description' ); # } RDF-Query-2.910/xt/dev-sql-compiler.t000644 000765 000024 00000103202 11707542737 017350 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use Test::More; use Test::Exception; use RDF::Query; use RDF::Query::Parser::SPARQL; if ($ENV{RDFQUERY_DEV_MYSQL}) { plan 'no_plan'; } else { plan tests => 34; } use_ok( 'RDF::Query::Compiler::SQL' ); my $parser = new RDF::Query::Parser::SPARQL (); { my $uri = 'http://xmlns.com/foaf/0.1/name'; my $node = RDF::Query::Node::Resource->new( $uri ); my $hash = RDF::Query::Compiler::SQL->_mysql_node_hash( $node ); is( $hash, '14911999128994829034', 'URI hash' ); } { my $node = RDF::Query::Node::Literal->new( 'kasei' ); my $hash = RDF::Query::Compiler::SQL->_mysql_node_hash( $node ); is( $hash, '12775641923308277283', 'literal hash' ); } { my $hash = RDF::Query::Compiler::SQL::_mysql_hash( 'LTom Croucher' ); is( $hash, '14336915341960534814', 'language-typed literal hash' ); } { my $node = RDF::Query::Node::Literal->new( 'Tom Croucher', 'en' ); my $hash = RDF::Query::Compiler::SQL->_mysql_node_hash( $node ); is( $hash, '14336915341960534814', 'language-typed literal node hash 1' ); } { my $node = RDF::Query::Node::Literal->new( 'RDF', 'en' ); my $hash = RDF::Query::Compiler::SQL->_mysql_node_hash( $node ); is( $hash, '16625494614570964497', 'language-typed literal node hash 2' ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed, 'model' ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements15799945864759145248 s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034", "select people and names" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034", "select people and names" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name ?homepage WHERE { ?person foaf:name ?name ; foaf:homepage ?homepage } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name,\n\ts3.object AS homepage_Node,\n\tljr2.URI AS homepage_URI,\n\tljl2.Value AS homepage_Value,\n\tljl2.Language AS homepage_Language,\n\tljl2.Datatype AS homepage_Datatype,\n\tljb2.Name AS homepage_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID),\n\tStatements s3 LEFT JOIN Resources ljr2 ON (s3.object = ljr2.ID) LEFT JOIN Literals ljl2 ON (s3.object = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s3.object = ljb2.ID)\nWHERE\n\ts2.predicate = 14911999128994829034 AND\n\ts3.subject = s2.subject AND\n\ts3.predicate = 9768710922987392204", "select people, names, and homepages" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?x ?name FROM NAMED FROM NAMED WHERE { GRAPH { ?x foaf:name ?name } } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts3.subject AS x_Node,\n\tljr0.URI AS x_URI,\n\tljl0.Value AS x_Value,\n\tljl0.Language AS x_Language,\n\tljl0.Datatype AS x_Datatype,\n\tljb0.Name AS x_Name,\n\ts3.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s3.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s3.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s3.object = ljb1.ID)\nWHERE\n\ts3.predicate = 14911999128994829034 AND\n\ts3.context = 2618056589919804847", "select people and names of context-specific graph" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?src ?name FROM NAMED FROM NAMED WHERE { GRAPH ?src { ?x foaf:name ?name } } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts3.context AS src_Node,\n\tljr0.URI AS src_URI,\n\tljl0.Value AS src_Value,\n\tljl0.Language AS src_Language,\n\tljl0.Datatype AS src_Datatype,\n\tljb0.Name AS src_Name,\n\ts3.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name,\n\tljr2.URI AS x_URI,\n\tljl2.Value AS x_Value,\n\tljl2.Language AS x_Language,\n\tljl2.Datatype AS x_Datatype,\n\tljb2.Name AS x_Name\nFROM\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.context = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.context = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.context = ljb0.ID) LEFT JOIN Resources ljr1 ON (s3.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s3.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s3.object = ljb1.ID) LEFT JOIN Resources ljr2 ON (s3.subject = ljr2.ID) LEFT JOIN Literals ljl2 ON (s3.subject = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s3.subject = ljb2.ID)\nWHERE\n\ts3.predicate = 14911999128994829034", "select context of people and names" ); } { my $parsed = $parser->parse(<<"END"); PREFIX rss: SELECT ?title WHERE { rss:title ?title . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.object AS title_Node,\n\tljr0.URI AS title_URI,\n\tljl0.Value AS title_Value,\n\tljl0.Language AS title_Language,\n\tljl0.Datatype AS title_Datatype,\n\tljb0.Name AS title_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.subject = 1083049239652454081 AND\n\ts2.predicate = 17858988500659793691", "select rss:title of uri" ); } { my $parsed = $parser->parse(<<"END"); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?page WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?page . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts3.object AS page_Node,\n\tljr0.URI AS page_URI,\n\tljl0.Value AS page_Value,\n\tljl0.Language AS page_Language,\n\tljl0.Datatype AS page_Datatype,\n\tljb0.Name AS page_Name,\n\tljr1.URI AS person_URI,\n\tljl1.Value AS person_Value,\n\tljl1.Language AS person_Language,\n\tljl1.Datatype AS person_Datatype,\n\tljb1.Name AS person_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.object = ljb0.ID)\nWHERE\n\ts2.predicate = 14911999128994829034 AND\n\ts2.object = 2782977400239829321 AND\n\ts3.subject = s2.subject AND\n\ts3.predicate = 9768710922987392204", "select homepage of person by name" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT ?s ?p WHERE { ?s ?p "RDF"@en . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS s_Node,\n\tljr0.URI AS s_URI,\n\tljl0.Value AS s_Value,\n\tljl0.Language AS s_Language,\n\tljl0.Datatype AS s_Datatype,\n\tljb0.Name AS s_Name,\n\ts2.predicate AS p_Node,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.predicate = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.predicate = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.predicate = ljb1.ID)\nWHERE\n\ts2.object = 16625494614570964497", "select s,p by language-tagged literal" ); } { RDF::Query::Compiler::SQL->add_function( 'time:now', sub { my $self = shift; my $parsed_vars = shift; my $expr = shift; my $level = shift || \do{ my $a = 0 }; my %queryvars = map { $_->name => 1 } @$parsed_vars; return ({}, [], ['NOW()']); } ); my $parsed = $parser->parse(<<'END'); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: PREFIX xsd: PREFIX time: SELECT ?point WHERE { ?point a geo:Point . FILTER( time:now() > "2006-01-01" ) } END my $sql; lives_ok { my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); $sql = $compiler->compile(); } 'compile: select with function filter'; is( $sql, qq(SELECT\n\ts6.subject AS point_Node,\n\tljr0.URI AS point_URI,\n\tljl0.Value AS point_Value,\n\tljl0.Language AS point_Language,\n\tljl0.Datatype AS point_Datatype,\n\tljb0.Name AS point_Name\nFROM\n\tStatements s6 LEFT JOIN Resources ljr0 ON (s6.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s6.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s6.subject = ljb0.ID)\nWHERE\n\tNOW() > '2006-01-01' AND\n\ts6.predicate = 2982895206037061277 AND\n\ts6.object = 11045396790191387947), "sql: select with function filter" ); } { RDF::Query::Compiler::SQL->add_function( 'http://kasei.us/e/ns/geo#distance', sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my $vars = $self->{vars}; my (@from, @where); my %queryvars = map { $_->name => 1 } @$parsed_vars; ++$$level; my $sql_a = $self->expr2sql( $args[0], $level ); ++$$level; my $sql_b = $self->expr2sql( $args[1], $level ); ++$$level; my $sql_c = $self->expr2sql( $args[1], $level ); push(@where, "distance($sql_a, $sql_b, $sql_c)"); return ($vars, \@from, \@where); } ); my $parsed = $parser->parse(<<'END'); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: PREFIX xsd: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, +41.849331, -71.392) < "10"^^xsd:integer ) } END my $sql; lives_ok { my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); $sql = $compiler->compile(); } 'compile: select images filterd by distance function comparison'; is( $sql, qq(SELECT\n\ts10.subject AS image_Node,\n\tljr0.URI AS image_URI,\n\tljl0.Value AS image_Value,\n\tljl0.Language AS image_Language,\n\tljl0.Datatype AS image_Datatype,\n\tljb0.Name AS image_Name,\n\ts9.subject AS point_Node,\n\tljr1.URI AS point_URI,\n\tljl1.Value AS point_Value,\n\tljl1.Language AS point_Language,\n\tljl1.Datatype AS point_Datatype,\n\tljb1.Name AS point_Name,\n\ts9.object AS lat_Node,\n\tljr2.URI AS lat_URI,\n\tljl2.Value AS lat_Value,\n\tljl2.Language AS lat_Language,\n\tljl2.Datatype AS lat_Datatype,\n\tljb2.Name AS lat_Name,\n\tljr3.URI AS pred_URI,\n\tljl3.Value AS pred_Value,\n\tljl3.Language AS pred_Language,\n\tljl3.Datatype AS pred_Datatype,\n\tljb3.Name AS pred_Name\nFROM\n\tStatements s9 LEFT JOIN Resources ljr1 ON (s9.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s9.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s9.subject = ljb1.ID) LEFT JOIN Resources ljr2 ON (s9.object = ljr2.ID) LEFT JOIN Literals ljl2 ON (s9.object = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s9.object = ljb2.ID),\n\tStatements s10 LEFT JOIN Resources ljr0 ON (s10.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s10.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s10.subject = ljb0.ID) LEFT JOIN Resources ljr3 ON (s10.predicate = ljr3.ID) LEFT JOIN Literals ljl3 ON (s10.predicate = ljl3.ID) LEFT JOIN Bnodes ljb3 ON (s10.predicate = ljb3.ID)\nWHERE\n\tdistance(, (0.0 + '+41.849331'), (0.0 + '+41.849331')) < (0 + '10') AND\n\ts9.predicate = 5391429383543785584 AND\n\ts10.object = s9.subject), "sql: select images filterd by distance function comparison" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER REGEX(?name, "Greg") . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, qq(SELECT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t(ljl0.Value REGEXP 'Greg' OR ljr0.URI REGEXP 'Greg' OR ljb0.Name REGEXP 'Greg') AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034), "select people by regex-filtered name" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT DISTINCT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER REGEX(?name, "Greg") . } LIMIT 1 END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, qq(SELECT DISTINCT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t(ljl0.Value REGEXP 'Greg' OR ljr0.URI REGEXP 'Greg' OR ljb0.Name REGEXP 'Greg') AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034\nLIMIT 1), "select people by regex-filtered name with DISTINCT and LIMIT" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT DISTINCT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER( ?name = "Gregory Todd Williams" ) } LIMIT 1 END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, qq(SELECT DISTINCT\n\ts7.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s6 LEFT JOIN Resources ljr1 ON (s6.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s6.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s6.subject = ljb1.ID),\n\tStatements s7 LEFT JOIN Resources ljr0 ON (s7.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s7.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s7.object = ljb0.ID)\nWHERE\n\t(SELECT value FROM Literals WHERE = ID LIMIT 1) = 'Gregory Todd Williams' AND\n\ts6.predicate = 2982895206037061277 AND\n\ts6.object = 3652866608875541952 AND\n\ts7.subject = s6.subject AND\n\ts7.predicate = 14911999128994829034\nLIMIT 1), "select people by Literal name with DISTINCT and LIMIT" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT DISTINCT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER( ?p = ) } LIMIT 1 END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, qq(SELECT DISTINCT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t = 2954181085641959508 AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034\nLIMIT 1), "select people by URI with DISTINCT and LIMIT" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name . FILTER BOUND(?name) . } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts4.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts4.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr0 ON (s4.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s4.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s4.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s4.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.object = ljb1.ID)\nWHERE\n\t IS NOT NULL AND\n\ts4.predicate = 14911999128994829034", "select people and names with filter BOUND()" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name } ORDER BY ?name END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tname_Value ASC, name_URI ASC, name_Name ASC", "order by names" ); } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name } ORDER BY ?name, ?person END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); throws_ok { my $sql = $compiler->compile(); } 'RDF::Query::Error::CompilationError', 'order by multiple columns throws error'; } { my $parsed = $parser->parse(<<"END"); PREFIX foaf: CONSTRUCT { ?person foaf:name ?name } WHERE { ?person foaf:name ?name } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed, 'model' ); throws_ok { my $sql = $compiler->compile(); } 'RDF::Query::Error::CompilationError', 'non-select throws error'; } { my $parsed = $parser->parse(<<"END"); PREFIX time: PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name } ORDER BY time:now() END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); $compiler->add_function( 'time:now', sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; return ({}, [], ['NOW()']); } ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tNOW() ASC", "order by function (NOW())" ); } { my $parsed = $parser->parse(<<"END"); PREFIX func: PREFIX foaf: SELECT ?person ?name WHERE { ?person foaf:name ?name } ORDER BY func:ascii(?name) END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); $compiler->add_function( 'func:ascii', sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my $expr = shift; my $col = $self->expr2sql( $expr, $level ); return ({}, [], ["ASCII($col)"]); } ); isa_ok( $compiler, 'RDF::Query::Compiler::SQL' ); my $sql = $compiler->compile(); is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tASCII(ljr1.URI) ASC, ASCII(ljl1.Value) ASC, ASCII(ljl1.Language) ASC", "order by function (ASCII(name))" ); } # if ($ENV{RDFQUERY_DEV_MYSQL}) { # my $model_name = 'model'; # SKIP: { # eval "require Kasei::RDF::Common;"; # if ($@) { # warn $@; # exit; # # skip "Failed to load Kasei::RDF::Common", 1; # } # # my $dsn = [ Kasei::Common::mysql_dbi_args() ]; # # { # my $model = DBI->connect( @$dsn ); # my $sparql = <<'END'; # SELECT ?s ?p ?o # WHERE { ?s ?p ?o } # LIMIT 1 # END # lives_ok { # my $query = new RDF::Query ( $sparql, undef, undef, 'sparql' ); # $query->execute( $model, model => $model_name, require_sql => 1 ); # } 's-p-o without pre-bound vars'; # } # # { # Kasei::RDF::Common->import('mysql_model'); # my @myargs = Kasei::Common::mysql_upd(); # my $model = mysql_model( $model_name, @myargs[ 2, 0, 1 ] ); # # { # my $sparql = <<'END'; # SELECT ?s ?p ?o # WHERE { ?s ?p ?o } # LIMIT 1 # END # lives_ok { # my $query = new RDF::Query ( $sparql, undef, undef, 'sparql' ); # $query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1 ); # } 's-p-o without pre-bound vars'; # # throws_ok { # my $query = new RDF::Query ( $sparql, undef, undef, 'sparql' ); # $query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1, bind => { p => 1 } ); # } 'RDF::Query::Error::CompilationError', 's-p-o without pre-bound vars: forced sql compilation (expected) failure'; # } # # { # my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); # PREFIX geo: # SELECT ?image ?point ?lat # WHERE { # ?point geo:lat ?lat . # ?image ?pred ?point . # FILTER( geo:distance(?point) ) . # } # END # throws_ok { # $query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1 ); # } 'RDF::Query::Error::CompilationError', 'forced sql compilation (expected) failure'; # } # # { # print "# FILTER rage test\n"; # my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); # PREFIX foaf: # PREFIX geo: # SELECT ?image ?point ?lat # WHERE { # ?image a foaf:Image ; ?pred ?point . # ?point geo:lat ?lat . # FILTER( # (?pred = || ?pred = ) # && ?lat > 52 # && ?lat < 53 # ) . # } # END # my $stream = $query->execute( $model, dsn => $dsn, model => $model_name ); # # my $count; # while (my $row = $stream->()) { # my ($image, $point, $lat) = @{ $row }; # ok( $image->isa('RDF::Trine::Node::Resource'), 'image is resource'); # my $latv = ($lat) ? $lat->literal_value : undef; # cmp_ok( $latv, '>', 52, 'lat: ' . $latv ); # cmp_ok( $latv, '<', 53, 'lat: ' . $latv ); # $count++; # } # } # # { # print "# lots of points!\n"; # my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); # PREFIX foaf: # PREFIX geo: # SELECT ?name # WHERE { # ?point a geo:Point; foaf:name ?name . # } # END # my $stream = $query->execute( $model, dsn => $dsn, model => $model_name ); # isa_ok( $stream, 'CODE', 'stream' ); # my $count; # while (my $row = $stream->()) { # my ($node) = @{ $row }; # my $name = $node->as_string; # ok( $name, $name ); # } continue { last if ++$count >= 100 }; # } # # { # print "# foaf:Person ORDER BY name\n"; # my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); # PREFIX foaf: # PREFIX geo: # SELECT DISTINCT ?p ?name # WHERE { # ?p a foaf:Person; foaf:name ?name # } # ORDER BY ?name # END # my $stream = $query->execute( $model, dsn => $dsn, model => $model_name ); # isa_ok( $stream, 'CODE', 'stream' ); # my ($count, $last); # while (my $row = $stream->()) { # my ($p, $node) = @{ $row }; # my $name = $node->as_string; # if (defined($last)) { # cmp_ok( $name, 'ge', $last, "In order: $name (" . $p->as_string . ")" ); # } else { # ok( $name, "$name (" . $p->as_string . ")" ); # } # $last = $name; # } continue { last if ++$count >= 200 }; # } # # { # print "# geo:Point ORDER BY longitude\n"; # my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); # PREFIX foaf: # PREFIX geo: # PREFIX xsd: # SELECT DISTINCT ?name ?lat ?lon # WHERE { # ?point a geo:Point; foaf:name ?name; geo:lat ?lat; geo:long ?lon # } # ORDER BY DESC( xsd:decimal(?lon) ) # END # my $stream = $query->execute( $model, dsn => $dsn, model => $model_name ); # isa_ok( $stream, 'CODE', 'stream' ); # my ($count, $last); # while (my $row = $stream->()) { # my ($node, $lat, $long) = @{ $row }; # my $name = $node->as_string; # if (defined($last)) { # cmp_ok( $long->as_string, '<=', $last, "In order: $name (" . $long->as_string . ")" ); # } else { # ok( $name, "$name (" . $long->as_string . ")" ); # } # $last = $long->as_string; # } continue { last if ++$count >= 200 }; # } # } # } # } # if ($ENV{RDFQUERY_DEV_POSTGRESQL}) { # eval "require Kasei::RDF::Common;"; # $ENV{'POSTGRESQL_MODEL'} = 'model'; # $ENV{'POSTGRESQL_DATABASE'} = 'greg'; # $ENV{'POSTGRESQL_PASSWORD'} = 'password'; # # Kasei::RDF::Common->import('postgresql_model'); # my $dbh = postgresql_model(); # warn $dbh; # # my @myargs = Kasei::Common::postgresql_upd(); # my $model = postgresql_model( 'db1', @myargs[ 2, 0, 1 ] ); # my $dsn = [ Kasei::Common::postgresql_dbi_args() ]; # # warn $model; # warn Dumper($dsn); # # # } __END__ { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person . ?p foaf:name ?name . FILTER( ?p = _:r1101876070r10 ) } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); is( $sql, qq(SELECT\n\ts2.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.predicate = 2982895206037061277 AND\n\ts2.object = 3652866608875541952 AND\n\ts2.subject = s2.subject AND\n\ts2.predicate = 14911999128994829034 AND\n\ts2.subject = 4025741532186680712), "select people by BNode" ); } { my $parsed = $parser->parse(<<'END'); PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER( ?p = [] ) } END my $compiler = RDF::Query::Compiler::SQL->new( $parsed ); my $sql = $compiler->compile(); $sql =~ s/(s2.subject\s*=\s*)\d+/$1XXX/; is( $sql, qq(SELECT\n\ts2.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.predicate = 2982895206037061277 AND\n\ts2.object = 3652866608875541952 AND\n\ts2.subject = s2.subject AND\n\ts2.predicate = 14911999128994829034 AND\n\ts2.subject = XXX), "select people by BNode" ); } RDF-Query-2.910/xt/dev-time-intervals.t000644 000765 000024 00000006523 11707542737 017714 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use Test::Exception; use Scalar::Util qw(refaddr); use RDF::Query; if ($ENV{RDFQUERY_TIMETEST}) { plan qw(no_plan); } else { plan skip_all => 'Developer tests. Set RDFQUERY_TIMETEST to run these tests.'; return; } use lib qw(. t); BEGIN { require "models.pl"; } my $debug = 1; my @files = map { "data/$_" } qw(temporal.rdf); my @models = test_models( @files ); my $tests = 0; my $find_interval = <<"END"; PREFIX xsd: PREFIX t: SELECT ?interval ?b ?e WHERE { { ?interval a t:Interval ; t:begins ?b ; t:ends ?e . FILTER( ?b <= "%s"^^xsd:dateTime && ?e > "%s"^^xsd:dateTime ) } UNION { ?interval a t:Interval ; t:begins ?b . OPTIONAL { ?interval t:ends ?e } . FILTER( !BOUND(?e) ) . FILTER( ?b <= "%s"^^xsd:dateTime ) } UNION { ?interval a t:Interval . OPTIONAL { ?interval t:begins ?b } . ?interval t:ends ?e . FILTER( !BOUND(?b) ) . FILTER( ?e > "%s"^^xsd:dateTime ) } UNION { ?interval a t:Interval . OPTIONAL { ?interval t:begins ?b } . OPTIONAL { ?interval t:ends ?e } . FILTER( !BOUND(?b) && !BOUND(?e) ) . } } END foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; { # find intervals that contain a specific date my $dt = '1999-06-01'; my $sparql = sprintf( $find_interval, ($dt) x 4 ); my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; while (my $data = $stream->next) { my $interval = $data->[0]; ok( $interval->isa('RDF::Trine::Node'), 'time-intervals' ); like( $interval->uri_value, qr/#yearTo2000/, 'time-intervals: 1999' ); $count++; } is( $count, 1, '1999: correct count of matching intervals' ); } { # find intervals that contain a specific date my $dt = '2000-06-01'; my $sparql = sprintf( $find_interval, ($dt) x 4 ); my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; while (my $data = $stream->next) { my $interval = $data->[0]; ok( $interval->isa('RDF::Trine::Node'), 'time-intervals' ); like( $interval->uri_value, qr/#year2000/, 'time-intervals: 2000' ); $count++; } is( $count, 1, '2000: correct count of matching intervals' ); } { # find intervals that contain a specific date my $dt = '2002-06-01'; my $sparql = sprintf( $find_interval, ($dt) x 4 ); my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; while (my $data = $stream->next) { my $interval = $data->[0]; $count++; } is( $count, 0, '2002: correct count of matching intervals' ); } { # find intervals that contain a specific date my $dt = '2005-06-01'; my $sparql = sprintf( $find_interval, ($dt) x 4 ); my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; while (my $data = $stream->next) { my $interval = $data->[0]; ok( $interval->isa('RDF::Trine::Node'), 'time-intervals' ); like( $interval->uri_value, qr/#yearFrom2003/, 'time-intervals: 2005' ); $count++; } is( $count, 1, '2005: correct count of matching intervals' ); } } RDF-Query-2.910/xt/federate.t000644 000765 000024 00000033107 11707542737 015752 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use Test::More; plan skip_all => "QUERY FEDERATION isn't implemented"; # use Config; # use URI::file; # use Test::More; # use Data::Dumper; # # use RDF::Query; # use RDF::Query::Util; # use RDF::Query::Algebra; # use RDF::Query::Federate; # use RDF::Query::Error qw(:try); # use RDF::Query::ServiceDescription; # # use RDF::Trine::Parser; # use RDF::Trine::Namespace qw(rdf foaf); # # use lib qw(. t); # BEGIN { require "models.pl"; } # # my $eval_tests = 3; # my $rewrite_tests = 4; # my $run_eval_tests = 1; # # if (not $ENV{RDFQUERY_DEV_TESTS}) { # plan skip_all => 'Developer tests. Set RDFQUERY_DEV_TESTS to run these tests.'; # return; # } # # plan tests => ($eval_tests + $rewrite_tests); # # my $reason; # eval { require LWP::Simple }; # if ($@) { # $run_eval_tests = 0; # $reason = "LWP::Simple is not available for loading URLs"; # } # # eval { require RDF::Endpoint::Server }; # if ($@) { # $run_eval_tests = 0; # $reason = "RDF::Endpoint::Server is not available"; # } # # ################################################################################ # # Log::Log4perl::init( \q[ # # # log4perl.category.rdf.query.federate.plan = TRACE, Screen # # log4perl.category.rdf.query.plan.thresholdunion = TRACE, Screen # # # log4perl.category.rdf.query.servicedescription = DEBUG, Screen # # # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # # log4perl.appender.Screen.stderr = 0 # # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # # ] ); # ################################################################################ # # my $quit_sig = 1; # my @sigs = split(' ', $Config{sig_name}); # foreach my $i (0 .. $#sigs) { # if ($sigs[$i] eq 'QUIT') { # $quit_sig = $i; # last; # } # } # my %named = map { $_ => File::Spec->rel2abs("data/federation_data/$_") } qw(alice.rdf bob.rdf); # my %models = map { $_ => RDF::Query::Util::make_model( {}, $named{$_} ) } (keys %named); # # # ################################################################################ # # run_tests(); # # ################################################################################ # # sub run_tests { # simple_optimistic_bgp_rewriting_test(); # simple_optimistic_bgp_rewriting_test_with_threshold_time(); # overlapping_optimistic_bgp_rewriting_test_1(); # overlapping_optimistic_bgp_rewriting_test_2(); # # SKIP: { # skip $reason, $eval_tests unless ($run_eval_tests); # simple_optimistic_bgp_rewriting_execution_test(); # } # } # # sub simple_optimistic_bgp_rewriting_test { # ### in this test, two services are used, both of which support the two triple patterns. # ### we're expecting the optimistic QEP to send the whole 2-triple BGP to each service # ### as a whole, and then fall back on joining the two triple patterns locally. # # my $alice_sd = local_sd( 'alice.rdf', 8889, 'http://work.example/people/', 5, [qw(rdf:type foaf:knows)] ); # my $bob_sd = local_sd( 'bob.rdf', 8891, 'http://oldcorp.example.org/bob/', 4, [qw(rdf:type foaf:knows)] ); # my $query = RDF::Query::Federate->new( <<"END", { optimize => 1 } ); # PREFIX foaf: # SELECT ?p ?knows WHERE { # ?p foaf:knows ?knows . # ?knows a foaf:Person . # } # END # $query->add_service( $alice_sd ); # $query->add_service( $bob_sd ); # my ($plan, $ctx) = $query->prepare(); # my $sse = $plan->sse({}, ' '); # is( _CLEAN_WS($sse), _CLEAN_WS(<<'END'), 'expected optimistic federation query plan' ); # (project (p knows) (threshold-union 0 # (service "PREFIX foaf: \nSELECT * WHERE {\n\t?p foaf:knows ?knows .\n\t?knows a foaf:Person .\n}") # (service "PREFIX foaf: \nSELECT * WHERE {\n\t?p foaf:knows ?knows .\n\t?knows a foaf:Person .\n}") # (bind-join # (triple ?knows ) # (triple ?p ?knows)))) # END # ### If we were to start an RDF::Endpoint server on the appropriate ports, this should work: # # my $iter = $query->execute_plan( $plan, $ctx ); # # while (my $row = $iter->next) { # # print "$row\n"; # # } # } # # sub simple_optimistic_bgp_rewriting_test_with_threshold_time { # ### this test is the same as simple_optimistic_bgp_rewriting_test() above, # ### but we use a 'optimistic_threshold_time' flag in the constructor, which # ### should come back out in the sse serialization of the thresholdtime QEP. # # my $alice_sd = local_sd( 'alice.rdf', 8889, 'http://work.example/people/', 5, [qw(rdf:type foaf:knows)] ); # my $bob_sd = local_sd( 'bob.rdf', 8891, 'http://oldcorp.example.org/bob/', 4, [qw(rdf:type foaf:knows)] ); # my $query = RDF::Query::Federate->new( <<"END", { optimize => 1, optimistic_threshold_time => 3 } ); # PREFIX foaf: # SELECT ?p ?knows WHERE { # ?p foaf:knows ?knows . # ?knows a foaf:Person . # } # END # $query->add_service( $alice_sd ); # $query->add_service( $bob_sd ); # my ($plan, $ctx) = $query->prepare(); # my $sse = $plan->sse({}, ' '); # is( _CLEAN_WS($sse), _CLEAN_WS(<<'END'), 'expected optimistic federation query plan' ); # (project (p knows) # (threshold-union 3 # (service "PREFIX foaf: \nSELECT * WHERE {\n\t?p foaf:knows ?knows .\n\t?knows a foaf:Person .\n}") # (service "PREFIX foaf: \nSELECT * WHERE {\n\t?p foaf:knows ?knows .\n\t?knows a foaf:Person .\n}") # (bind-join # (triple ?knows ) # (triple ?p ?knows)) # ) # ) # END # } # # sub overlapping_optimistic_bgp_rewriting_test_1 { # ### this test uses four endpoint service descriptions, with overlapping # ### coverage of five predicates: # ### service \ predicate: P Q R S T # ### a * * * # ### b * * * # ### c * * * # ### d * * # ### no single endpoint can answer the whole query, involving a BGP with # ### 3 triple patterns, but endpoint 'a' can answer a two-triple-pattern # ### subquery (predicates P and Q), then joining with results from endpoint # ### 'd' (the single triple-pattern with predicate T). # my @names = ('a' .. 'd'); # my %preds = ( # a => [qw(P Q R)], # b => [qw(Q R S)], # c => [qw(R S P)], # d => [qw(S T)], # ); # my %sd = map { # my $port = 10000 + (ord($_) - ord('a')); # $_ => local_sd( $_, $port, "http://${_}.example.com/", 5, [ map { "ex:$_" } @{ $preds{ $_ } } ] ); # } @names; # my $query = RDF::Query::Federate->new( <<"END", { optimize => 1 } ); # PREFIX ex: # SELECT * WHERE { # ?v ex:P ?p ; # ex:Q ?q ; # ex:T ?t . # } # END # while (my ($name,$sd) = each(%sd)) { # $query->add_service( $sd ); # } # my ($plan, $ctx) = $query->prepare(); # my $sse = $plan->sse({}, ' '); # is( _CLEAN_WS($sse), _CLEAN_WS(<<'END'), 'expected optimistic federation query plan (1)' ); # (project (v p q t) # (threshold-union 0 # (nestedloop-join # (service "PREFIX ex: \nSELECT * WHERE {\n\t?v ex:P ?p .\n\t?v ex:Q ?q .\n}") # (service "PREFIX ex: \nSELECT * WHERE {\n\t?v ex:T ?t .\n}")) # (bind-join # (bind-join # (triple ?v ?t) # (triple ?v ?q)) # (triple ?v ?p)) # ) # ) # END # } # # sub overlapping_optimistic_bgp_rewriting_test_2 { # ### this test uses two endpoint service descriptions, with overlapping # ### coverage of four predicates: # ### service \ predicate: P Q R S # ### a * * * # ### b * * * # ### no single endpoint can answer the whole query, involving a BGP with # ### 4 triple patterns, but each endpoint can answer a three-triple-pattern # ### subquery, then joining with results with a single-triple-pattern query # ### from the other endpoint. # my @names = (qw(a b)); # my %preds = ( # a => [qw(P Q R)], # b => [qw(Q R S)], # ); # my %sd = map { # my $port = 10000 + (ord($_) - ord('a')); # $_ => local_sd( $_, $port, "http://${_}.example.com/", 5, [ map { "ex:$_" } @{ $preds{ $_ } } ] ); # } @names; # my $query = RDF::Query::Federate->new( <<"END", { optimize => 1 } ); # PREFIX ex: # SELECT * WHERE { # ?v ex:P ?p ; # ex:Q ?q ; # ex:R ?t ; # ex:S ?s . # } # END # while (my ($name,$sd) = each(%sd)) { # $query->add_service( $sd ); # } # my $ctx = RDF::Query::ExecutionContext->new( # query => $query, # optimize => 1, # model => RDF::Trine::Model->temporary_model, # optimistic_threshold_time => 2, # ); # my @plans = $query->query_plan( $ctx ); # my $plan = $plans[0]; # my $sse = $plan->sse({}, ' '); # is( _CLEAN_WS($sse), _CLEAN_WS(<<'END'), 'expected optimistic federation query plan (2)' ); # (project (v p q t s) # (threshold-union 2 # (nestedloop-join # (service "SELECT * WHERE {\n\t?v ?p .\n\t?v ?q .\n\t?v ?t .\n}") # (triple ?v ?s)) # (nestedloop-join # (service "SELECT * WHERE {\n\t?v ?q .\n\t?v ?t .\n\t?v ?s .\n}") # (triple ?v ?p)) # (bind-join # (bind-join (bind-join (triple ?v ?s) (triple ?v ?t)) (triple ?v ?q)) # (triple ?v ?p)) # ) # ) # END # } # # sub simple_optimistic_bgp_rewriting_execution_test { # my %ports = qw(alice.rdf 8889 bob.rdf 8891); # my $alice_sd = local_sd( 'alice.rdf', 8889, 'http://work.example/people/', 5, [qw(rdf:type foaf:knows foaf:name)] ); # my $bob_sd = local_sd( 'bob.rdf', 8891, 'http://oldcorp.example.org/bob/', 4, [qw(rdf:type foaf:knows foaf:name)] ); # my $query = RDF::Query::Federate->new( <<"END", { optimize => 1, optimistic_threshold_time => 0 } ); # PREFIX foaf: # SELECT ?p ?name WHERE { # ?p foaf:knows ?knows ; foaf:name ?name. # } # END # $query->add_service( $alice_sd ); # $query->add_service( $bob_sd ); # my ($plan, $ctx) = $query->prepare(); # # my %pids; # while (my($name, $model) = each(%models)) { # my $pid = start_endpoint_for_service( $ports{ $name }, $model ); # $pids{ $name } = $pid; # } # # my $iter = $query->execute_plan( $plan, $ctx ); # # my %names; # my %origins; # my $counter = 0; # while (my $row = $iter->next) { # my $orig = join(',', sort @{ $row->label('origin') }); # $origins{ $orig }++; # $counter++; # $names{ $row->{name}->literal_value }++; # } # # # we expect to find: # # - one result with name=Bob from the optimistic BGP sent to bob's server on port 8891 # # - one result with name=Alice from alice's server on port 8889 # # - two results from the local join that merges data from both servers, one with name=Alice, and one with name=Bob # is( $counter, 4, 'expected result count with duplicates from optimistic execution' ); # is_deeply( \%names, { Bob => 2, Alice => 2 }, 'expected duplicate result counts per result' ); # is_deeply( \%origins, { 'http://127.0.0.1:8889/sparql' => 2, 'http://127.0.0.1:8891/sparql' => 2 }, 'expected originating endpoint distribution' ); # # while (my($name, $pid) = each(%pids)) { # kill_endpoint( $pid, $quit_sig ); # } # sleep 1; # } # # sub start_endpoint_for_service { # my $req_port = shift; # my $model = shift; # my ($pid, $port) = RDF::Query::Util::start_endpoint( $model, $req_port++, '../RDF-Endpoint/include' ); # return $pid; # } # # sub kill_endpoint { # my $pid = shift; # my $quit_sig = shift; # my $sent = kill( $quit_sig, $pid ); # } # # sub local_sd { # my $name = shift; # my $port = shift; # my $base = shift; # my $size = shift; # my $preds = shift || []; # my $pred_rdf = join("\n\t", map { "sd:capability [ sd:predicate $_ ] ;" } @$preds); # my $rdf = sprintf( <<'END', $name, $port, $size, $pred_rdf ); # @prefix rdfs: . # @prefix rdf: . # @prefix xsd: . # @prefix sd: . # @prefix foaf: . # @prefix saddle: . # @prefix sparql: . # @prefix geo: . # @prefix exif: . # @prefix dc: . # @prefix dcterms: . # @prefix ex: . # # # definition of an endpoint # [] a sd:Service ; # rdfs:label "SPARQL Endpoint for data from %s" ; # sd:url ; # sd:totalTriples %d ; # %s # . # END # my $store = RDF::Trine::Store::DBI->temporary_store(); # my $model = RDF::Trine::Model->new( $store ); # my $parser = RDF::Trine::Parser->new('turtle'); # $parser->parse_into_model( $base, $rdf, $model ); # return RDF::Query::ServiceDescription->new_with_model( $model ); # } # # sub _CLEAN_WS { # my $string = shift; # $string =~ s/^\s+//; # chomp($string); # for ($string) { # s/\s+/ /g; # 1 while s/[)]\s+[)]/))/g; # } # return $string; # } RDF-Query-2.910/xt/parser-rdql.t000644 000765 000024 00000021152 11707542737 016424 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use Test::More tests => 5; use RDF::Query::Node qw(variable); use_ok( 'RDF::Query::Parser::RDQL' ); my $parser = new RDF::Query::Parser::RDQL (undef); isa_ok( $parser, 'RDF::Query::Parser::RDQL' ); { my $rdql = <<"END"; SELECT ?page WHERE (?person foaf:name "Gregory Todd Williams") (?person foaf:homepage ?page) USING rdf FOR , foaf FOR , dcterms FOR , geo FOR END my $correct = { 'triples' => [ bless( [ bless( [ bless( [ bless( [ 'person' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/name' ], 'RDF::Query::Node::Resource' ), bless( [ 'Gregory Todd Williams' ], 'RDF::Query::Node::Literal' ) ], 'RDF::Query::Algebra::Triple' ), bless( [ bless( [ 'person' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/homepage' ], 'RDF::Query::Node::Resource' ), bless( [ 'page' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ), [ bless( [ 'page' ], 'RDF::Query::Node::Variable' ) ] ], 'RDF::Query::Algebra::Project' ) ], 'sources' => undef, 'variables' => [ bless( [ 'page' ], 'RDF::Query::Node::Variable' ) ], 'method' => 'SELECT', 'namespaces' => { 'geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#', 'foaf' => 'http://xmlns.com/foaf/0.1/', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'dcterms' => 'http://purl.org/dc/terms/' } }; my $parsed = $parser->parse( $rdql ); is_deeply( $parsed, $correct, 'SELECT, WHERE, USING' ); } { my $rdql = <<"END"; SELECT ?image ?point ?lat WHERE (?point geo:lat ?lat) (?image ?pred ?point) AND (?pred == || ?pred == ) AND ?lat > 52.988674, ?lat < 53.036526 USING rdf FOR , foaf FOR , dcterms FOR , geo FOR END my $correct = { method => 'SELECT', 'triples' => [ bless( [ bless( [ 'FILTER', bless( [ bless( [ 'URI', 'sparql:logical-and' ], 'RDF::Query::Node::Resource' ), bless( [ bless( [ 'URI', 'sparql:logical-or' ], 'RDF::Query::Node::Resource' ), bless( [ '==', bless( [ 'pred' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://purl.org/dc/terms/spatial' ], 'RDF::Query::Node::Resource' ) ], 'RDF::Query::Expression::Binary' ), bless( [ '==', bless( [ 'pred' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/based_near' ], 'RDF::Query::Node::Resource' ) ], 'RDF::Query::Expression::Binary' ) ], 'RDF::Query::Expression::Function' ), bless( [ '>', bless( [ 'lat' ], 'RDF::Query::Node::Variable' ), bless( [ '52.988674', undef, 'http://www.w3.org/2001/XMLSchema#float' ], 'RDF::Query::Node::Literal' ) ], 'RDF::Query::Expression::Binary' ) ], 'RDF::Query::Expression::Function' ), bless( [ bless( [ bless( [ 'point' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://www.w3.org/2003/01/geo/wgs84_pos#lat' ], 'RDF::Query::Node::Resource' ), bless( [ 'lat' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ), bless( [ bless( [ 'image' ], 'RDF::Query::Node::Variable' ), bless( [ 'pred' ], 'RDF::Query::Node::Variable' ), bless( [ 'point' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ) ], 'RDF::Query::Algebra::Filter' ), [ bless( [ 'image' ], 'RDF::Query::Node::Variable' ), bless( [ 'point' ], 'RDF::Query::Node::Variable' ), bless( [ 'lat' ], 'RDF::Query::Node::Variable' ) ] ], 'RDF::Query::Algebra::Project' ) ], 'sources' => undef, 'namespaces' => {'foaf' => 'http://xmlns.com/foaf/0.1/','geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#','dcterms' => 'http://purl.org/dc/terms/','rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}, 'variables' => [variable('image'),variable('point'),variable('lat')] }; my $parsed = $parser->parse( $rdql ); is_deeply( $parsed, $correct, 'VarUri EQ OR constraint, numeric comparison constraint' ); } { my $rdql = <<"END"; SELECT ?person ?homepage WHERE (?person foaf:name "Gregory Todd Williams") (?person foaf:homepage ?homepage) AND ?homepage ~~ /kasei/ USING rdf FOR , foaf FOR , dcterms FOR , geo FOR END my $correct = { method => 'SELECT', 'triples' => [ bless( [ bless( [ 'FILTER', bless( [ bless( [ 'URI', 'sparql:regex' ], 'RDF::Query::Node::Resource' ), bless( [ 'homepage' ], 'RDF::Query::Node::Variable' ), bless( [ 'kasei' ], 'RDF::Query::Node::Literal' ) ], 'RDF::Query::Expression::Function' ), bless( [ bless( [ bless( [ 'person' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/name' ], 'RDF::Query::Node::Resource' ), bless( [ 'Gregory Todd Williams' ], 'RDF::Query::Node::Literal' ) ], 'RDF::Query::Algebra::Triple' ), bless( [ bless( [ 'person' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/homepage' ], 'RDF::Query::Node::Resource' ), bless( [ 'homepage' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ) ], 'RDF::Query::Algebra::Filter' ), [ bless( [ 'person' ], 'RDF::Query::Node::Variable' ), bless( [ 'homepage' ], 'RDF::Query::Node::Variable' ) ] ], 'RDF::Query::Algebra::Project' ) ], 'namespaces' => {'foaf' => 'http://xmlns.com/foaf/0.1/','rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#','geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#','dcterms' => 'http://purl.org/dc/terms/'}, 'sources' => undef, 'variables' => [bless(['person'], 'RDF::Query::Node::Variable'),bless(['homepage'], 'RDF::Query::Node::Variable')] }; my $parsed = $parser->parse( $rdql ); is_deeply( $parsed, $correct, 'regex constraint' ); } RDF-Query-2.910/xt/parser-sparql.t000644 000765 000024 00000531225 11707542740 016765 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 156; use YAML; use Data::Dumper; use Scalar::Util qw(reftype); use RDF::Query; use_ok( 'RDF::Query::Parser::SPARQL' ); my $parser = new RDF::Query::Parser::SPARQL (); isa_ok( $parser, 'RDF::Query::Parser::SPARQL' ); my (@data) = YAML::Load(do { local($/) = undef; }); foreach (@data) { next unless (reftype($_) eq 'ARRAY'); my ($name, $sparql, $correct) = @$_; my $parsed = $parser->parse( $sparql ); my $r = is_deeply( $parsed, $correct, $name ); unless ($r) { warn 'PARSE ERROR: ' . $parser->error; # my $triples = $parsed->{triples} || []; # foreach my $t (@$triples) { # warn $t->as_sparql . "\n"; # } # warn Dumper($parsed); my $dump = YAML::Dump($parsed); $dump =~ s/\n/\n /g; warn $dump; exit; } } sub _____PATTERNS______ {} ##### PATTERNS { my $pattern = $parser->parse_pattern( "{ ?s ?p ?o }" ); isa_ok( $pattern, 'RDF::Query::Algebra::GroupGraphPattern' ); my $expect = bless( [ bless( [ bless( [ bless( [ 's' ], 'RDF::Query::Node::Variable' ), bless( [ 'p' ], 'RDF::Query::Node::Variable' ), bless( [ 'o' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::BasicGraphPattern' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ); is_deeply( $pattern, $expect, 'GGP with variables' ); } { my $pattern = $parser->parse_pattern( "{ ?s a ?o }" ); isa_ok( $pattern, 'RDF::Query::Algebra::GroupGraphPattern' ); my $expect = bless( [ bless( [ bless( [ bless( [ 's' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ], 'RDF::Query::Node::Resource' ), bless( [ 'o' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::BasicGraphPattern' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ); is_deeply( $pattern, $expect, 'GGP with variables and Verb' ); } { my $pattern = $parser->parse_pattern( "{ ?s a foaf:Person ; foaf:name ?o }", undef, { foaf => 'http://xmlns.com/foaf/0.1/' } ); isa_ok( $pattern, 'RDF::Query::Algebra::GroupGraphPattern' ); my $expect = bless( [ bless( [ bless( [ bless( [ 's' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ], 'RDF::Query::Node::Resource' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/Person' ], 'RDF::Query::Node::Resource' ), ], 'RDF::Query::Algebra::Triple' ), bless( [ bless( [ 's' ], 'RDF::Query::Node::Variable' ), bless( [ 'URI', 'http://xmlns.com/foaf/0.1/name' ], 'RDF::Query::Node::Resource' ), bless( [ 'o' ], 'RDF::Query::Node::Variable' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::BasicGraphPattern' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ); is_deeply( $pattern, $expect, 'GGP with multiple triples and QName' ); } sub _____ERRORS______ {} ##### ERRORS { my $sparql = <<"END"; # Multiple DOTs SELECT * WHERE { ?s ?p ?o .. } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'syn-bad-09.rq: Extra dot after triple' ); like( $parser->error, qr/Syntax error/, 'got error: Extra dot after triple' ); } { my $sparql = <<"END"; # DOT, no triples SELECT * WHERE { . } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'Extra dot in empty GGP' ); like( $parser->error, qr/Syntax error/, 'got error: Extra dot in empty GGP' ); } { my $sparql = <<"END"; # Missing DOT between triples PREFIX : SELECT * { :s1 :p1 :o1 :s2 :p2 :o2 . } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'Missing DOT between triples' ); like( $parser->error, qr/Syntax error/, 'got error: Missing DOT between triples' ); } { my $sparql = <<"END"; PREFIX rdf: SELECT ?node WHERE { ?node rdf:type . } extra stuff END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'extra input after query' ); like( $parser->error, qr/Remaining input/, 'got error: remaining input' ); } { my $sparql = <<"END"; PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { { ?book dc10:title ?title . ?book dc10:creator ?author } UNION ?foo } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing union part' ); like( $parser->error, qr/Expected GroupGraphPattern/, 'got error: Expected GroupGraphPattern' ); } { my $sparql = <<"END"; PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { ?book dc10:title ?title . ?book dc10:creator ?author . FILTER } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing filter' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/^Expected FILTER declaration/, 'got expected error' ); } { my $sparql = <<"END"; PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { ?book dc10:title ?title . FILTER( ?title = ) . } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad syntax in filter' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/^Expecting numeric expression/, 'got expected error' ); } { my $sparql = <<"END"; PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { ?book dc10:title ?title . FILTER( ?title = foo ) . } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad syntax in filter' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/^Expecting ":"/, 'got expected error' ); } { my $sparql = <<"END"; PREFIX dc: SELECT ?title ?author WHERE { ?book dc:title ?title ; dc:identifier ?id . FILTER( ?id < 2 * ) . } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad syntax in filter' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/^Expecting unary expression after '*'/, 'got expected error' ); } { my $sparql = <<"END"; PREFIX foaf: SELECT ?x WHERE { (1 2) foaf:name } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing object' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting object after predicate/, 'parse error' ); } { my $sparql = <<"END"; PREFIX foaf: SELECT ?x WHERE { [] foaf:name } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing object' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting object after predicate/, 'parse error' ); } { my $sparql = <<"END"; PREFIX foaf: SELECT ?x WHERE { ?x foaf:name } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing object' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting object after predicate/, 'parse error' ); } { my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . FILTER( 10 > ?lat + ) } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing multiplicative expression' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting multiplicative expression after '[+]'/, 'parse error' ); } { my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . FILTER( ! ) } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'missing multiplicative expression' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting primary expression after '[!]'/, 'parse error' ); } { my $sparql = <<"END"; PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY ASC END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad ORDER BY expression' ); like( $parser->error, qr/Expected BrackettedExpression/, 'parse error' ); } { my $sparql = <<"END"; PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad ORDER BY expression' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting ORDER BY expression/, 'parse error' ); } { my $sparql = <<"END"; FOO ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad query type expression' ); like( $parser->error, qr/Expected query type/, 'got expected syntax error' ); } { my $sparql = <<"END"; SELECT ?name WHERE { ] } ORDER BY END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, 'bad triple pattern' ); like( $parser->error, qr/^Syntax error/, 'got expected syntax error' ); # XXX # like( $parser->error, qr/Expecting "}"/, 'parse error' ); } { my $sparql = <<"END"; PREFIX : SELECT * WHERE { _:a ?p ?v . { _:a ?q 1 } } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, '(DAWG) syn-bad-34.rq' ); if ($parsed) { warn "unexpected parse tree: " . Dumper($parsed); } like( $parser->error, qr/^Same blank node identifier/, 'got expected syntax error' ); # XXX } { my $sparql = <<"END"; PREFIX : SELECT * WHERE { { _:a ?p ?v . } _:a ?q 1 } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, '(DAWG) syn-bad-37.rq' ); if ($parsed) { warn "unexpected parse tree: " . Dumper($parsed); } like( $parser->error, qr/^Same blank node identifier/, 'got expected syntax error' ); # XXX } { my $sparql = <<'END'; # bad: re-used BNode label after GRAPH # $Id: syn-bad-GRAPH-breaks-BGP.rq,v 1.1 2007/02/15 15:14:31 eric Exp $ PREFIX : SELECT * WHERE { _:a ?p ?v . GRAPH ?g { ?s ?p ?v } _:a ?q 1 } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, '(DAWG) syn-bad-GRAPH-breaks-BGP.rq' ); if ($parsed) { warn "unexpected parse tree: " . Dumper($parsed); } like( $parser->error, qr/^Same blank node identifier/, 'got expected syntax error' ); # XXX } { my $sparql = <<"END"; PREFIX : SELECT * WHERE { _:a ?p ?v . OPTIONAL { ?s ?p ?v } _:a ?q 1 } END my $parsed = $parser->parse( $sparql ); is( $parsed, undef, '(DAWG) syn-bad-OPT-breaks-BGP.rq' ); if ($parsed) { warn "unexpected parse tree: " . Dumper($parsed); } like( $parser->error, qr/^Same blank node identifier/, 'got expected syntax error' ); # XXX } __END__ --- - single triple; no prefix - | SELECT ?node WHERE { ?node a . } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - simple DESCRIBE - | DESCRIBE ?node WHERE { ?node a } - method: DESCRIBE namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog variables: - !!perl/array:RDF::Query::Node::Variable - node --- - SELECT, WHERE, USING - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?page WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?page . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - SELECT, WHERE, USING; variables with "$" - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT $page WHERE { $person foaf:name "Gregory Todd Williams" . $person foaf:homepage $page . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - VarUri EQ OR constraint, numeric comparison constraint - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( (?pred = || ?pred = ) && ?lat > 52.988674 && ?lat < 53.036526 ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-and - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-or - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/based_near - !!perl/array:RDF::Query::Expression::Binary - '>' - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - 52.988674 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - 53.036526 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - regex constraint; no trailing '.' - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER REGEX(?homepage, "kasei") } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Node::Variable - homepage - !!perl/array:RDF::Query::Node::Literal - kasei - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with variable/function-call equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX func: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person ?pred ?homepage . FILTER( ?pred = func:homepagepred() ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# func: http://example.com/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/homepagepred - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with variable/function-call equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person ?pred ?homepage . FILTER( ?pred = () ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - func:homepagepred - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with LANG(?var)/literal equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( LANG(?name) = 'en' ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:lang - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - en - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with LANGMATCHES(?var, 'literal') - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( LANGMATCHES(?name, "foo"@en ) ). } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:langmatches - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - foo - en - ~ - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with isLITERAL(?var) - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( isLITERAL(?name) ). } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:isliteral - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with DATATYPE(?var)/URI equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( DATATYPE(?name) = rdf:Literal ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:datatype - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#Literal - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - multiple attributes using ';' - | PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" ; foaf:homepage ?homepage . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *2 --- - predicate with full qURI - | PREFIX foaf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams", "Greg Williams" . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - *2 - !!perl/array:RDF::Query::Node::Literal - Greg Williams - &3 - !!perl/array:RDF::Query::Node::Variable - person variables: *3 --- - "'a' rdf:type" - | PREFIX foaf: SELECT ?person WHERE { ?person foaf:Person } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - &1 - !!perl/array:RDF::Query::Node::Variable - person variables: *1 --- - "'a' rdf:type; multiple attributes using ';'" - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person ; foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - "blank node subject; multiple attributes using ';'" - | PREFIX foaf: SELECT ?nick WHERE { [ foaf:name "Gregory Todd Williams" ; foaf:nick ?nick ] . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - &2 - !!perl/array:RDF::Query::Node::Variable - nick variables: *2 --- - "blank node subject; using brackets '[...]'; 'a' rdf:type" - | PREFIX foaf: SELECT ?name WHERE { [ a foaf:Person ] foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - "blank node subject; empty brackets '[]'" - | PREFIX foaf: SELECT ?name WHERE { [] foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - blank node object - | PREFIX dao: PREFIX dc: PREFIX beer: SELECT ?name WHERE { ?me dao:consumed [ a beer:Ale ; beer:name ?name ] . } - method: SELECT namespaces: beer: http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer# dao: http://kasei.us/ns/dao# dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer#Ale - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer#name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - me - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/ns/dao#consumed - *1 - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - blank node; using qName _:abc - | PREFIX foaf: SELECT ?name WHERE { _:abc foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - abc - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - select with ORDER BY - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY ?name - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with DISTINCT - | PREFIX foaf: SELECT DISTINCT ?name WHERE { ?person a foaf:Person; foaf:name ?name } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Distinct - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; asc() - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY asc( ?name ) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; DESC() - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; DESC(); with LIMIT - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) LIMIT 10 - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name - 10 variables: *2 --- - select with ORDER BY; DESC(); with LIMIT - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) LIMIT 10 OFFSET 10 - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Offset - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name - 10 - 10 variables: *2 --- - select with ORDER BY; DESC(); with LIMIT; variables with "$" - |2 PREFIX foaf: PREFIX dc: select $pic $thumb $date WHERE { $pic foaf:thumbnail $thumb . $pic dc:date $date } order by desc($date) limit 10 - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/thumbnail - !!perl/array:RDF::Query::Node::Variable - thumb - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &1 - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Variable - thumb - !!perl/array:RDF::Query::Node::Variable - date - 10 variables: *1 --- - FILTER function call 1 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, +41.849331, -71.392) < 10 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - +41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - 10 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - OLDFILTER function call 2 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 5 + 5 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - + - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - OLDFILTER function call 3 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 5 * 5 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - '*' - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - multiple FILTERs; with function call - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?name WHERE { ?image dcterms:spatial ?point . ?point foaf:name ?name . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 10 ) . FILTER REGEX(?name, "Providence, RI") } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - 'Providence, RI' - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - 10 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - "optional triple '{...}'" - |2 PREFIX foaf: SELECT ?person ?name ?mbox WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox variables: *1 --- - "optional triples '{...; ...}'" - |2 PREFIX foaf: SELECT ?person ?name ?mbox ?nick WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox; foaf:nick ?nick } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox - !!perl/array:RDF::Query::Node::Variable - nick variables: *2 --- - union; sparql 6.2 - |2 PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { { ?book dc10:title ?title . ?book dc10:creator ?author } UNION { ?book dc11:title ?title . ?book dc11:creator ?author } } - method: SELECT namespaces: dc10: http://purl.org/dc/elements/1.1/ dc11: http://purl.org/dc/elements/1.0/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/creator - !!perl/array:RDF::Query::Node::Variable - author - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.0/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.0/creator - !!perl/array:RDF::Query::Node::Variable - author - &1 - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Node::Variable - author variables: *1 --- - literal language tag @en - |2 PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gary P"@en ; foaf:homepage ?homepage . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gary P - en - ~ - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *2 --- - typed literal ^^URI - |2 PREFIX dc: PREFIX foaf: SELECT ?image WHERE { ?image dc:date "2005-04-07T18:27:56-04:00"^^ } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Literal - 2005-04-07T18:27:56-04:00 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - &1 - !!perl/array:RDF::Query::Node::Variable - image variables: *1 --- - typed literal ^^qName - |2 PREFIX foaf: PREFIX dc: PREFIX xs: SELECT ?image WHERE { ?image dc:date "2005-04-07T18:27:56-04:00"^^xs:dateTime } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ dc: http://purl.org/dc/elements/1.1/ xs: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Literal - 2005-04-07T18:27:56-04:00 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - &1 - !!perl/array:RDF::Query::Node::Variable - image variables: *1 --- - subject collection syntax - |2 SELECT ?x WHERE { (1 ?x 3) } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - subject collection syntax; with pred-obj. - |2 PREFIX foaf: SELECT ?x WHERE { (1 ?x 3) foaf:name "My Collection" } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - My Collection - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - subject collection syntax; object collection syntax - |2 PREFIX dc: SELECT ?x WHERE { (1 ?x 3) dc:subject (1 2 3) } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - &6 !!perl/array:RDF::Query::Node::Blank - BLANK - a5 - &7 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *6 - &8 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &9 !!perl/array:RDF::Query::Node::Blank - BLANK - a6 - !!perl/array:RDF::Query::Algebra::Triple - *9 - *7 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *9 - *8 - &10 !!perl/array:RDF::Query::Node::Blank - BLANK - a7 - !!perl/array:RDF::Query::Algebra::Triple - *10 - *7 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *10 - *8 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/subject - *6 - &11 - !!perl/array:RDF::Query::Node::Variable - x variables: *11 --- - object collection syntax - |2 PREFIX test: SELECT ?x WHERE { test:mycollection (1 ?x 3) . } - method: SELECT namespaces: test: http://kasei.us/e/ns/test# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/about/foaf.xrdf#greg - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/test#mycollection - *1 - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - SELECT * - |2 SELECT * WHERE { ?a ?a ?b . } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - &1 - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b variables: - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b --- - default prefix - |2 PREFIX : SELECT ?person WHERE { ?person :name "Gregory Todd Williams", "Greg Williams" . } - method: SELECT namespaces: __DEFAULT__: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - *2 - !!perl/array:RDF::Query::Node::Literal - Greg Williams - &3 - !!perl/array:RDF::Query::Node::Variable - person variables: *3 --- - select from named; single triple; no prefix - |2 PREFIX foaf: SELECT ?src ?name FROM NAMED FROM NAMED WHERE { GRAPH ?src { ?x foaf:name ?name } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: - - !!perl/array:RDF::Query::Node::Resource - URI - file://data/named_graphs/alice.rdf - NAMED - - !!perl/array:RDF::Query::Node::Resource - URI - file://data/named_graphs/bob.rdf - NAMED triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - src - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Quad - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - *1 - &2 - !!perl/array:RDF::Query::Node::Variable - src - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - ASK FILTER; using <= (shouldn't parse as '<') - |2 PREFIX xsd: ASK { FILTER ( "1995-11-05"^^xsd:dateTime <= "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } - method: ASK namespaces: xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression - <= - !!perl/array:RDF::Query::Node::Literal - 1995-11-05 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - !!perl/array:RDF::Query::Node::Literal - 1994-11-05T13:15:30Z - ~ - http://www.w3.org/2001/XMLSchema#dateTime - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] variables: [] --- - ORDER BY with expression - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX xsd: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . } ORDER BY ASC( xsd:decimal( ?lat ) ) - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Variable - lat - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - triple pattern with trailing internal '.' - | PREFIX rdf: PREFIX foaf: PREFIX cyc: PREFIX dcterms: PREFIX dc: SELECT ?place ?img ?date WHERE { ?region foaf:name "Maine" . ?p cyc:inRegion ?region; foaf:name ?place . ?img dcterms:spatial ?p . ?img dc:date ?date; rdf:type foaf:Image . } ORDER BY DESC(?date) LIMIT 10 - method: SELECT namespaces: cyc: http://www.cyc.com/2004/06/04/cyc# dc: http://purl.org/dc/elements/1.1/ dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - region - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Maine - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://www.cyc.com/2004/06/04/cyc#inRegion - !!perl/array:RDF::Query::Node::Variable - region - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - place - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Algebra::Triple - &2 !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - !!perl/array:RDF::Query::Algebra::Triple - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &3 - !!perl/array:RDF::Query::Node::Variable - place - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Variable - date - 10 variables: *3 --- - "[bug] query with predicate starting with 'a' (confused with { ?subj a ?type})" - |2 PREFIX rdf: PREFIX foaf: PREFIX cyc: PREFIX dcterms: PREFIX dc: PREFIX album: PREFIX p: SELECT ?img ?date WHERE { album:image ?img . ?img dc:date ?date ; rdf:type foaf:Image . } ORDER BY DESC(?date) - method: SELECT namespaces: album: http://kasei.us/e/ns/album# cyc: http://www.cyc.com/2004/06/04/cyc# dc: http://purl.org/dc/elements/1.1/ dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ p: http://www.usefulinc.com/picdiary/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/pictures/parties/19991205-Tims_Party/ - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/album#image - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &2 - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Variable - date variables: *2 --- - dawg/simple/01 - |2 PREFIX : SELECT * WHERE { :x ?p ?q . } - method: SELECT namespaces: __DEFAULT__: http://example.org/data/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/data/x - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q --- - single triple with comment; dawg/data/part1 - |2 # Get name, and optionally the mbox, of each person PREFIX foaf: SELECT ?name ?mbox WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox} } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &1 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox variables: *1 --- - ask query - | ASK { ?node a . } - method: ASK namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog variables: [] --- - blank-pred-blank - | PREFIX rdf: PREFIX foaf: SELECT ?name WHERE { [ foaf:name ?name ] foaf:maker [] } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/maker - !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - Filter with unary-plus - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( ?lat > +52 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - '>' - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - +52 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - Filter with isIRI - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( isIRI(?image) ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:isiri - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - 'xsd:double' - | PREFIX dc: SELECT ?node WHERE { ?node dc:identifier 1e4 . } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/identifier - !!perl/array:RDF::Query::Node::Literal - 1e4 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - boolean literal - | PREFIX dc: SELECT ?node WHERE { ?node dc:identifier true . } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/identifier - !!perl/array:RDF::Query::Node::Literal - true - ~ - http://www.w3.org/2001/XMLSchema#boolean - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - select with ORDER BY function call - | PREFIX foaf: PREFIX : SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY :foo(?name) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ __DEFAULT__: http://example.com/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/foo - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with bnode object as second pred-obj - | PREFIX rdf: PREFIX foaf: SELECT ?name WHERE { ?r foaf:name ?name ; foaf:maker [ a foaf:Person ] } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - &2 !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/maker - *1 - &3 - !!perl/array:RDF::Query::Node::Variable - name variables: *3 --- - select with qname with '-2' suffix - | PREFIX foaf: PREFIX wn: SELECT ?thing WHERE { ?image a foaf:Image ; foaf:depicts ?thing . ?thing a wn:Flower-2 . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ wn: http://xmlns.com/wordnet/1.6/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/depicts - !!perl/array:RDF::Query::Node::Variable - thing - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - thing - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/wordnet/1.6/Flower-2 - &2 - !!perl/array:RDF::Query::Node::Variable - thing variables: *2 --- - select with qname with underscore - | PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person ; foaf:mbox_sha1sum "2057969209f1dfdad832de387cf13e6ff8c93b12" ; foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox_sha1sum - !!perl/array:RDF::Query::Node::Literal - 2057969209f1dfdad832de387cf13e6ff8c93b12 - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - construct with one construct triple - | PREFIX foaf: CONSTRUCT { ?person foaf:name ?name } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name --- - construct with two construct triples - | PREFIX foaf: CONSTRUCT { ?person foaf:name ?name . ?person a foaf:Person } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person --- - construct with three construct triples - | PREFIX foaf: CONSTRUCT { ?person a foaf:Person . ?person foaf:name ?name . ?person foaf:firstName ?name } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name --- - select with triple-optional-triple - | PREFIX foaf: SELECT ?person ?nick ?page WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:nick ?nick } . ?person foaf:homepage ?page } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - nick - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - select with FROM - | PREFIX foaf: PREFIX geo: SELECT ?lat ?long FROM WHERE { ?point a geo:Point ; geo:lat ?lat ; geo:long ?long . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# sources: - - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/code/rdf-query/test-data/greenwich.rdf triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#Point - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#long - !!perl/array:RDF::Query::Node::Variable - long - &2 - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Variable - long variables: *2 --- - select with graph-triple-triple - | # select all the email addresses ever held by the person # who held a given email address on 2007-01-01 PREFIX foaf: PREFIX t: SELECT ?mbox WHERE { GRAPH ?time { ?p foaf:mbox } . ?time t:inside "2007-01-01" . ?p foaf:mbox ?mbox . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ t: http://www.w3.org/2006/09/time# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - time - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Quad - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Resource - URI - mailto:gtw@cs.umd.edu - *1 - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - time - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2006/09/time#inside - !!perl/array:RDF::Query::Node::Literal - 2007-01-01 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &2 - !!perl/array:RDF::Query::Node::Variable - mbox variables: *2 --- - (DAWG) syn-leading-digits-in-prefixed-names.rq - | PREFIX dob: PREFIX t: PREFIX dc: SELECT ?desc WHERE { dob:1D a t:ProperInterval; dc:description ?desc. } - method: SELECT namespaces: dob: http://placetime.com/interval/gregorian/1977-01-18T04:00:00Z/P t: http://www.ai.sri.com/daml/ontologies/time/Time.daml# dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Resource - URI - http://placetime.com/interval/gregorian/1977-01-18T04:00:00Z/P1D - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.ai.sri.com/daml/ontologies/time/Time.daml#ProperInterval - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/description - !!perl/array:RDF::Query::Node::Variable - desc - &2 - !!perl/array:RDF::Query::Node::Variable - desc variables: *2 --- - (DAWG) syn-07.rq - | # Trailing ; PREFIX : SELECT * WHERE { :s :p :o ; FILTER(?x) } - method: SELECT namespaces: __DEFAULT__: http://example/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#s - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#p - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#o - &1 - !!perl/array:RDF::Query::Node::Variable - x variables: - !!perl/array:RDF::Query::Node::Variable - x --- - (DAWG) syn-08.rq - | # Broken ; PREFIX : SELECT * WHERE { :s :p :o ; . } - method: SELECT namespaces: __DEFAULT__: http://example/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#s - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#p - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#o - &1 [] variables: [] --- - (DAWG) syn-11.rq - | PREFIX : SELECT * WHERE { _:a ?p ?v . FILTER(true) . [] ?q _:a } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Node::Literal - true - ~ - http://www.w3.org/2001/XMLSchema#boolean - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - a - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q --- - (DAWG) syntax-form-describe01.rq - | DESCRIBE - method: DESCRIBE namespaces: {} sources: [] triples: [] variables: - !!perl/array:RDF::Query::Node::Resource - URI - u --- - (DAWG) syntax-form-construct04.rq - | PREFIX rdf: CONSTRUCT { [] rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o . } WHERE {?s ?p ?o} - method: CONSTRUCT namespaces: rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#subject - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#object - !!perl/array:RDF::Query::Node::Variable - o --- - (DAWG) syntax-lists-02.rq - | PREFIX : SELECT * WHERE { ?x :p ( ?z ) } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#p - *1 - &2 - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Node::Variable - x variables: - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Node::Variable - x --- - (DAWG) syntax-qname-03.rq - | PREFIX : SELECT * WHERE { :_1 :p.rdf :z.z . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#_1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#p.rdf - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z.z - &1 [] variables: [] --- - (DAWG) syntax-qname-08.rq - | BASE PREFIX : <#> PREFIX x.y: SELECT * WHERE { :a.b x.y: : . } - method: SELECT namespaces: __DEFAULT__: http://example.org/# x.y: http://example.org/x# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#a.b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/x# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/# - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-07.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p 123 } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - 123 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-08.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p 123. . } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - 123. - ~ - http://www.w3.org/2001/XMLSchema#decimal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-12.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long''\'Literal''' } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - Long'''Literal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-13.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p """Long\"""Literal""" } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - Long"""Literal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-general-07.rq - | SELECT * WHERE { +1.0 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - +1.0 - ~ - http://www.w3.org/2001/XMLSchema#decimal - &1 [] variables: [] --- - (DAWG) syntax-general-09.rq - | SELECT * WHERE { 1.0e0 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - 1.0e0 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 [] variables: [] --- - (DAWG) syntax-general-10.rq - | SELECT * WHERE { +1.0e+1 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - +1.0e+1 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 [] variables: [] --- - (DAWG) syntax-lists-03.rq - | PREFIX : SELECT * WHERE { ( ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] variables: [] --- - (DAWG) syntax-lists-04.rq - | PREFIX : SELECT * WHERE { ( 1 2 ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &5 [] variables: [] --- - (DAWG) syntax-lists-02.rq - | PREFIX : SELECT * WHERE { ( ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] variables: [] --- - (DAWG) syntax-lists-04.rq - | PREFIX : SELECT * WHERE { ( 1 2 ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &5 [] variables: [] --- - (DAWG) dawg-eval - | PREFIX rdf: PREFIX ex: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(str(?val), "example\\.com") } - method: SELECT namespaces: rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# ex: http://example.com/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:str - !!perl/array:RDF::Query::Node::Variable - val - !!perl/array:RDF::Query::Node::Literal - example\.com - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/#foo - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#value - !!perl/array:RDF::Query::Node::Variable - val - &1 - !!perl/array:RDF::Query::Node::Variable - val variables: *1 --- - (DAWG) dawg-eval: sameTerm - | PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( !sameTerm(?v1, ?v2) && ?v1 = ?v2 ) } - method: SELECT namespaces: __DEFAULT__: http://example.org/things# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-and - !!perl/array:RDF::Query::Expression::Unary - '!' - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:sameterm - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v2 - &1 - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Variable - v2 variables: *1 --- - (DAWG) dawg-eval: basic/manifest#term-8 - | PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p +5 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Literal - +5 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 - !!perl/array:RDF::Query::Node::Variable - p variables: - !!perl/array:RDF::Query::Node::Variable - p --- - (DAWG) dawg-eval: algebra/manifest#filter-nested-2 - | PREFIX : SELECT ?v { :x :p ?v . { FILTER(?v = 1) } } - method: SELECT namespaces: __DEFAULT__: http://example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/x - !!perl/array:RDF::Query::Node::Resource - URI - http://example/p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - &1 - !!perl/array:RDF::Query::Node::Variable - v variables: *1 --- - (DAWG) dawg-eval: optional/manifest#dawg-optional-complex-4 - | PREFIX foaf: PREFIX ex: SELECT ?name ?plan ?dept ?img FROM <...> FROM NAMED <...> WHERE { ?person foaf:name ?name { ?person ex:healthplan ?plan } UNION { ?person ex:department ?dept } OPTIONAL { ?person a foaf:Person GRAPH ?g { [] foaf:name ?name; foaf:depiction ?img } } } - method: SELECT namespaces: ex: http://example.org/things# foaf: http://xmlns.com/foaf/0.1/ sources: - - !!perl/array:RDF::Query::Node::Resource - URI - ... - - !!perl/array:RDF::Query::Node::Resource - URI - ... - NAMED triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#healthplan - !!perl/array:RDF::Query::Node::Variable - plan - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#department - !!perl/array:RDF::Query::Node::Variable - dept - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - g - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Quad - &2 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - *1 - !!perl/array:RDF::Query::Algebra::Quad - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/depiction - !!perl/array:RDF::Query::Node::Variable - img - *1 - &3 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - plan - !!perl/array:RDF::Query::Node::Variable - dept - !!perl/array:RDF::Query::Node::Variable - img variables: *3 --- - (DAWG) dawg-eval: i18n/manifest#kanji-1 - | PREFIX foaf: PREFIX 食: SELECT ?name ?food WHERE { [ foaf:name ?name ; 食:食べる ?food ] . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ 食: http://www.w3.org/2001/sw/DataAccess/tests/data/i18n/kanji.ttl# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2001/sw/DataAccess/tests/data/i18n/kanji.ttl#食べる - !!perl/array:RDF::Query::Node::Variable - food - &2 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - food variables: *2 --- - (DAWG) dawg-syntax: syntax-sparql4/manifest#syn-10 - | PREFIX : SELECT * WHERE { { _:a ?p ?v . _:a ?q _:a } UNION { _:b ?q _:c } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - b - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - c - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q --- - (DAWG) dawg-syntax: syntax-sparql1/manifest#syntax-pat-04 - | PREFIX : SELECT * { OPTIONAL{:x :y :z} ?a :b :c { :x1 :y1 :z1 } UNION { :x2 :y2 :z2 } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#c - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z1 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z2 - &1 - !!perl/array:RDF::Query::Node::Variable - a variables: - !!perl/array:RDF::Query::Node::Variable - a --- - (DAWG) dawg-syntax: syntax-sparql1/manifest#syntax-struct-10 - | PREFIX : SELECT * { OPTIONAL { :a :b :c } . ?x ?y ?z } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#a - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#c - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Variable - z - &1 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Variable - z variables: *1 --- - (DAWG) dawg-syntax: expr-equals/manifest#eq-2-1 - | PREFIX xsd: PREFIX : SELECT ?v1 ?v2 WHERE { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) . } - method: SELECT namespaces: __DEFAULT__: http://example.org/things# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v2 - &1 - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 variables: *1 --- - (DAWG) dawg-syntax: expr-ops/manifest#minus-1 - | PREFIX : SELECT ?s WHERE { ?s :p ?o . ?s2 :p ?o2 . FILTER(?o - ?o2 = 3) . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Binary - - - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Variable - o2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Variable - o2 - &1 - !!perl/array:RDF::Query::Node::Variable - s variables: *1 --- - (DAWG) dawg-syntax: syntax-qname-04.rq - | PREFIX : PREFIX a: SELECT * WHERE { : a: :a . : : : . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# a: http://example.org/ns2# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns2# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#a - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - &1 [] variables: *1 --- - (DAWG) dawg-syntax: syntax-union-02.rq - | PREFIX : SELECT * { { ?s ?p ?o } UNION { ?a ?b ?c } UNION { ?r ?s ?t } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - !!perl/array:RDF::Query::Node::Variable - c - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - t - &1 - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - !!perl/array:RDF::Query::Node::Variable - c - !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Variable - t variables: *1 --- - (DAWG) dawg-syntax: syntax-order-06.rq - | PREFIX : SELECT * { ?s ?p ?o } ORDER BY DESC(?o+57) :func2(?o) ASC(?s) - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - DESC - !!perl/array:RDF::Query::Expression::Binary - + - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Literal - 57 - ~ - http://www.w3.org/2001/XMLSchema#integer - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#func2 - !!perl/array:RDF::Query::Node::Variable - o - - ASC - !!perl/array:RDF::Query::Node::Variable - s - &1 - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o variables: *1 --- - (DAWG) dawg-syntax: syntax-bnode-02.rq - | PREFIX : # Tab SELECT * WHERE { [ ] :p [ ] } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - &1 [] variables: [] --- - (DAWG) dawg-syntax: syntax-esc-04.rq - | PREFIX : SELECT * WHERE { <\u0078> :\u0070 ?xx\u0078 } - method: SELECT namespaces: __DEFAULT__: http://example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example/p - !!perl/array:RDF::Query::Node::Variable - xxx - &1 - !!perl/array:RDF::Query::Node::Variable - xxx variables: *1 --- - CONSTRUCT with LIMIT (github pull request 17, from kjetilk) - | PREFIX rdf: PREFIX foaf: CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . } LIMIT 5 - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] options: limit: 5 triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o RDF-Query-2.910/xt/parser-sparql11.t000644 000765 000024 00000546520 12065422135 017124 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 114; use YAML; use Data::Dumper; use Scalar::Util qw(reftype); use RDF::Query; use_ok( 'RDF::Query::Parser::SPARQL11' ); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.parser = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my $parser = RDF::Query::Parser::SPARQL11->new(); isa_ok( $parser, 'RDF::Query::Parser::SPARQL11' ); my (@data) = YAML::Load(do { local($/) = undef; }); foreach (@data) { next unless (reftype($_) eq 'ARRAY'); my ($name, $sparql, $correct) = @$_; my $parsed = $parser->parse( $sparql, undef, 1 ); my $r = is_deeply( $parsed, $correct, $name ); unless ($r) { warn 'PARSE ERROR: ' . $parser->error; # my $triples = $parsed->{triples} || []; # foreach my $t (@$triples) { # warn $t->as_sparql . "\n"; # } # warn Dumper($parsed); my $dump = YAML::Dump($parsed); $dump =~ s/\n/\n /g; warn $dump; exit; } } # sub _____ERRORS______ {} # ##### ERRORS # # { # my $sparql = <<"END"; # PREFIX : # SELECT * # WHERE # { # _:a ?p ?v . { _:a ?q 1 } # } # END # my $parsed = $parser->parse( $sparql ); # is( $parsed, undef, '(DAWG) syn-bad-34.rq' ); # if ($parsed) { # warn "unexpected parse tree: " . Dumper($parsed); # } # like( $parser->error, qr/^Same blank node identifier/, 'got expected syntax error' ); # XXX # } __END__ --- - Subselect - | PREFIX : SELECT ?y ?name WHERE { :alice :knows ?y . { SELECT ?y ?name WHERE { ?y :name ?name } ORDER BY ?name LIMIT 1 } } - method: SELECT namespaces: &1 __DEFAULT__: http://people.example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/alice - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/knows - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Algebra::SubSelect - !!perl/hash:RDF::Query base: ~ parsed: method: SELECT triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - - &1 !!perl/array:RDF::Query::Node::Variable - y - &2 !!perl/array:RDF::Query::Node::Variable - name - 1 variables: - *1 - *2 - - &3 !!perl/array:RDF::Query::Node::Variable - y - &4 !!perl/array:RDF::Query::Node::Variable - name variables: - *3 - *4 --- - Subselect with differing projection - | PREFIX : SELECT ?name WHERE { :alice :knows ?y . { SELECT ?y ?name WHERE { ?y :name ?name } ORDER BY ?name LIMIT 1 } } - method: SELECT namespaces: &1 __DEFAULT__: http://people.example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/alice - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/knows - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Algebra::SubSelect - !!perl/hash:RDF::Query base: ~ parsed: method: SELECT triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Resource - URI - http://people.example/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - - &1 !!perl/array:RDF::Query::Node::Variable - y - &2 !!perl/array:RDF::Query::Node::Variable - name - 1 variables: - *1 - *2 - - &3 !!perl/array:RDF::Query::Node::Variable - name variables: - *3 --- - EXISTS filter - | SELECT * WHERE { {} FILTER(EXISTS { ?s a }) } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:exists - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - type - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - &1 [] variables: *1 --- - NOT EXISTS filter - | SELECT * WHERE { ?s a FILTER(NOT EXISTS { ?s a }) } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Unary - '!' - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:exists - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - type2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - type - - &1 !!perl/array:RDF::Query::Node::Variable - s variables: - *1 --- - SELECT expression - | PREFIX dc: PREFIX ns: SELECT ?title (?p*(1-?discount) AS ?price) { ?x ns:price ?p . ?x dc:title ?title . ?x ns:discount ?discount } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ ns: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Extend - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#price - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#discount - !!perl/array:RDF::Query::Node::Variable - discount - - &1 !!perl/array:RDF::Query::Expression::Alias - alias - &2 !!perl/array:RDF::Query::Node::Variable - price - !!perl/array:RDF::Query::Expression::Binary - '*' - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Expression::Binary - - - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Node::Variable - discount - - &3 !!perl/array:RDF::Query::Node::Variable - title - *2 variables: - *3 - *1 --- - GROUP_CONCAT Aggregate - | PREFIX dc: PREFIX ns: SELECT (GROUP_CONCAT(?title) AS ?titles) { ?x dc:title ?title . ?x ns:discount ?discount } GROUP BY ?discount - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ ns: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Extend - !!perl/array:RDF::Query::Algebra::Aggregate - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#discount - !!perl/array:RDF::Query::Node::Variable - discount - - !!perl/array:RDF::Query::Node::Variable - discount - - GROUP_CONCAT(?title) - - GROUP_CONCAT - {} - !!perl/array:RDF::Query::Node::Variable - title - - &1 !!perl/array:RDF::Query::Expression::Alias - alias - &2 !!perl/array:RDF::Query::Node::Variable - titles - !!perl/array:RDF::Query::Node::Variable::ExpressionProxy - GROUP_CONCAT(?title) - - *2 variables: - *1 --- - Aggregate with HAVING Clause - | PREFIX : SELECT (SUM(?lprice) AS ?totalPrice) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?org HAVING (SUM(?lprice) > 10) - method: SELECT namespaces: __DEFAULT__: http://books.example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - '>' - !!perl/array:RDF::Query::Node::Variable::ExpressionProxy - SUM(?lprice) - !!perl/array:RDF::Query::Node::Literal - 10 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Extend - !!perl/array:RDF::Query::Algebra::Aggregate - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - org - !!perl/array:RDF::Query::Node::Resource - URI - http://books.example/affiliates - !!perl/array:RDF::Query::Node::Variable - auth - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - auth - !!perl/array:RDF::Query::Node::Resource - URI - http://books.example/writesBook - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://books.example/price - !!perl/array:RDF::Query::Node::Variable - lprice - - !!perl/array:RDF::Query::Node::Variable - org - - SUM(?lprice) - - SUM - {} - !!perl/array:RDF::Query::Node::Variable - lprice - - &1 !!perl/array:RDF::Query::Expression::Alias - alias - &2 !!perl/array:RDF::Query::Node::Variable - totalPrice - !!perl/array:RDF::Query::Node::Variable::ExpressionProxy - SUM(?lprice) - - *2 variables: - *1 --- - single triple; no prefix - | SELECT ?node WHERE { ?node a . } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - simple DESCRIBE - | DESCRIBE ?node WHERE { ?node a } - method: DESCRIBE namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog variables: - !!perl/array:RDF::Query::Node::Variable - node --- - SELECT, WHERE - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?page WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?page . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - SELECT, WHERE; variables with "$" - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT $page WHERE { $person foaf:name "Gregory Todd Williams" . $person foaf:homepage $page . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - VarUri EQ OR constraint, numeric comparison constraint - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( (?pred = || ?pred = ) && ?lat > 52.988674 && ?lat < 53.036526 ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-and - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-or - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/based_near - !!perl/array:RDF::Query::Expression::Binary - '>' - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - 52.988674 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - 53.036526 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - regex constraint; no trailing '.' - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER REGEX(?homepage, "kasei") } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Node::Variable - homepage - !!perl/array:RDF::Query::Node::Literal - kasei - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with variable/function-call equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX func: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person ?pred ?homepage . FILTER( ?pred = func:homepagepred() ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# func: http://example.com/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/homepagepred - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with variable/function-call equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person ?pred ?homepage . FILTER( ?pred = () ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - func:homepagepred - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - homepage - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with LANG(?var)/literal equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( LANG(?name) = 'en' ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:lang - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - en - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with LANGMATCHES(?var, 'literal') - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( LANGMATCHES(?name, "foo"@en ) ). } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:langmatches - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - foo - en - ~ - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with isLITERAL(?var) - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( isLITERAL(?name) ). } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:isliteral - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - filter with DATATYPE(?var)/URI equality - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name ?name . FILTER( DATATYPE(?name) = rdf:Literal ) . } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:datatype - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#Literal - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *1 --- - multiple attributes using ';' - | PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" ; foaf:homepage ?homepage . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *2 --- - predicate with full qURI - | PREFIX foaf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams", "Greg Williams" . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - *2 - !!perl/array:RDF::Query::Node::Literal - Greg Williams - &3 - !!perl/array:RDF::Query::Node::Variable - person variables: *3 --- - "'a' rdf:type" - | PREFIX foaf: SELECT ?person WHERE { ?person foaf:Person } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - &1 - !!perl/array:RDF::Query::Node::Variable - person variables: *1 --- - "'a' rdf:type; multiple attributes using ';'" - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person ; foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - "blank node subject; multiple attributes using ';'" - | PREFIX foaf: SELECT ?nick WHERE { [ foaf:name "Gregory Todd Williams" ; foaf:nick ?nick ] . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - &2 - !!perl/array:RDF::Query::Node::Variable - nick variables: *2 --- - "blank node subject; using brackets '[...]'; 'a' rdf:type" - | PREFIX foaf: SELECT ?name WHERE { [ a foaf:Person ] foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - "blank node subject; empty brackets '[]'" - | PREFIX foaf: SELECT ?name WHERE { [] foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - blank node object - | PREFIX dao: PREFIX dc: PREFIX beer: SELECT ?name WHERE { ?me dao:consumed [ a beer:Ale ; beer:name ?name ] . } - method: SELECT namespaces: beer: http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer# dao: http://kasei.us/ns/dao# dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer#Ale - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.csd.abdn.ac.uk/research/AgentCities/ontologies/beer#name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - me - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/ns/dao#consumed - *1 - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - blank node; using qName _:abc - | PREFIX foaf: SELECT ?name WHERE { _:abc foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - abc - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - select with ORDER BY - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY ?name - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with DISTINCT - | PREFIX foaf: SELECT DISTINCT ?name WHERE { ?person a foaf:Person; foaf:name ?name } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Distinct - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; asc() - | PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY asc( ?name ) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; DESC() - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with ORDER BY; DESC(); with LIMIT - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) LIMIT 10 - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name - 10 variables: *2 --- - select with ORDER BY; DESC(); with LIMIT - |2 PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY DESC(?name) LIMIT 10 OFFSET 10 - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Offset - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - DESC - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name - 10 - 10 variables: *2 --- - select with ORDER BY; DESC(); with LIMIT; variables with "$" - |2 PREFIX foaf: PREFIX dc: select $pic $thumb $date WHERE { $pic foaf:thumbnail $thumb . $pic dc:date $date } order by desc($date) limit 10 - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/thumbnail - !!perl/array:RDF::Query::Node::Variable - thumb - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &1 - !!perl/array:RDF::Query::Node::Variable - pic - !!perl/array:RDF::Query::Node::Variable - thumb - !!perl/array:RDF::Query::Node::Variable - date - 10 variables: *1 --- - FILTER function call 1 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, +41.849331, -71.392) < 10 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - +41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - 10 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - OLDFILTER function call 2 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 5 + 5 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - + - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - OLDFILTER function call 3 - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 5 * 5 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Expression::Binary - '*' - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Node::Literal - 5 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - multiple FILTERs; with function call - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?name WHERE { ?image dcterms:spatial ?point . ?point foaf:name ?name . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 10 ) . FILTER REGEX(?name, "Providence, RI") } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# mygeo: http://kasei.us/e/ns/geo# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Literal - 'Providence, RI' - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - < - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/geo#distance - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Literal - 41.849331 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - -71.392 - ~ - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Literal - 10 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - name variables: *1 --- - "optional triple '{...}'" - |2 PREFIX foaf: SELECT ?person ?name ?mbox WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox variables: *1 --- - "optional triples '{...; ...}'" - |2 PREFIX foaf: SELECT ?person ?name ?mbox ?nick WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox; foaf:nick ?nick } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox - !!perl/array:RDF::Query::Node::Variable - nick variables: *2 --- - union; sparql 6.2 - |2 PREFIX dc10: PREFIX dc11: SELECT ?title ?author WHERE { { ?book dc10:title ?title . ?book dc10:creator ?author } UNION { ?book dc11:title ?title . ?book dc11:creator ?author } } - method: SELECT namespaces: dc10: http://purl.org/dc/elements/1.1/ dc11: http://purl.org/dc/elements/1.0/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/creator - !!perl/array:RDF::Query::Node::Variable - author - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.0/title - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - book - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.0/creator - !!perl/array:RDF::Query::Node::Variable - author - &1 - !!perl/array:RDF::Query::Node::Variable - title - !!perl/array:RDF::Query::Node::Variable - author variables: *1 --- - literal language tag @en - |2 PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gary P"@en ; foaf:homepage ?homepage . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gary P - en - ~ - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - homepage - &2 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - homepage variables: *2 --- - typed literal ^^URI - |2 PREFIX dc: PREFIX foaf: SELECT ?image WHERE { ?image dc:date "2005-04-07T18:27:56-04:00"^^ } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Literal - 2005-04-07T18:27:56-04:00 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - &1 - !!perl/array:RDF::Query::Node::Variable - image variables: *1 --- - typed literal ^^qName - |2 PREFIX foaf: PREFIX dc: PREFIX xs: SELECT ?image WHERE { ?image dc:date "2005-04-07T18:27:56-04:00"^^xs:dateTime } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ dc: http://purl.org/dc/elements/1.1/ xs: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Literal - 2005-04-07T18:27:56-04:00 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - &1 - !!perl/array:RDF::Query::Node::Variable - image variables: *1 --- - subject collection syntax - |2 SELECT ?x WHERE { (1 ?x 3) } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - subject collection syntax; with pred-obj. - |2 PREFIX foaf: SELECT ?x WHERE { (1 ?x 3) foaf:name "My Collection" } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - My Collection - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - subject collection syntax; object collection syntax - |2 PREFIX dc: SELECT ?x WHERE { (1 ?x 3) dc:subject (1 2 3) } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - &6 !!perl/array:RDF::Query::Node::Blank - BLANK - a5 - &7 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *6 - &8 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &9 !!perl/array:RDF::Query::Node::Blank - BLANK - a6 - !!perl/array:RDF::Query::Algebra::Triple - *9 - *7 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *9 - *8 - &10 !!perl/array:RDF::Query::Node::Blank - BLANK - a7 - !!perl/array:RDF::Query::Algebra::Triple - *10 - *7 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *10 - *8 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/subject - *6 - &11 - !!perl/array:RDF::Query::Node::Variable - x variables: *11 --- - object collection syntax - |2 PREFIX test: SELECT ?x WHERE { test:mycollection (1 ?x 3) . } - method: SELECT namespaces: test: http://kasei.us/e/ns/test# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - &5 !!perl/array:RDF::Query::Node::Blank - BLANK - a3 - !!perl/array:RDF::Query::Algebra::Triple - *5 - *2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *5 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/about/foaf.xrdf#greg - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/test#mycollection - *1 - &6 - !!perl/array:RDF::Query::Node::Variable - x variables: *6 --- - SELECT * - |2 SELECT * WHERE { ?a ?a ?b . } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - &1 - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b variables: - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b --- - default prefix - |2 PREFIX : SELECT ?person WHERE { ?person :name "Gregory Todd Williams", "Greg Williams" . } - method: SELECT namespaces: __DEFAULT__: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::Triple - *1 - *2 - !!perl/array:RDF::Query::Node::Literal - Greg Williams - &3 - !!perl/array:RDF::Query::Node::Variable - person variables: *3 --- - select from named; single triple; no prefix - |2 PREFIX foaf: SELECT ?src ?name FROM NAMED FROM NAMED WHERE { GRAPH ?src { ?x foaf:name ?name } } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: - - !!perl/array:RDF::Query::Node::Resource - URI - file://data/named_graphs/alice.rdf - NAMED - - !!perl/array:RDF::Query::Node::Resource - URI - file://data/named_graphs/bob.rdf - NAMED triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - src - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - src - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - ASK FILTER; using <= (shouldn't parse as '<') - |2 PREFIX xsd: ASK { FILTER ( "1995-11-05"^^xsd:dateTime <= "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } - method: ASK namespaces: xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression - <= - !!perl/array:RDF::Query::Node::Literal - 1995-11-05 - ~ - http://www.w3.org/2001/XMLSchema#dateTime - !!perl/array:RDF::Query::Node::Literal - 1994-11-05T13:15:30Z - ~ - http://www.w3.org/2001/XMLSchema#dateTime - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] variables: [] --- - ORDER BY with expression - |2 PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX xsd: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . } ORDER BY ASC( xsd:decimal( ?lat ) ) - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2001/XMLSchema#decimal - !!perl/array:RDF::Query::Node::Variable - lat - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - triple pattern with trailing internal '.' - | PREFIX rdf: PREFIX foaf: PREFIX cyc: PREFIX dcterms: PREFIX dc: SELECT ?place ?img ?date WHERE { ?region foaf:name "Maine" . ?p cyc:inRegion ?region; foaf:name ?place . ?img dcterms:spatial ?p . ?img dc:date ?date; rdf:type foaf:Image . } ORDER BY DESC(?date) LIMIT 10 - method: SELECT namespaces: cyc: http://www.cyc.com/2004/06/04/cyc# dc: http://purl.org/dc/elements/1.1/ dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Limit - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - region - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Maine - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://www.cyc.com/2004/06/04/cyc#inRegion - !!perl/array:RDF::Query::Node::Variable - region - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - place - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/terms/spatial - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Algebra::Triple - &2 !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - !!perl/array:RDF::Query::Algebra::Triple - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &3 - !!perl/array:RDF::Query::Node::Variable - place - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Variable - date - 10 variables: *3 --- - "[bug] query with predicate starting with 'a' (confused with { ?subj a ?type})" - |2 PREFIX rdf: PREFIX foaf: PREFIX cyc: PREFIX dcterms: PREFIX dc: PREFIX album: PREFIX p: SELECT ?img ?date WHERE { album:image ?img . ?img dc:date ?date ; rdf:type foaf:Image . } ORDER BY DESC(?date) - method: SELECT namespaces: album: http://kasei.us/e/ns/album# cyc: http://www.cyc.com/2004/06/04/cyc# dc: http://purl.org/dc/elements/1.1/ dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ p: http://www.usefulinc.com/picdiary/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/pictures/parties/19991205-Tims_Party/ - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/album#image - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/date - !!perl/array:RDF::Query::Node::Variable - date - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - - DESC - !!perl/array:RDF::Query::Node::Variable - date - &2 - !!perl/array:RDF::Query::Node::Variable - img - !!perl/array:RDF::Query::Node::Variable - date variables: *2 --- - dawg/simple/01 - |2 PREFIX : SELECT * WHERE { :x ?p ?q . } - method: SELECT namespaces: __DEFAULT__: http://example.org/data/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/data/x - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - q --- - single triple with comment; dawg/data/part1 - |2 # Get name, and optionally the mbox, of each person PREFIX foaf: SELECT ?name ?mbox WHERE { ?person foaf:name ?name . OPTIONAL { ?person foaf:mbox ?mbox} } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &1 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - mbox variables: *1 --- - ask query - | ASK { ?node a . } - method: ASK namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/e/ns/mt/blog variables: [] --- - blank-pred-blank - | PREFIX rdf: PREFIX foaf: SELECT ?name WHERE { [ foaf:name ?name ] foaf:maker [] } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/maker - !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - Filter with unary-plus - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( ?lat > +52 ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - '>' - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Literal - +52 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - Filter with isIRI - | PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?lat WHERE { ?point geo:lat ?lat . ?image ?pred ?point . FILTER( isIRI(?image) ) } - method: SELECT namespaces: dcterms: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:isiri - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - pred - !!perl/array:RDF::Query::Node::Variable - point - &1 - !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Variable - lat variables: *1 --- - 'xsd:double' - | PREFIX dc: SELECT ?node WHERE { ?node dc:identifier 1e4 . } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/identifier - !!perl/array:RDF::Query::Node::Literal - 1e4 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - boolean literal - | PREFIX dc: SELECT ?node WHERE { ?node dc:identifier true . } - method: SELECT namespaces: dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - node - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/identifier - !!perl/array:RDF::Query::Node::Literal - true - ~ - http://www.w3.org/2001/XMLSchema#boolean - &1 - !!perl/array:RDF::Query::Node::Variable - node variables: *1 --- - select with ORDER BY function call - | PREFIX foaf: PREFIX : SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY :foo(?name) - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ __DEFAULT__: http://example.com/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/foo - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - select with bnode object as second pred-obj - | PREFIX rdf: PREFIX foaf: SELECT ?name WHERE { ?r foaf:name ?name ; foaf:maker [ a foaf:Person ] } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - &2 !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/maker - *1 - &3 - !!perl/array:RDF::Query::Node::Variable - name variables: *3 --- - select with qname with '-2' suffix - | PREFIX foaf: PREFIX wn: SELECT ?thing WHERE { ?image a foaf:Image ; foaf:depicts ?thing . ?thing a wn:Flower-2 . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ wn: http://xmlns.com/wordnet/1.6/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - image - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Image - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/depicts - !!perl/array:RDF::Query::Node::Variable - thing - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - thing - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/wordnet/1.6/Flower-2 - &2 - !!perl/array:RDF::Query::Node::Variable - thing variables: *2 --- - select with qname with underscore - | PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person ; foaf:mbox_sha1sum "2057969209f1dfdad832de387cf13e6ff8c93b12" ; foaf:name ?name . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox_sha1sum - !!perl/array:RDF::Query::Node::Literal - 2057969209f1dfdad832de387cf13e6ff8c93b12 - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - &2 - !!perl/array:RDF::Query::Node::Variable - name variables: *2 --- - construct with one construct triple - | PREFIX foaf: CONSTRUCT { ?person foaf:name ?name } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name --- - construct with two construct triples - | PREFIX foaf: CONSTRUCT { ?person foaf:name ?name . ?person a foaf:Person } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person --- - construct with three construct triples - | PREFIX foaf: CONSTRUCT { ?person a foaf:Person . ?person foaf:name ?name . ?person foaf:firstName ?name } WHERE { ?person foaf:firstName ?name } - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/firstName - !!perl/array:RDF::Query::Node::Variable - name --- - select with triple-optional-triple - | PREFIX foaf: SELECT ?person ?nick ?page WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:nick ?nick } . ?person foaf:homepage ?page } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Literal - Gregory Todd Williams - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/nick - !!perl/array:RDF::Query::Node::Variable - nick - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/homepage - !!perl/array:RDF::Query::Node::Variable - page - &1 - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Variable - nick - !!perl/array:RDF::Query::Node::Variable - page variables: *1 --- - select with FROM - | PREFIX foaf: PREFIX geo: SELECT ?lat ?long FROM WHERE { ?point a geo:Point ; geo:lat ?lat ; geo:long ?long . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ geo: http://www.w3.org/2003/01/geo/wgs84_pos# sources: - - !!perl/array:RDF::Query::Node::Resource - URI - http://kasei.us/code/rdf-query/test-data/greenwich.rdf triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Variable - point - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#Point - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#lat - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2003/01/geo/wgs84_pos#long - !!perl/array:RDF::Query::Node::Variable - long - &2 - !!perl/array:RDF::Query::Node::Variable - lat - !!perl/array:RDF::Query::Node::Variable - long variables: *2 --- - select with graph-triple-triple - | # select all the email addresses ever held by the person # who held a given email address on 2007-01-01 PREFIX foaf: PREFIX t: SELECT ?mbox WHERE { GRAPH ?time { ?p foaf:mbox } . ?time t:inside "2007-01-01" . ?p foaf:mbox ?mbox . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ t: http://www.w3.org/2006/09/time# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - time - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Resource - URI - mailto:gtw@cs.umd.edu - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - time - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2006/09/time#inside - !!perl/array:RDF::Query::Node::Literal - 2007-01-01 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/mbox - !!perl/array:RDF::Query::Node::Variable - mbox - &2 - !!perl/array:RDF::Query::Node::Variable - mbox variables: *2 --- - (DAWG) syn-leading-digits-in-prefixed-names.rq - | PREFIX dob: PREFIX t: PREFIX dc: SELECT ?desc WHERE { dob:1D a t:ProperInterval; dc:description ?desc. } - method: SELECT namespaces: dob: http://placetime.com/interval/gregorian/1977-01-18T04:00:00Z/P t: http://www.ai.sri.com/daml/ontologies/time/Time.daml# dc: http://purl.org/dc/elements/1.1/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Resource - URI - http://placetime.com/interval/gregorian/1977-01-18T04:00:00Z/P1D - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://www.ai.sri.com/daml/ontologies/time/Time.daml#ProperInterval - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://purl.org/dc/elements/1.1/description - !!perl/array:RDF::Query::Node::Variable - desc - &2 - !!perl/array:RDF::Query::Node::Variable - desc variables: *2 --- - (DAWG) syn-07.rq - | # Trailing ; PREFIX : SELECT * WHERE { :s :p :o ; FILTER(?x) } - method: SELECT namespaces: __DEFAULT__: http://example/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#s - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#p - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#o - &1 [] variables: *1 --- - (DAWG) syn-08.rq - | # Broken ; PREFIX : SELECT * WHERE { :s :p :o ; . } - method: SELECT namespaces: __DEFAULT__: http://example/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#s - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#p - !!perl/array:RDF::Query::Node::Resource - URI - http://example/ns#o - &1 [] variables: [] --- - (DAWG) syn-11.rq - | PREFIX : SELECT * WHERE { _:a ?p ?v . FILTER(true) . [] ?q _:a } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Node::Literal - true - ~ - http://www.w3.org/2001/XMLSchema#boolean - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - a - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q --- - (DAWG) syntax-form-describe01.rq - | DESCRIBE - method: DESCRIBE namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] variables: - !!perl/array:RDF::Query::Node::Resource - URI - u --- - (DAWG) syntax-form-construct04.rq - | PREFIX rdf: CONSTRUCT { [] rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o . } WHERE {?s ?p ?o} - method: CONSTRUCT namespaces: rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#subject - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#object - !!perl/array:RDF::Query::Node::Variable - o --- - (DAWG) syntax-lists-02.rq - | PREFIX : SELECT * WHERE { ?x :p ( ?z ) } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#p - *1 - &2 - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Node::Variable - x variables: - !!perl/array:RDF::Query::Node::Variable - z - !!perl/array:RDF::Query::Node::Variable - x --- - (DAWG) syntax-qname-03.rq - | PREFIX : SELECT * WHERE { :_1 :p.rdf :z.z . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#_1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#p.rdf - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z.z - &1 [] variables: [] --- - (DAWG) syntax-qname-08.rq - | BASE PREFIX : <#> PREFIX x.y: SELECT * WHERE { :a.b x.y: : . } - method: SELECT namespaces: __DEFAULT__: http://example.org/# x.y: http://example.org/x# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#a.b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/x# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/# - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-07.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p 123 } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - 123 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-08.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p 123. . } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - 123. - ~ - http://www.w3.org/2001/XMLSchema#decimal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-12.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long''\'Literal''' } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - Long'''Literal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-lit-13.rq - | BASE PREFIX : <#> SELECT * WHERE { :x :p """Long\"""Literal""" } - method: SELECT namespaces: __DEFAULT__: http://example.org/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/#p - !!perl/array:RDF::Query::Node::Literal - Long"""Literal - &1 [] base: !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ variables: [] --- - (DAWG) syntax-general-07.rq - | SELECT * WHERE { +1.0 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - +1.0 - ~ - http://www.w3.org/2001/XMLSchema#decimal - &1 [] variables: [] --- - (DAWG) syntax-general-09.rq - | SELECT * WHERE { 1.0e0 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - 1.0e0 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 [] variables: [] --- - (DAWG) syntax-general-10.rq - | SELECT * WHERE { +1.0e+1 } - method: SELECT namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - a - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Literal - +1.0e+1 - ~ - http://www.w3.org/2001/XMLSchema#double - &1 [] variables: [] --- - (DAWG) syntax-lists-03.rq - | PREFIX : SELECT * WHERE { ( ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] variables: [] --- - (DAWG) syntax-lists-04.rq - | PREFIX : SELECT * WHERE { ( 1 2 ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &5 [] variables: [] --- - (DAWG) syntax-lists-02.rq - | PREFIX : SELECT * WHERE { ( ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 [] variables: [] --- - (DAWG) syntax-lists-04.rq - | PREFIX : SELECT * WHERE { ( 1 2 ) :p 1 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - &2 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#first - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *1 - &3 !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#rest - &4 !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - !!perl/array:RDF::Query::Algebra::Triple - *4 - *2 - !!perl/array:RDF::Query::Node::Literal - 2 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::Triple - *4 - *3 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#nil - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - &5 [] variables: [] --- - (DAWG) dawg-eval - | PREFIX rdf: PREFIX ex: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(str(?val), "example\\.com") } - method: SELECT namespaces: rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# ex: http://example.com/# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:regex - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:str - !!perl/array:RDF::Query::Node::Variable - val - !!perl/array:RDF::Query::Node::Literal - example\.com - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.com/#foo - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#value - !!perl/array:RDF::Query::Node::Variable - val - &1 - !!perl/array:RDF::Query::Node::Variable - val variables: *1 --- - (DAWG) dawg-eval: sameTerm - | PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( !sameTerm(?v1, ?v2) && ?v1 = ?v2 ) } - method: SELECT namespaces: __DEFAULT__: http://example.org/things# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:logical-and - !!perl/array:RDF::Query::Expression::Unary - '!' - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - sparql:sameterm - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v2 - &1 - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Variable - v2 variables: *1 --- - (DAWG) dawg-eval: basic/manifest#term-8 - | PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p +5 } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Literal - +5 - ~ - http://www.w3.org/2001/XMLSchema#integer - &1 - !!perl/array:RDF::Query::Node::Variable - p variables: - !!perl/array:RDF::Query::Node::Variable - p --- - (DAWG) dawg-eval: algebra/manifest#filter-nested-2 - | PREFIX : SELECT ?v { :x :p ?v . { FILTER(?v = 1) } } - method: SELECT namespaces: __DEFAULT__: http://example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example/x - !!perl/array:RDF::Query::Node::Resource - URI - http://example/p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Literal - 1 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - &1 - !!perl/array:RDF::Query::Node::Variable - v variables: *1 --- - (DAWG) dawg-eval: optional/manifest#dawg-optional-complex-4 - | PREFIX foaf: PREFIX ex: SELECT ?name ?plan ?dept ?img FROM <...> FROM NAMED <...> WHERE { ?person foaf:name ?name { ?person ex:healthplan ?plan } UNION { ?person ex:department ?dept } OPTIONAL { ?person a foaf:Person GRAPH ?g { [] foaf:name ?name; foaf:depiction ?img } } } - method: SELECT namespaces: ex: http://example.org/things# foaf: http://xmlns.com/foaf/0.1/ sources: - - !!perl/array:RDF::Query::Node::Resource - URI - ... - - !!perl/array:RDF::Query::Node::Resource - URI - ... - NAMED triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#healthplan - !!perl/array:RDF::Query::Node::Variable - plan - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#department - !!perl/array:RDF::Query::Node::Variable - dept - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - person - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/Person - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Variable - g - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &2 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *2 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/depiction - !!perl/array:RDF::Query::Node::Variable - img - &3 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - plan - !!perl/array:RDF::Query::Node::Variable - dept - !!perl/array:RDF::Query::Node::Variable - img variables: *3 --- - (DAWG) dawg-eval: i18n/manifest#kanji-1 - | PREFIX foaf: PREFIX 食: SELECT ?name ?food WHERE { [ foaf:name ?name ; 食:食べる ?food ] . } - method: SELECT namespaces: foaf: http://xmlns.com/foaf/0.1/ 食: http://www.w3.org/2001/sw/DataAccess/tests/data/i18n/kanji.ttl# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - &1 !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://xmlns.com/foaf/0.1/name - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Algebra::Triple - *1 - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/2001/sw/DataAccess/tests/data/i18n/kanji.ttl#食べる - !!perl/array:RDF::Query::Node::Variable - food - &2 - !!perl/array:RDF::Query::Node::Variable - name - !!perl/array:RDF::Query::Node::Variable - food variables: *2 --- - (DAWG) dawg-syntax: syntax-sparql4/manifest#syn-10 - | PREFIX : SELECT * WHERE { { _:a ?p ?v . _:a ?q _:a } UNION { _:b ?q _:c } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - a - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - b - !!perl/array:RDF::Query::Node::Variable - q - !!perl/array:RDF::Query::Node::Blank - BLANK - c - &1 - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q variables: - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - v - !!perl/array:RDF::Query::Node::Variable - q --- - (DAWG) dawg-syntax: syntax-sparql1/manifest#syntax-pat-04 - | PREFIX : SELECT * { OPTIONAL{:x :y :z} ?a :b :c { :x1 :y1 :z1 } UNION { :x2 :y2 :z2 } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#c - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z1 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#y2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#z2 - &1 - !!perl/array:RDF::Query::Node::Variable - a variables: - !!perl/array:RDF::Query::Node::Variable - a --- - (DAWG) dawg-syntax: syntax-sparql1/manifest#syntax-struct-10 - | PREFIX : SELECT * { OPTIONAL { :a :b :c } . ?x ?y ?z } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Optional - OPTIONAL - !!perl/array:RDF::Query::Algebra::GroupGraphPattern [] - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#a - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#b - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#c - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Variable - z - &1 - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Variable - y - !!perl/array:RDF::Query::Node::Variable - z variables: *1 --- - (DAWG) dawg-syntax: expr-equals/manifest#eq-2-1 - | PREFIX xsd: PREFIX : SELECT ?v1 ?v2 WHERE { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) . } - method: SELECT namespaces: __DEFAULT__: http://example.org/things# xsd: http://www.w3.org/2001/XMLSchema# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/things#p - !!perl/array:RDF::Query::Node::Variable - v2 - &1 - !!perl/array:RDF::Query::Node::Variable - v1 - !!perl/array:RDF::Query::Node::Variable - v2 variables: *1 --- - (DAWG) dawg-syntax: expr-ops/manifest#minus-1 - | PREFIX : SELECT ?s WHERE { ?s :p ?o . ?s2 :p ?o2 . FILTER(?o - ?o2 = 3) . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Filter - FILTER - !!perl/array:RDF::Query::Expression::Binary - == - !!perl/array:RDF::Query::Expression::Binary - - - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Variable - o2 - !!perl/array:RDF::Query::Node::Literal - 3 - ~ - http://www.w3.org/2001/XMLSchema#integer - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s2 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Variable - o2 - &1 - !!perl/array:RDF::Query::Node::Variable - s variables: *1 --- - (DAWG) dawg-syntax: syntax-qname-04.rq - | PREFIX : PREFIX a: SELECT * WHERE { : a: :a . : : : . } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# a: http://example.org/ns2# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns2# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#a - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns# - &1 [] variables: *1 --- - (DAWG) dawg-syntax: syntax-union-02.rq - | PREFIX : SELECT * { { ?s ?p ?o } UNION { ?a ?b ?c } UNION { ?r ?s ?t } } - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::Union - UNION - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - !!perl/array:RDF::Query::Node::Variable - c - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - t - &1 - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Variable - a - !!perl/array:RDF::Query::Node::Variable - b - !!perl/array:RDF::Query::Node::Variable - c - !!perl/array:RDF::Query::Node::Variable - r - !!perl/array:RDF::Query::Node::Variable - t variables: *1 --- - (DAWG) dawg-syntax: syntax-order-06.rq - | PREFIX : SELECT * { ?s ?p ?o } ORDER BY DESC(?o+57) :func2(?o) ASC(?s) - method: SELECT namespaces: __DEFAULT__: http://example.org/ns# sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::Sort - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - DESC - !!perl/array:RDF::Query::Expression::Binary - + - !!perl/array:RDF::Query::Node::Variable - o - !!perl/array:RDF::Query::Node::Literal - 57 - ~ - http://www.w3.org/2001/XMLSchema#integer - - ASC - !!perl/array:RDF::Query::Expression::Function - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/ns#func2 - !!perl/array:RDF::Query::Node::Variable - o - - ASC - !!perl/array:RDF::Query::Node::Variable - s - &1 - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o variables: *1 --- - (DAWG) dawg-syntax: syntax-bnode-02.rq - | PREFIX : # Tab SELECT * WHERE { [ ] :p [ ] } - method: SELECT namespaces: __DEFAULT__: http://example.org/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Blank - BLANK - a1 - !!perl/array:RDF::Query::Node::Resource - URI - http://example.org/p - !!perl/array:RDF::Query::Node::Blank - BLANK - a2 - &1 [] variables: [] --- - (DAWG) dawg-syntax: syntax-esc-04.rq - | PREFIX : SELECT * WHERE { <\u0078> :\u0070 ?xx\u0078 } - method: SELECT namespaces: __DEFAULT__: http://example/ sources: [] triples: - !!perl/array:RDF::Query::Algebra::Project - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Resource - URI - x - !!perl/array:RDF::Query::Node::Resource - URI - http://example/p - !!perl/array:RDF::Query::Node::Variable - xxx - &1 - !!perl/array:RDF::Query::Node::Variable - xxx variables: *1 --- - CONSTRUCT with LIMIT (github pull request 17, from kjetilk) - | PREFIX rdf: PREFIX foaf: CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . } LIMIT 5 - method: CONSTRUCT namespaces: foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# sources: [] options: limit: 5 triples: - !!perl/array:RDF::Query::Algebra::Construct - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o - - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - s - !!perl/array:RDF::Query::Node::Variable - p - !!perl/array:RDF::Query::Node::Variable - o --- - INSERT-DELETE-USING-WHERE - | DELETE { GRAPH { ?x } } INSERT { GRAPH { ?x } } USING WHERE { ?x a } - method: UPDATE custom_update_dataset: 1 namespaces: {} sources: [] triples: - !!perl/array:RDF::Query::Algebra::Update - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &1 !!perl/array:RDF::Query::Node::Resource - URI - g1 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Quad - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - b - !!perl/array:RDF::Query::Node::Resource - URI - c - *1 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::NamedGraph - GRAPH - &2 !!perl/array:RDF::Query::Node::Resource - URI - g1 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Quad - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - y - !!perl/array:RDF::Query::Node::Resource - URI - z - *2 - !!perl/array:RDF::Query::Algebra::GroupGraphPattern - !!perl/array:RDF::Query::Algebra::BasicGraphPattern - !!perl/array:RDF::Query::Algebra::Triple - !!perl/array:RDF::Query::Node::Variable - x - !!perl/array:RDF::Query::Node::Resource - URI - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - !!perl/array:RDF::Query::Node::Resource - URI - Foo - default: - !!perl/array:RDF::Query::Node::Resource - URI - g1 - 0 RDF-Query-2.910/xt/pod.t000644 000765 000024 00000000233 11760736733 014747 0ustar00gregstaff000000 000000 use strict; use warnings; use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); RDF-Query-2.910/xt/pod_coverage.t000644 000765 000024 00000000273 11760736733 016626 0ustar00gregstaff000000 000000 use strict; use warnings; use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; all_pod_coverage_ok(); RDF-Query-2.910/xt/sparql11-federation.t000644 000765 000024 00000002707 11760736733 017757 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use Test::More; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); my $tests = 2; plan tests => $tests; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.costmodel = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ SKIP: { print "# Remote SERVICE invocations\n"; my $why = "No network. Set RDFQUERY_NETWORK_TESTS to run these tests."; skip $why, 2 unless ($ENV{RDFQUERY_NETWORK_TESTS}); { my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ) or warn RDF::Query->error; PREFIX foaf: SELECT DISTINCT * WHERE { SERVICE { ?p a foaf:Person ; foaf:name "Gregory Todd Williams" . FILTER(ISIRI(?p)) } } LIMIT 1 END my $model = RDF::Trine::Model->new(); my $iter = $query->execute( $model ); my $count = 0; while (my $row = $iter->next) { $count++; is( $row->{p}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'expected URI value from remote SERVICE' ); } is( $count, 1 ); } } RDF-Query-2.910/t/00-simple.t000644 000765 000024 00000010601 12054225575 015475 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); use Test::More; plan tests => 1 + (37 * scalar(@models)); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR END isa_ok( $query, 'RDF::Query' ); ok( not($query->is_update), "query isn't an update" ); print "# (?var qname literal)\n"; my ($p, $c) = $query->prepare( $model ); my @results = $query->execute_plan( $p, $c ); ok( scalar(@results), 'got result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{'person'}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{'person'}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'got person uri' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:homepage ) USING foaf FOR END isa_ok( $query, 'RDF::Query' ); ok( not($query->is_update), "query isn't an update" ); print "# (?var qname quri)\n"; my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{person}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{person}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'got person uri' ); } { print "# multiple namespaces\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX rdf: PREFIX foaf: PREFIX dc: SELECT ?title WHERE { ?desc rdf:type foaf:PersonalProfileDocument . ?desc foaf:maker ?person . ?person foaf:name "Gregory Todd Williams" . ?desc dc:title ?title . } END isa_ok( $query, 'RDF::Query' ); ok( not($query->is_update), "query isn't an update" ); my ($p, $c) = $query->prepare( $model ); my @results = $query->execute_plan( $p, $c ); ok( scalar(@results), 'got result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( ref($results[0]) && $results[0]{title}->isa('RDF::Trine::Node::Literal'), 'Literal' ); is( ref($results[0]) && $results[0]{title}->literal_value, 'FOAF Description for Gregory Williams', 'got file title' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?page WHERE (?person foaf:name "Gregory Todd Williams") (?person foaf:homepage ?page) USING foaf FOR END isa_ok( $query, 'RDF::Query' ); ok( not($query->is_update), "query isn't an update" ); print "# chained (name->person->homepage)\n"; my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{page}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{page}->uri_value, 'http://kasei.us/', 'got homepage url' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?name ?mbox WHERE (?person foaf:homepage ) (?person foaf:name ?name) (?person foaf:mbox ?mbox) USING foaf FOR END isa_ok( $query, 'RDF::Query' ); ok( not($query->is_update), "query isn't an update" ); print "# chained (homepage->person->(name|mbox)\n"; my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 2, 'got two field' ); ok( $results[0]{name}->isa('RDF::Trine::Node::Literal'), 'Literal' ); ok( $results[0]{mbox}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{name}->literal_value, 'Gregory Todd Williams', 'got name' ); is( $results[0]{mbox}->uri_value, 'mailto:greg@evilfunhouse.com', 'got mbox uri' ); } } RDF-Query-2.910/t/01-coverage.t000644 000765 000024 00000030177 11707542734 016015 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::Exception; use Scalar::Util qw(refaddr); use lib qw(. t); BEGIN { require "models.pl"; } my $verbose = 1; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); use Test::More; use_ok( 'RDF::Query' ); use RDF::Query::Node qw(iri); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; { print "# using RDQL language URI\n" if ($verbose); my $query = new RDF::Query ( <<"END", { lang => 'rdql' } ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR END my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); } { print "# using SPARQL language URI\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams" } END my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); } { print "# SPARQL query\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER REGEX(str(?homepage), "kasei") } END my @results = $query->execute( $model ); ok( scalar(@results), 'results' ); my $row = $results[0]; my ($p,$h) = @{ $row }{qw(person homepage)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node' ); ok( $h->isa('RDF::Trine::Node::Resource'), 'isa_resource(resource)' ); is( $h->uri_value, 'http://kasei.us/', 'http://kasei.us/' ); } { print "# geo:Point with geo:lat\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { [ a geo:Point; geo:lat "52.972770"; foaf:name ?name ] } END my ($name) = $query->get( $model ); ok( $name, 'got name' ); is( $name->literal_value, 'Cliffs of Moher, Ireland', 'Cliffs of Moher, Ireland' ); } { print "# RDQL query\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR END my ($person) = $query->get( $model ); ok( $person->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $person->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'Person uri' ); } { print "# Triple with QName subject\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?name WHERE (kasei:greg foaf:name ?name) USING kasei FOR foaf FOR END my ($name) = $query->get( $model ); ok( $name->isa('RDF::Trine::Node::Literal'), 'Literal' ); is( $name->literal_value, 'Gregory Todd Williams', 'Person name' ); } { print "# Early triple with multiple unbound variables\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person ?name WHERE (?person foaf:name ?name) (?person foaf:homepage ) USING foaf FOR END my @results = $query->execute( $model ); ok( $results[0]{person}->isa('RDF::Trine::Node::Resource'), 'Person Resource' ); ok( $results[0]{name}->isa('RDF::Trine::Node::Literal'), 'Name Resource' ); is( $results[0]{person}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'Person uri' ); is( $results[0]{name}->literal_value, 'Gregory Todd Williams', 'Person name' ); like( $results[0]{name}->as_string, qr'Gregory Todd Williams', 'Person name #2' ); } { print "# Triple with no variable, present in data\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") ( foaf:homepage ) USING foaf FOR END my @results = $query->execute( $model ); ok( $results[0]{person}->isa('RDF::Trine::Node::Resource'), 'Person Resource' ); is( $results[0]{person}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'Person uri' ); } { print "# Triple with no variable, not present in data\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") ( foaf:homepage ) USING foaf FOR END my @results = $query->execute( $model ); is( scalar(@results), 0, 'No data returned for bogus triple' ); } { print "# Query with one triple, two variables\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name ?name) USING foaf FOR END my @results = $query->execute( $model ); ok( scalar(@results), 'one triple, two variables (query call)' ); my ($person) = $query->get( $model ); ok( $person->isa('RDF::Trine::Node'), 'one triple, two variables (get call)' ); } { print "# Broken query triple (variable with missing '?')\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (person foaf:name "Gregory Todd Williams") USING foaf FOR END is( $query, undef, 'Error (undef row) on no triples (query call)' ); } { print "# Backend tests\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?name ?homepage WHERE (kasei:greg foaf:name ?name) (kasei:greg foaf:homepage ?homepage) USING kasei FOR foaf FOR END my ($name,$homepage) = $query->get( $model ); ok( !$name->isa('RDF::Trine::Node::Resource'), 'isa_resource(literal)' ); ok( $homepage->isa('RDF::Trine::Node::Resource'), 'isa_resource(resource)' ); ok( !$homepage->isa('RDF::Trine::Node::Literal'), 'isa_literal(resource)' ); ok( $name->isa('RDF::Trine::Node::Literal'), 'isa_literal(literal)' ); } { print "# SPARQL getting foaf:aimChatID by foaf:nick\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?aim WHERE { ?p foaf:nick "kasei"; foaf:aimChatID ?aim } END my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); my $row = $results[0]; my ($aim) = @{ $row }{qw(aim)}; ok( $aim->isa('RDF::Trine::Node::Literal'), 'isa_literal' ); like( $aim->as_string, qr'samofool', 'got string' ); } { print "# SPARQL getting foaf:aimChatID by foaf:nick on non-existant person\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?aim WHERE { ?p foaf:nick "libby"; foaf:aimChatID ?aim } END my @results = $query->execute( $model ); is( scalar(@results), 0, '0 results' ); } { print "# SPARQL getting blank nodes (geo:Points) and sorting by genid\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX geo: SELECT ?p WHERE { ?p a geo:Point } ORDER BY ?p LIMIT 2 END my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($p) = @{ $row }{qw(p)}; ok( $p, $p->as_string ); } } { print "# broken query with get call\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX geo: break me END is( $query, undef, 'broken query with get call' ); } { print "# SPARQL query with missing (optional) WHERE\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX foaf: SELECT ?person { ?person foaf:name "Gregory Todd Williams" } END my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); } { print "# SPARQL query with SELECT *\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); SELECT * WHERE { ?a ?a ?b . } END my @results = $query->execute( $model ); is( scalar(@results), 1, 'got one result' ); my $result = $results[0]; is( $result->{'a'}->uri_value, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'rdf:type' ); is( $result->{'b'}->uri_value, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property', 'rdfs:Property' ); } { print "# SPARQL query with default namespace\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX : SELECT ?person WHERE { ?person :name "Gregory Todd Williams" } END my @results = $query->execute( $model ); ok( scalar(@results), 'got result' ); } { print "# SPARQL query; blank node results\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX foaf: PREFIX wn: SELECT ?thing WHERE { ?image a foaf:Image ; foaf:depicts ?thing . ?thing a wn:Flower-2 . } END my $stream = $query->execute( $model ); while (my $row = $stream->next) { my $thing = $row->{thing}; ok( $thing->isa('RDF::Trine::Node::Blank'), 'isa blank' ); my $id = $thing->blank_identifier; ok( length($id), 'blank identifier' ); } is( $stream->seen_count, 3, '3 result' ); } { print "# SPARQL query; language-typed literal\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX foaf: SELECT ?name WHERE { ?p a foaf:Person ; foaf:mbox_sha1sum "2057969209f1dfdad832de387cf13e6ff8c93b12" ; foaf:name ?name . } END my ($name) = $query->get( $model ); my $lang = $name->literal_value_language; is ($lang, 'en', 'language'); } { print "# \n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX : SELECT ?person ?name WHERE { ?person :name ?name } END my ($id, $name) = ('lauren', 'Lauren B'); my $person = iri( "http://kasei.us/about/foaf.xrdf#${id}" ); my $stream = $query->execute( $model, bind => { person => $person } ); while (my $row = $stream->next) { my $p = $row->{person}; is( $p->uri_value, "http://kasei.us/about/foaf.xrdf#${id}", 'expected pre-bound person URI' ); my $node = $row->{name}; my $value = $node->literal_value; is( $value, $name, 'expected name on pre-bound node' ); } is( $stream->seen_count, 1, '1 result' ); } { print "# SPARQL query; Stream accessors-1\n" if ($verbose); my $query = new RDF::Query ( <<"END", undef, 'http://www.w3.org/TR/rdf-sparql-query/', undef ); PREFIX : SELECT ?person WHERE { ?person :name "Gregory Todd Williams" } END my $stream = $query->execute( $model ); my $value = $stream->binding_value_by_name('person'); is( $value, $stream->binding_value( 0 ), 'binding_value' ); ok( $value->isa('RDF::Trine::Node'), 'binding_value_by_name' ); my @names = $stream->binding_names; is_deeply( ['person'], \@names, 'binding_names' ); my @values = $stream->binding_values; ok( $values[0]->isa('RDF::Trine::Node'), 'binding_value_by_name' ); } { print "# SPARQL query; BASE declaration\n" if ($verbose); my $query = new RDF::Query ( <<"END" ); BASE SELECT ?person WHERE { ?person "Gregory Todd Williams" } END my $stream = $query->execute( $model ); my $row = $stream->next; isa_ok( $row, 'HASH' ); ok( exists( $row->{ person } ) ); is( $row->{person}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg' ); } } done_testing(); RDF-Query-2.910/t/algebra-bgp.t000644 000765 000024 00000003053 11707542734 016140 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 7; use Test::Exception; use Scalar::Util qw(reftype blessed); use RDF::Query; use RDF::Query::Node; use RDF::Query::Algebra; use RDF::Query::Parser::SPARQL; my $parser = RDF::Query::Parser::SPARQL->new(); my $ns = { foaf => 'http://xmlns.com/foaf/0.1/' }; { my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person }', undef, $ns)->patterns; ok( $bgp->connected, 'single-connected BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person ; foaf:name ?name }', undef, $ns)->patterns; ok( $bgp->connected, 'connected BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?a ?b ?c . ?c ?d ?e . ?e ?f ?g . ?g ?h ?i }', undef, $ns)->patterns; ok( $bgp->connected, 'connected chain BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?a ?b ?c . ?e ?f ?g . ?c ?d ?e . ?g ?h ?a }', undef, $ns)->patterns; ok( $bgp->connected, 'connected loop BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person ; foaf:name ?name ; foaf:knows ?q . ?q a foaf:Person ; foaf:name ?name }', undef, $ns)->patterns; ok( $bgp->connected, '(multi-)connected BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person . ?q a foaf:Person }', undef, $ns)->patterns; ok( not($bgp->connected), 'non-connected two-triples BGP' ); } { my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person ; foaf:name ?name . ?q a foaf:Person ; foaf:homepage ?h . ?h foaf:isPrimaryTopicOf ?t }', undef, $ns)->patterns; ok( not($bgp->connected), 'non-connected two-clusteres BGP' ); } RDF-Query-2.910/t/algebra-expr.t000644 000765 000024 00000015643 11707542734 016356 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 42; use Scalar::Util qw(reftype blessed); use RDF::Query::Node; use RDF::Query::Algebra; my $bz = RDF::Query::Node::Blank->new( 'z' ); my $bb = RDF::Query::Node::Blank->new( 'b' ); my $ba = RDF::Query::Node::Blank->new( 'a' ); my $lb = RDF::Query::Node::Literal->new( 'b' ); my $la = RDF::Query::Node::Literal->new( 'a' ); my $lal = RDF::Query::Node::Literal->new( 'a', 'en' ); my $l1 = RDF::Query::Node::Literal->new( '1' ); my $l2d = RDF::Query::Node::Literal->new( '2.0', undef, 'http://www.w3.org/2001/XMLSchema#float' ); my $l1d = RDF::Query::Node::Literal->new( '1', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l4d = RDF::Query::Node::Literal->new( '-4', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l5d = RDF::Query::Node::Literal->new( '5.3', undef, 'http://www.w3.org/2001/XMLSchema#float' ); my $l01d = RDF::Query::Node::Literal->new( '01', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l0d = RDF::Query::Node::Literal->new( '0', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l3dd = RDF::Query::Node::Literal->new( '3', undef, 'http://www.w3.org/2001/XMLSchema#double' ); my $true = RDF::Query::Node::Literal->new( 'true', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); my $false = RDF::Query::Node::Literal->new( 'false', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); my $rea = RDF::Query::Node::Resource->new( 'http://example.org/a' ); my $reb = RDF::Query::Node::Resource->new( 'http://example.org/b' ); my $lea = RDF::Query::Node::Literal->new( 'http://example.org/a' ); my $dt3 = RDF::Query::Node::Literal->new( '2007-12-31T22:55:00-06:00', undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); my $dt2 = RDF::Query::Node::Literal->new( '2007-12-31T23:55:00-05:00', undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); my $dt1 = RDF::Query::Node::Literal->new( '2008-01-01T00:00:00Z', undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); my $cv = RDF::Query::Node::Literal->new( '1', undef, 'http://example.org/mytype' ); my $ct = RDF::Query::Node::Literal->new( 'true', undef, 'http://example.org/mytype' ); my $un = 'RDF::Query::Expression::Unary'; my $bin = 'RDF::Query::Expression::Binary'; { # NUMERIC OPERATORS { my $TEST = 'integer add'; my $plus = $bin->new( '+', $l1d, $l01d ); my $value = $plus->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 2, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $plus = $bin->new( '+', $l1d, $l2d ); my $value = $plus->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 3, 'integer-float add value' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', 'integer-float add datatype' ); } { my $diff = $bin->new( '-', $l0d, $l1d ); my $value = $diff->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, -1, 'integer subtract value' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', 'integer subtract datatype' ); } { my $diff = $bin->new( '-', $l2d, $l1d ); my $value = $diff->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, 'integer-float subtract value' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', 'integer-float subtract datatype' ); } { my $prod = $bin->new( '*', $l2d, $l3dd ); my $value = $prod->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 6, 'doule-float quotient value' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#double', 'double-float quotient datatype' ); } { my $quo = $bin->new( '/', $l3dd, $l2d ); my $value = $quo->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1.5, 'doule-float quotient value' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#double', 'double-float quotient datatype' ); } { my $TEST = 'integer unary-plus'; my $plus = $un->new( '+', $l4d ); my $value = $plus->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, -4, $TEST ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $TEST = 'integer unary-minus'; my $plus = $un->new( '-', $l4d ); my $value = $plus->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 4, $TEST ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $sum = $bin->new( '+', $l1d, $l2d ); my $diff = $bin->new( '-', $l0d, $l1d ); my $prod = $bin->new( '*', $sum, $diff ); my $value = $prod->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, -3, 'prod(sum, diff)' ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', 'double-float quotient datatype' ); } } { # RELATIONAL OPERATORS local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; { my $TEST = 'double-float less-than'; my $lt = $bin->new( '<', $l3dd, $l2d ); my $value = $lt->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'resource-float less-than'; my $lt = $bin->new( '<', $rea, $l2d ); my $value = $lt->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'literal-blank greater-than'; my $lt = $bin->new( '>', $la, $ba ); my $value = $lt->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'dateTime less-than'; my $lt = $bin->new( '<', $dt1, $dt2 ); my $value = $lt->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'dateTime equal'; my $lt = $bin->new( '==', $dt3, $dt2 ); my $value = $lt->evaluate( undef, undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } RDF-Query-2.910/t/algebra-func.t000644 000765 000024 00000072466 11707542734 016341 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 210; use Test::Exception; use Scalar::Util qw(reftype blessed); use RDF::Query; use RDF::Query::Node; use RDF::Query::Algebra; use RDF::Query::Functions; my $bz = RDF::Query::Node::Blank->new( 'z' ); my $bb = RDF::Query::Node::Blank->new( 'b' ); my $ba = RDF::Query::Node::Blank->new( 'a' ); my $li = RDF::Query::Node::Literal->new( 'i' ); my $lb = RDF::Query::Node::Literal->new( 'b' ); my $la = RDF::Query::Node::Literal->new( 'a' ); my $lalpha = RDF::Query::Node::Literal->new( 'abcdefg' ); my $lALPHA = RDF::Query::Node::Literal->new( 'ABCDEFG' ); my $lal = RDF::Query::Node::Literal->new( 'a', 'en' ); my $l1 = RDF::Query::Node::Literal->new( '1' ); my $l2d = RDF::Query::Node::Literal->new( '2.0', undef, 'http://www.w3.org/2001/XMLSchema#float' ); my $l1d = RDF::Query::Node::Literal->new( '1', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l4d = RDF::Query::Node::Literal->new( '-4', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l5d = RDF::Query::Node::Literal->new( '5.3', undef, 'http://www.w3.org/2001/XMLSchema#float' ); my $l01d = RDF::Query::Node::Literal->new( '01', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l0d = RDF::Query::Node::Literal->new( '0', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l3dd = RDF::Query::Node::Literal->new( '3', undef, 'http://www.w3.org/2001/XMLSchema#double' ); my $lpat = RDF::Query::Node::Literal->new( '^b' ); my $lemail = RDF::Query::Node::Literal->new( 'mailto:greg@evilfunhouse.com' ); my $ldate2 = RDF::Query::Node::Literal->new( '2008-01-01T00:00:00Z', undef, 'http://www.w3.org/2001/XMLSchema#string' ); my $ldate1 = RDF::Query::Node::Literal->new( '2008-01-01T00:00:00Z' ); my $true = RDF::Query::Node::Literal->new( 'true', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); my $false = RDF::Query::Node::Literal->new( 'false', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); my $rea = RDF::Query::Node::Resource->new( 'http://example.org/a' ); my $reb = RDF::Query::Node::Resource->new( 'http://example.org/b' ); my $lea = RDF::Query::Node::Literal->new( 'http://example.org/a' ); my $dt2 = RDF::Query::Node::Literal->new( '2007-12-31T23:55:00-05:00', undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); my $dt1 = RDF::Query::Node::Literal->new( '2008-01-01T00:00:00Z', undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); my $cv = RDF::Query::Node::Literal->new( '1', undef, 'http://example.org/mytype' ); my $ct = RDF::Query::Node::Literal->new( 'true', undef, 'http://example.org/mytype' ); my $ct2 = RDF::Query::Node::Literal->new( 'true', undef, 'http://example.org/mytype' ); my $len = RDF::Query::Node::Literal->new( 'en' ); my $lengb = RDF::Query::Node::Literal->new( 'en-gb' ); my $lenus = RDF::Query::Node::Literal->new( 'en-us' ); my $va = RDF::Query::Node::Variable->new( 'a' ); my $vb = RDF::Query::Node::Variable->new( 'b' ); my $un = 'RDF::Query::Expression::Unary'; my $bin = 'RDF::Query::Expression::Binary'; my $func = 'RDF::Query::Expression::Function'; my $xsd = 'http://www.w3.org/2001/XMLSchema#'; local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; { # xsd:integer() { my $TEST = 'integer->integer cast'; my $alg = $func->new( "${xsd}integer", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $TEST = 'string->integer cast'; my $alg = $func->new( "${xsd}integer", $l1 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $TEST = 'bool(true)->integer cast'; my $alg = $func->new( "${xsd}integer", $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $TEST = 'bool(false)->integer cast'; my $alg = $func->new( "${xsd}integer", $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 0, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', "$TEST datatype" ); } { my $TEST = 'resource->integer cast (throws)'; my $alg = $func->new( "${xsd}integer", $rea ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'double->integer cast (throws)'; my $alg = $func->new( "${xsd}integer", $l5d ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::FilterEvaluationError', $TEST; } } { # xsd:decimal() { my $TEST = 'integer->decimal cast'; my $alg = $func->new( "${xsd}decimal", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#decimal', "$TEST datatype" ); } { my $TEST = 'bool(true)->decimal cast'; my $alg = $func->new( "${xsd}decimal", $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#decimal', "$TEST datatype" ); } { my $TEST = 'bool(false)->decimal cast'; my $alg = $func->new( "${xsd}decimal", $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 0, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#decimal', "$TEST datatype" ); } { my $TEST = 'resource->decimal cast (throws)'; my $alg = $func->new( "${xsd}decimal", $rea ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'custom->decimal cast'; my $alg = $func->new( "${xsd}decimal", $cv ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#decimal', "$TEST datatype" ); } } { # xsd:float() { my $TEST = 'integer->float cast'; my $alg = $func->new( "${xsd}float", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', "$TEST datatype" ); } { my $TEST = 'bool(true)->float cast'; my $alg = $func->new( "${xsd}float", $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', "$TEST datatype" ); } { my $TEST = 'bool(false)->float cast'; my $alg = $func->new( "${xsd}float", $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 0, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', "$TEST datatype" ); } { my $TEST = 'resource->float cast (throws)'; my $alg = $func->new( "${xsd}float", $rea ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'custom->float cast (throws)'; my $alg = $func->new( "${xsd}float", $cv ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } } { # xsd:double() { my $TEST = 'integer->double cast'; my $alg = $func->new( "${xsd}double", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#double', "$TEST datatype" ); } { my $TEST = 'bool(true)->double cast'; my $alg = $func->new( "${xsd}double", $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#double', "$TEST datatype" ); } { my $TEST = 'bool(false)->double cast'; my $alg = $func->new( "${xsd}double", $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 0, "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#double', "$TEST datatype" ); } { my $TEST = 'resource->double cast (throws)'; my $alg = $func->new( "${xsd}double", $rea ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'custom->double cast (throws)'; my $alg = $func->new( "${xsd}double", $cv ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } } { # xsd:boolean() { my $TEST = 'integer(0)->boolean cast'; my $alg = $func->new( "${xsd}boolean", $l0d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 0, "$TEST value" ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'integer(1)->boolean cast'; my $alg = $func->new( "${xsd}boolean", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'resource->boolean cast (throws)'; my $alg = $func->new( "${xsd}boolean", $rea ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'custom->boolean cast (throws)'; my $alg = $func->new( "${xsd}boolean", $cv ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } { my $TEST = 'custom(true)->boolean cast'; my $alg = $func->new( "${xsd}boolean", $ct ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->numeric_value, 1, "$TEST value" ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # xsd:string { my $TEST = 'literal->string cast'; my $alg = $func->new( "${xsd}string", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'a', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', "$TEST datatype" ); } { my $TEST = 'integer->string cast'; my $alg = $func->new( "${xsd}string", $l1d ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, '1', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', "$TEST datatype" ); } { my $TEST = 'resource->string cast'; my $alg = $func->new( "${xsd}string", $rea ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'http://example.org/a', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', "$TEST datatype" ); } { my $TEST = 'boolean->string cast'; my $alg = $func->new( "${xsd}string", $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', "$TEST datatype" ); } } { # xsd:dateTime { my $TEST = 'literal->dateTime cast'; my $alg = $func->new( "${xsd}dateTime", $ldate1 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, '2008-01-01T00:00:00Z', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#dateTime', "$TEST datatype" ); } { my $TEST = 'string->dateTime cast'; my $alg = $func->new( "${xsd}dateTime", $ldate2 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, '2008-01-01T00:00:00Z', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#dateTime', "$TEST datatype" ); } { my $TEST = 'string(bad)->dateTime cast (throws)'; my $alg = $func->new( "${xsd}dateTime", $la ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } } { # sparql:str { my $TEST = 'str(literal)'; my $alg = $func->new( "sparql:str", $ct ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); ok( not($value->has_datatype), "$TEST datatype" ); } { my $TEST = 'str(resource)'; my $alg = $func->new( "sparql:str", $reb ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'http://example.org/b', "$TEST value" ); ok( not($value->has_datatype), "$TEST datatype" ); } { my $TEST = 'str(blank) (throws)'; my $alg = $func->new( "sparql:str", $ba ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } } { # sparql:lang { my $TEST = 'lang(plain) is empty'; my $alg = $func->new( "sparql:lang", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, '', "$TEST value" ); } { my $TEST = 'lang(english)'; my $alg = $func->new( "sparql:lang", $lal ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'en', "$TEST value" ); } { my $TEST = 'lang(blank) (throws)'; my $alg = $func->new( "sparql:lang", $ba ); throws_ok { my $value = $alg->evaluate( undef, {} ); } 'RDF::Query::Error::TypeError', $TEST; } } { # sparql:bound { my $TEST = 'bound(var) true'; my $alg = $func->new( "sparql:bound", $va ); my $value = $alg->evaluate( undef, { a => $la } ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'bound(var) false'; my $alg = $func->new( "sparql:bound", $vb ); my $value = $alg->evaluate( undef, { a => $la } ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:isuri # sparql:isiri { my $TEST = 'isuri(resource)'; my $alg = $func->new( "sparql:isuri", $rea ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isuri(literal)'; my $alg = $func->new( "sparql:isuri", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isiri(resource)'; my $alg = $func->new( "sparql:isiri", $rea ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isiri(literal)'; my $alg = $func->new( "sparql:isiri", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:isblank { my $TEST = 'isblank(resource)'; my $alg = $func->new( "sparql:isblank", $rea ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isblank(literal)'; my $alg = $func->new( "sparql:isblank", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isblank(blank)'; my $alg = $func->new( "sparql:isblank", $ba ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:isliteral { my $TEST = 'isliteral(resource)'; my $alg = $func->new( "sparql:isliteral", $rea ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isliteral(literal)'; my $alg = $func->new( "sparql:isliteral", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'isliteral(blank)'; my $alg = $func->new( "sparql:isliteral", $ba ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:langmatches { my $TEST = 'langmatches(en-gb, en)'; my $alg = $func->new( "sparql:langmatches", $lengb, $len ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'langmatches(en-us, en-gb)'; my $alg = $func->new( "sparql:langmatches", $lenus, $lengb ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:sameterm { my $TEST = 'sameTerm on equivalent dateTime literals'; my $alg = $func->new( "sparql:sameterm", $dt1, $dt2 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'sameTerm on identical literals'; my $alg = $func->new( "sparql:sameterm", $ct, $ct2 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = 'sameTerm on different typed literals with same value'; my $alg = $func->new( "sparql:sameterm", $ct, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:datatype { my $TEST = 'datatype(plain literal)'; my $alg = $func->new( "sparql:datatype", $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Resource' ); is( $value->uri_value, 'http://www.w3.org/2001/XMLSchema#string', "$TEST value" ); } { my $TEST = 'datatype(dateTime)'; my $alg = $func->new( "sparql:datatype", $dt1 ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Resource' ); is( $value->uri_value, 'http://www.w3.org/2001/XMLSchema#dateTime', "$TEST value" ); } { my $TEST = 'datatype(custom typed literal)'; my $alg = $func->new( "sparql:datatype", $cv ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Resource' ); is( $value->uri_value, 'http://example.org/mytype', "$TEST value" ); } } { # sparql:regex { my $TEST = "regex('abcdefg', 'a')"; my $alg = $func->new( "sparql:regex", $lalpha, $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = "regex('b', 'a')"; my $alg = $func->new( "sparql:regex", $lb, $la ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = "regex('abcdefg', '^b')"; my $alg = $func->new( "sparql:regex", $lalpha, $lpat ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = "regex('b', '^b')"; my $alg = $func->new( "sparql:regex", $lb, $lpat ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = "regex('ABCDEFG', 'b', 'i')"; my $alg = $func->new( "sparql:regex", $lALPHA, $lb, $li ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } } { # sparql:logical-or { my $TEST = "logical-or(T,T)"; my $alg = $func->new( "sparql:logical-or", $true, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); } { my $TEST = "logical-or(T,F)"; my $alg = $func->new( "sparql:logical-or", $true, $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); } { my $TEST = "logical-or(F,T)"; my $alg = $func->new( "sparql:logical-or", $false, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); } { my $TEST = "logical-or(F,F)"; my $alg = $func->new( "sparql:logical-or", $false, $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); } { my $TEST = "logical-or(F,F,T)"; my $alg = $func->new( "sparql:logical-or", $false, $false, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); } } { # sparql:logical-and { my $TEST = "logical-and(T,T)"; my $alg = $func->new( "sparql:logical-and", $true, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); } { my $TEST = "logical-and(T,F)"; my $alg = $func->new( "sparql:logical-and", $true, $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); } { my $TEST = "logical-and(F,T)"; my $alg = $func->new( "sparql:logical-and", $false, $true ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); } { my $TEST = "logical-and(F,F)"; my $alg = $func->new( "sparql:logical-and", $false, $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); } { my $TEST = "logical-and(T,T,F)"; my $alg = $func->new( "sparql:logical-and", $true, $true, $false ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); } } { # jena:sha1sum # jena:now # jena:langeq # jena:listMember { my $TEST = "jena:sha1sum"; my $alg = $func->new( "java:com.hp.hpl.jena.query.function.library.sha1sum", $lemail ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8', "$TEST value" ); } { my $TEST = "jena:now"; my $alg = $func->new( "java:com.hp.hpl.jena.query.function.library.now" ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); like( $value->literal_value, qr/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ$/, "$TEST value" ); } { my $TEST = "jena:langeq (true)"; my $alg = $func->new( "java:com.hp.hpl.jena.query.function.library.langeq", $lal, $len ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $TEST = "jena:langeq (false)"; my $alg = $func->new( "java:com.hp.hpl.jena.query.function.library.langeq", $lal, $lengb ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'false', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { local($TODO) = "can't test jena:listMember yet because this test script doesn't instantiate a model"; my $TEST = "jena:listMember"; fail(); } } { eval "use Geo::Distance 0.09;"; my $GEO_DISTANCE_LOADED = ($@) ? 0 : 1; # ldodds:Distance SKIP: { skip( "Need Geo::Distance 0.09 or higher to run these tests.", 4 ) unless ($GEO_DISTANCE_LOADED); my @args = map { RDF::Query::Node::Literal->new($_, undef, 'http://www.w3.org/2001/XMLSchema#float') } qw(34.015673 -118.496947 41.8351 -71.3971); my $TEST = "ldodds:Distance"; my $alg = $func->new( "java:com.ldodds.sparql.Distance", @args ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Literal' ); my $dist = $value->numeric_value; cmp_ok( $dist, '>', 4165, "$TEST value lower bound" ); cmp_ok( $dist, '<', 4170, "$TEST value upper bound" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#float', "$TEST datatype" ); } } SKIP: { # kasei:bloom unless ($RDF::Query::Functions::BLOOM_FILTER_LOADED) { skip('Bloom::Filter is not available', 7); } { my $TEST = "bloom:filter"; my $filter = RDF::Query::Node::Literal->new( 'AAAAAgAAAAcAAAACAAAAAgAAAAEAAAADklyWOSVq5odsMC4y' ); my $alg = $func->new( "http://kasei.us/code/rdf-query/functions/bloom/filter", $va, $filter ); { my $value = $alg->evaluate( undef, { a => $rea } ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } { my $value = $alg->evaluate( undef, { a => $reb } ); isa_ok( $value, 'RDF::Query::Node::Literal' ); is( $value->literal_value, 'true', "$TEST value" ); is( $value->literal_datatype, 'http://www.w3.org/2001/XMLSchema#boolean', "$TEST datatype" ); } my @resources = map { RDF::Query::Node::Resource->new( 'http://localhost/' . $_ ) } ('a' .. 'z', 'A' .. 'Z', 0 .. 9); my $true = 0; foreach my $r (@resources) { my $value = $alg->evaluate( undef, { a => $r } ); if ($value->literal_value eq 'true') { $true++; } } cmp_ok( $true, '<', scalar(@resources), "$TEST (false)"); } } ################################################################################ { # nested { my $TEST = 'datatype(jena:now())'; my $now = $func->new( "java:com.hp.hpl.jena.query.function.library.now" ); my $alg = $func->new( "sparql:datatype", $now ); my $value = $alg->evaluate( undef, {} ); isa_ok( $value, 'RDF::Query::Node::Resource' ); is( $value->uri_value, 'http://www.w3.org/2001/XMLSchema#dateTime', "$TEST datatype" ); } } __END__ RDF-Query-2.910/t/algebra-subsumption.t000644 000765 000024 00000005570 11707542734 017766 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 9; use Test::Exception; use Scalar::Util qw(reftype blessed); use RDF::Query; use RDF::Query::Node; use RDF::Query::Algebra; my $la = RDF::Query::Node::Literal->new( 'a' ); my $lb = RDF::Query::Node::Literal->new( 'b' ); my $ra = RDF::Query::Node::Resource->new( 'http://example.org/a' ); my $rb = RDF::Query::Node::Resource->new( 'http://example.org/b' ); my $va = RDF::Query::Node::Variable->new( 'a' ); my $vb = RDF::Query::Node::Variable->new( 'b' ); { # triple-triple subsumption testing my $triple = RDF::Query::Algebra::Triple->new( $ra, $rb, $la ); my $patterna = RDF::Query::Algebra::Triple->new( $ra, $rb, $va ); my $patternb = RDF::Query::Algebra::Triple->new( $ra, $rb, $vb ); my $patternc = RDF::Query::Algebra::Triple->new( $va, $va, $vb ); ok( $patterna->subsumes( $triple ), 'triple pattern subsumes triple (a)' ); ok( $patternb->subsumes( $triple ), 'triple pattern subsumes triple (b)' ); TODO: { local($TODO) = "subsumption testing needs to respect repeated variables"; ok( not($patternc->subsumes( $triple )), "pattern with repeated variables doesn't subsume triple (c)" ); } ok( not($triple->subsumes( $patterna )), "triple doesn't subsume pattern" ); } { # bgp-bgp subsumption testing my $triplea = RDF::Query::Algebra::Triple->new( $ra, $ra, $la ); my $tripleb = RDF::Query::Algebra::Triple->new( $ra, $rb, $lb ); my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( $triplea, $tripleb ); my $ptriplea = RDF::Query::Algebra::Triple->new( $ra, $ra, $va ); my $ptripleb = RDF::Query::Algebra::Triple->new( $ra, $rb, $vb ); my $patterna = RDF::Query::Algebra::BasicGraphPattern->new( $ptriplea, $ptripleb ); ok( $patterna->subsumes( $bgp ), 'bgp pattern subsumes bgp (a)' ); TODO: { local($TODO) = "subsumption testing needs to respect repeated variables"; my $ptriplec = RDF::Query::Algebra::Triple->new( $ra, $ra, $va ); my $ptripled = RDF::Query::Algebra::Triple->new( $ra, $rb, $va ); my $patternb = RDF::Query::Algebra::BasicGraphPattern->new( $ptriplec, $ptripled ); ok( not($patternb->subsumes( $bgp )), "bgp pattern with repeated variables doesn't subsume bgp (b)" ); } } { # bgp-triple subsumption testing my $ptriplea = RDF::Query::Algebra::Triple->new( $ra, $ra, $va ); my $ptripleb = RDF::Query::Algebra::Triple->new( $ra, $rb, $vb ); my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( $ptriplea, $ptripleb ); my $triplea = RDF::Query::Algebra::Triple->new( $ra, $ra, $la ); my $tripleb = RDF::Query::Algebra::Triple->new( $ra, $rb, $lb ); my $triplec = RDF::Query::Algebra::Triple->new( $rb, $ra, $la ); ok( $bgp->subsumes( $triplea ), 'bgp pattern subsumes triple (a)' ); ok( $bgp->subsumes( $tripleb ), 'bgp pattern subsumes triple (b)' ); ok( not($bgp->subsumes( $triplec )), "bgp pattern doesn't subsumes triple (c)" ); } RDF-Query-2.910/t/algebra.t000644 000765 000024 00000015650 11707542734 015400 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Test::More tests => 35; use Test::Exception; use Scalar::Util qw(reftype blessed); use RDF::Query; { my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dc: SELECT ?name WHERE { [ a foaf:PersonalProfileDocument ; foaf:maker [ a foaf:Person ; foaf:name ?name ] ] . } END my $query = RDF::Query->new( $sparql ); my $pattern = $query->pattern; isa_ok( $pattern, 'RDF::Query::Algebra' ); is_deeply( [ $pattern->referenced_variables ], ['name'], 'ppd: referenced_variables' ); is_deeply( [ $pattern->definite_variables ], ['name'], 'ppd: definite_variables' ); is_deeply( [ $pattern->referenced_blanks ], ['a1', 'a2'], 'ppd: referenced_blanks' ); } { my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dc: SELECT ?name WHERE { ?p a foaf:Person ; foaf:name ?name . OPTIONAL { ?p foaf:homepage ?page . } } END my $query = RDF::Query->new( $sparql ); my $pattern = $query->pattern; isa_ok( $pattern, 'RDF::Query::Algebra' ); is_deeply( [ sort $pattern->referenced_variables ], [qw(name p page)], 'foaf-simple: referenced_variables' ); is_deeply( [ sort $pattern->definite_variables ], [qw(name p)], 'foaf-simple: definite_variables' ); is_deeply( [ sort $pattern->referenced_blanks ], [], 'foaf-simple: referenced_blanks' ); } { my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dc: SELECT ?name WHERE { { ?p a foaf:Person ; foaf:name ?name . OPTIONAL { ?p foaf:homepage ?page . } } UNION { ?d a foaf:Document ; dc:title ?name . } } END my $query = RDF::Query->new( $sparql ); my $pattern = $query->pattern; isa_ok( $pattern, 'RDF::Query::Algebra' ); is_deeply( [ sort $pattern->referenced_variables ], [qw(d name p page)], 'union: referenced_variables' ); is_deeply( [ sort $pattern->definite_variables ], [qw(name)], 'union: definite_variables' ); is_deeply( [ sort $pattern->referenced_blanks ], [], 'union: referenced_blanks' ); } { # SORTING my $bz = RDF::Query::Node::Blank->new( 'z' ); my $bb = RDF::Query::Node::Blank->new( 'b' ); my $ba = RDF::Query::Node::Blank->new( 'a' ); my $lb = RDF::Query::Node::Literal->new( 'b' ); my $la = RDF::Query::Node::Literal->new( 'a' ); my $lal = RDF::Query::Node::Literal->new( 'a', 'en' ); my $l2d = RDF::Query::Node::Literal->new( '2.0', undef, 'http://www.w3.org/2001/XMLSchema#float' ); my $l1d = RDF::Query::Node::Literal->new( '1', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $l01d = RDF::Query::Node::Literal->new( '01', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $rea = RDF::Query::Node::Resource->new( 'http://example.org/a' ); my $reb = RDF::Query::Node::Resource->new( 'http://example.org/b' ); my $lea = RDF::Query::Node::Literal->new( 'http://example.org/a' ); { cmp_ok( $ba, '<', $bb, 'blank less-than' ); cmp_ok( $bb, '>', $ba, 'blank greater-than' ); cmp_ok( $ba, '!=', $bb, 'blank not-eq' ); cmp_ok( $ba, '==', $ba, 'blank eq' ); } { cmp_ok( $la, '<', $lb, 'literal less-than' ); cmp_ok( $lb, '>', $la, 'literal greater-than' ); cmp_ok( $la, '!=', $lb, 'literal not-eq' ); cmp_ok( $la, '==', $la, 'literal eq' ); # cmp_ok( $la, '<', $lal, 'same-valued plain- and language-literals are unsortable' ); throws_ok { $la <=> $l1d } 'RDF::Query::Error::TypeError', 'different-valued plain- and datatype-literals are sortable'; cmp_ok( $l1d, '==', $l01d, 'numeric-datatype-literals are sortable (equal, but not sameTerm)' ); cmp_ok( $l2d, '>', $l1d, 'numeric-datatype-literals are sortable (greater-than, but different numeric type)' ); } { cmp_ok( $rea, '<', $reb, 'resource less-than' ); cmp_ok( $reb, '>', $rea, 'resource greater-than' ); cmp_ok( $rea, '!=', $reb, 'resource not-eq' ); cmp_ok( $rea, '==', $rea, 'resource eq' ); } { cmp_ok( $bz, '<', $la, 'blank less-than resource (different value)' ); cmp_ok( $rea, '<', $lea, 'resource less-than literal (same value)' ); cmp_ok( $rea, '<', $la, 'resource less-than literal (different value)' ); } } { # Accessing Subpatterns { my $sparql = <<"END"; PREFIX foaf: SELECT ?name WHERE { [ a foaf:PersonalProfileDocument ; foaf:maker [ a foaf:Person ; foaf:name ?name ] ] } END my $query = RDF::Query->new( $sparql ); my $pattern = $query->pattern; my @triples = $pattern->subpatterns_of_type('RDF::Query::Algebra::Triple'); is( scalar(@triples), 4, 'count of triple subpatterns' ); } { my $sparql = <<"END"; PREFIX foaf: PREFIX ex: SELECT ?name ?plan ?dept ?img WHERE { ?person foaf:name ?name { ?person ex:healthplan ?plan } UNION { ?person ex:department ?dept } OPTIONAL { ?person a foaf:Person GRAPH ?g { [] foaf:name ?name; foaf:depiction ?img } } } END my $query = RDF::Query->new( $sparql ); my $pattern = $query->pattern; my @bgps = $pattern->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern'); is( scalar(@bgps), 5, 'count of bgp subpatterns' ); is_deeply( $bgps[3], bless( [ bless([ bless( ['person'], 'RDF::Query::Node::Variable' ), bless( ['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'], 'RDF::Query::Node::Resource' ), bless( ['URI','http://xmlns.com/foaf/0.1/Person'], 'RDF::Query::Node::Resource' ) ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::BasicGraphPattern' ), 'RDF::Query::Algebra::BasicGraphPattern' ); my ($optional) = $pattern->subpatterns_of_type('RDF::Query::Algebra::Optional'); my @obgps = $optional->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern'); is( scalar(@obgps), 5, 'count of bgp subpatterns under optional' ); my ($graph) = $pattern->subpatterns_of_type('RDF::Query::Algebra::NamedGraph'); is_deeply( $graph, bless( [ 'GRAPH', bless( ['g'], 'RDF::Query::Node::Variable' ), bless( [ bless( [ bless( [ bless( ['BLANK','a1'], 'RDF::Query::Node::Blank' ), bless( ['URI','http://xmlns.com/foaf/0.1/name'], 'RDF::Query::Node::Resource' ), bless( ['name'], 'RDF::Query::Node::Variable' ), ], 'RDF::Query::Algebra::Triple' ), bless( [ bless( ['BLANK','a1'], 'RDF::Query::Node::Blank' ), bless( ['URI','http://xmlns.com/foaf/0.1/depiction'], 'RDF::Query::Node::Resource' ), bless( ['img'], 'RDF::Query::Node::Variable' ), ], 'RDF::Query::Algebra::Triple' ) ], 'RDF::Query::Algebra::BasicGraphPattern' ) ], 'RDF::Query::Algebra::GroupGraphPattern' ) ], 'RDF::Query::Algebra::NamedGraph' ), 'deep comparison on GRAPH subpattern' ); } } RDF-Query-2.910/t/constraints.t000644 000765 000024 00000010010 11707542734 016333 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 11); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER ( REGEX(STR(?homepage), "kasei") ) . } END my ($person, $homepage) = $query->get( $model ); ok( $person->isa('RDF::Trine::Node::Resource'), 'Resource with regex match' ); is( $person->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'Person uri' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER ( REGEX(STR(?homepage), "not_in_here") ) . } END my ($person, $homepage) = $query->get( $model ); is( $person, undef, 'no result with regex match' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX xsd: SELECT ?point ?lat ?lon WHERE { dcterms:spatial ?point . ?point geo:lat ?lat ; geo:long ?lon . FILTER ( xsd:float(?lat) > 52.97 && xsd:float(?lat) < 53.036526 ) } END my ($point, $lat, $lon) = $query->get( $model ); ok( $point->isa('RDF::Trine::Node'), 'Point isa Node' ); cmp_ok( abs( $lat->numeric_value - 52.97277 ), '<', 0.001, 'latitude' ); cmp_ok( abs( $lon->literal_value + 9.430733 ), '<', 0.001, 'longitude' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX xsd: SELECT ?point ?lat ?lon WHERE { dcterms:spatial ?point . ?point geo:lat ?lat ; geo:long ?lon . FILTER( xsd:float(?lat) > 52 && xsd:float(?lat) < 53 ) } END warn RDF::Query->error unless ($query); my ($point, $lat, $lon) = $query->get( $model ); ok( $point->isa('RDF::Trine::Node'), 'Point isa Node' ); cmp_ok( abs( $lat->literal_value - 52.97277 ), '<', 0.001, 'latitude' ); cmp_ok( abs( $lon->literal_value + 9.430733 ), '<', 0.001, 'longitude' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX xsd: SELECT ?image ?lat ?lon WHERE { ?image dcterms:spatial [ geo:lat ?lat ] . FILTER( xsd:float(?lat) > 52.972 ) . FILTER( xsd:float(?lat) < 53 ) . } END my ($image, $point, $lat) = $query->get( $model ); ok( $image->isa('RDF::Trine::Node::Resource'), 'Image isa Resource' ); is( $image->uri_value, 'http://kasei.us/pictures/2004/20040909-Ireland/images/DSC_5705.jpg', 'Image url' ); } } __END__ RDF-Query-2.910/t/dataset-from-file.t000644 000765 000024 00000005223 11760736733 017304 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } my @models = test_models(); my $tests = 1 + (16 * scalar(@models)); plan tests => $tests; my $file = URI::file->new_abs( 'data/foaf.xrdf' ); my $rdfa = URI::file->new_abs( 'data/rdfa-test.xhtml' ); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?page FROM <$file> WHERE (?person foaf:name "Gregory Todd Williams") (?person foaf:homepage ?page) USING foaf FOR END my @results = $query->execute( $model ); is( scalar(@results), 1, 'result for RDQL query with FROM clause' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{page}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{page}->uri_value, 'http://kasei.us/', 'Got homepage url' ); } { my $query = new RDF::Query ( <<"END", { lang => 'sparql' } ); PREFIX foaf: SELECT ?page FROM <$file> WHERE { ?person foaf:name "Gregory Todd Williams" ; foaf:homepage ?page } END warn RDF::Query->error unless ($query); my @results = $query->execute( $model ); is( scalar(@results), 1, 'result for SPARQL query with FROM clause' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{page}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{page}->uri_value, 'http://kasei.us/', 'Got homepage url' ); } SKIP: { unless ($ENV{RDFQUERY_NETWORK_TESTS}) { skip "RDFa tests require network access. Set RDFQUERY_NETWORK_TESTS to run these tests.", 6; } eval "use RDF::RDFa::Parser;"; skip( "Need RDF::RDFa::Parser to run these tests.", 6 ) if ($@); my $query = new RDF::Query ( <<"END", { lang => 'sparql' } ); PREFIX dc: SELECT * FROM <$rdfa> WHERE { ?s dc:creator ?o } END warn RDF::Query->error unless ($query); my @results = $query->execute( $model ); is( scalar(@results), 1, 'result for SPARQL query with FROM clause for RDFa content' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 2, 'got two field' ); my $s = $results[0]{'s'}; my $o = $results[0]{'o'}; isa_ok( $s, 'RDF::Trine::Node::Resource' ); isa_ok( $o, 'RDF::Trine::Node::Literal' ); is( $o->literal_value, 'Mark Birbeck', 'expected value from RDFa data' ); } } RDF-Query-2.910/t/dataset-from-net.t000644 000765 000024 00000002560 11707542734 017151 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); BEGIN { require "models.pl"; } my @files; my @models = test_models( @files ); eval { require LWP::Simple }; if ($@) { plan skip_all => "LWP::Simple is not available for loading URLs"; return; } elsif ($ENV{RDFQUERY_NETWORK_TESTS}) { plan tests => 1 + (5 * scalar(@models)); } else { plan skip_all => 'No network. Set RDFQUERY_NETWORK_TESTS to run these tests.'; return; } my $loaded = use_ok( 'RDF::Query' ); BAIL_OUT( "RDF::Query not loaded" ) unless ($loaded); my $has_backend = 0; { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?page FROM WHERE (?person foaf:name "Gregory Todd Williams") (?person foaf:homepage ?page) USING foaf FOR END foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; my @model = ref($model) ? $model : (); my @results = $query->execute( @model ); is( scalar(@results), 1, 'Got one result' ); isa_ok( $results[0], 'HASH' ); is( scalar(@{ [ keys %{ $results[0] } ] }), 1, 'got one field' ); ok( $results[0]{page}->isa('RDF::Trine::Node::Resource'), 'Resource' ); is( $results[0]{page}->uri_value, 'http://kasei.us/', 'Got homepage url' ); } } RDF-Query-2.910/t/dev-computed-statements.t000644 000765 000024 00000007773 12054225575 020570 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use Scalar::Util qw(blessed); use lib qw(. t); BEGIN { require "models.pl"; } use Test::More; my $tests = 7; my @models = test_models( qw(data/foaf.xrdf) ); plan tests => 1 + ($tests * scalar(@models)); use_ok( 'RDF::Query' ); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query = TRACE, Screen # log4perl.category.rdf.query.plan.computedstatement = TRACE, Screen # log4perl.category.rdf.query.plan.join.pushdownnestedloop = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; SKIP: { { print "# computed predicate: list:member\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX test: PREFIX list: SELECT ?member WHERE { ?x test:mycollection ?list . ?list list:member ?member . } END $query->add_computed_statement_generator( 'http://www.jena.hpl.hp.com/ARQ/list#member' => \&__compute_list_member ); my $count = 0; # warn $model->as_string; # my ($plan, $ctx) = $query->prepare( $model ); # warn $plan->explain(); # $query->execute_plan( $plan, $ctx ); my $stream = $query->execute( $model ); my %expect = map { $_ => 1 } (1,2,3); while (my $row = $stream->next) { isa_ok( $row->{member}, 'RDF::Query::Node::Literal' ); my $value = $row->{member}->literal_value; ok( exists($expect{ $value }), "got expected value $value"); delete $expect{ $value }; } continue { ++$count }; is( $count, 3, 'expecting three list members' ); } } } sub __compute_list_member { my $query = shift; my $bound = shift; my $s = shift; my $p = shift; my $o = shift; my $c = shift; my $first = RDF::Query::Node::Resource->new( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first' ); my $rest = RDF::Query::Node::Resource->new( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest' ); my $model = $query->model; if (blessed($p) and $p->isa('RDF::Query::Node::Resource') and $p->uri_value( 'http://www.jena.hpl.hp.com/ARQ/list#member' )) { my @lists; my $lists = ($c) ? $model->get_named_statements( $s, $first, $o, $c ) : $model->get_statements( $s, $first, $o ); while (my $l = $lists->next) { push(@lists, [$l, $l->subject]); } my %seen; my $sub = sub { # warn 'trying to compute list:member'; my ($listst, $list, $head); while (1) { unless (scalar(@lists)) { # warn "no more lists to check"; return undef; } my $data = shift(@lists); ($listst, $head) = @$data; $list = $listst->subject; if ($seen{ $head->as_string, $list->as_string }++) { # warn "already seen this list..."; next; } else { last; } } # warn "checking list " . $list->as_string; return undef if (blessed($list) and $list->isa('RDF::Query::Node::Resource') and $list->uri_value eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'); my $obj = $listst->object; my $tail = ($c) ? $model->get_named_statements( $list, $rest, undef, $c ) : $model->get_statements( $list, $rest, undef ); while (my $st = $tail->next) { my $lists = ($c) ? $model->get_named_statements( $st->object, $first, $o, $c ) : $model->get_statements( $st->object, $first, $o ); while (my $st = $lists->next) { push(@lists, [$st, $head]); } } my $newhead = $head; my $st = RDF::Query::Algebra::Triple->new( $head, RDF::Query::Node::Resource->new('http://www.jena.hpl.hp.com/ARQ/list#member'), $obj ); return $st; }; return RDF::Trine::Iterator::Graph->new( $sub ); } } RDF-Query-2.910/t/distance.js000644 000765 000024 00000002077 11707542734 015745 0ustar00gregstaff000000 000000 function str (x) { warn(x); if (x instanceof Object) { warn('-> Node'); if (x.is_literal()) { warn('-> literal'); warn(x.literal_value); return x.literal_value; } else if (x.is_resource()) { warn('-> resource'); return x.uri_value; } else { warn('-> blank'); return x.blank_identifier; } } else { warn('-> Non-Node'); return x; } } function square (x) { return x * x; } function deg2rad(d) { return Math.PI*d/180 } function gcdistance( lat1, lon1, lat2, lon2 ) { lat1 = deg2rad( makeTerm(lat1).toString() ); lat2 = deg2rad( makeTerm(lat2).toString() ); lon1 = deg2rad( makeTerm(lon1).toString() ); lon2 = deg2rad( makeTerm(lon2).toString() ); var londiff = Math.abs(lon1 - lon2); var s1 = square(Math.sin((lat2 - lat1) / 2)); var s2 = square(Math.sin( londiff / 2 )); var sq = Math.sqrt( s1 + Math.cos(lat1) * Math.cos(lat2) * s2 ); var adist = 2 * Math.asin( sq ); var r = 6372.795; var dist = r * adist; var literal = makeTerm(dist, null, "http://www.w3.org/2001/XMLSchema#float"); return literal; } RDF-Query-2.910/t/ext-select-expr.t000644 000765 000024 00000005720 11707542734 017031 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use lib qw(. t); BEGIN { require "models.pl"; } use Test::More; my $tests = 7; my @models = test_models( qw(data/greenwich.rdf data/about.xrdf) ); plan tests => 1 + ($tests * scalar(@models)); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; { print "# select expression (node plus literal)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: PREFIX geo: SELECT (?lat AS ?latitude) WHERE { ?point a geo:Point ; foaf:name "Royal Observatory Greenwich" ; geo:lat ?lat ; geo:long ?long } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($lat) = @{ $row }{qw(latitude)}; is( $lat->literal_value, '51.477222', 'AS for alpha conversion' ); } continue { ++$count }; is( $count, 1, 'expecting one statement in model' ); } { print "# select expression (node plus literal)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: PREFIX geo: SELECT ?lat (?long + 1.0 AS ?long_off) WHERE { ?point a geo:Point ; foaf:name "Royal Observatory Greenwich" ; geo:lat ?lat ; geo:long ?long } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($lat, $long) = @{ $row }{qw(lat long_off)}; is( $lat->literal_value, '51.477222', 'existing latitude' ); cmp_ok( $long->literal_value, '==', 1, 'modified longitude' ); } continue { ++$count }; is( $count, 1, 'expecting one statement in model' ); } eval "use Geo::Distance 0.09;"; my $GEO_DISTANCE_LOADED = ($@) ? 0 : 1; SKIP: { skip( "Need Geo::Distance 0.09 or higher to run these tests.", 2 ) unless ($GEO_DISTANCE_LOADED); print "# select expression (function)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX dcterms: PREFIX foaf: PREFIX geo: PREFIX ldodds: SELECT ?image ?name (ldodds:Distance(?lat, ?long, 41.849331, -71.392) AS ?dist) WHERE { ?image a foaf:Image ; dcterms:spatial [ foaf:name ?name ; geo:lat ?lat ; geo:long ?long ; ] . } ORDER BY ldodds:Distance(?lat, ?long, 41.849331, -71.392) LIMIT 1 END my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next()) { my ($image, $pname, $pdist) = @{ $row }{qw(image name dist)}; my $name = $pname->literal_value; my $dist = $pdist->literal_value; like( $dist, qr/^0[.]0577\d*$/, "distance $name" ); $count++; } is( $count, 1, "ldodds:Distance: 1 objects found" ); } } RDF-Query-2.910/t/filters.t000644 000765 000024 00000027445 12054225575 015455 0ustar00gregstaff000000 000000 use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; use Scalar::Util qw(blessed); my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf Flower-2.rdf); my @models = test_models( @files ); my $tests = 0 + (scalar(@models) * 46); plan tests => $tests; eval "use Geo::Distance 0.09;"; my $GEO_DISTANCE_LOADED = ($@) ? 0 : 1; use RDF::Query; use RDF::Query::Node qw(iri); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.filter = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { print "# FILTER equality disjunction\n"; my $sparql = <<"END"; PREFIX exif: SELECT ?image ?time WHERE { ?image exif:exposureTime ?time . FILTER( ?time = "1/80" || ?time = "1/500" ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my $image = $row->{image}->uri_value; like( $image, qr<(DSC_5705|DSC_8057)>, 'expected image URI' ); $count++; } is( $count, 2, "3 object depictions found" ); } { print "# FILTER REGEX\n"; my $sparql = <<"END"; PREFIX rdf: PREFIX rdfs: PREFIX foaf: PREFIX myrdf: PREFIX wn: SELECT ?image ?thing ?type ?name WHERE { ?image foaf:depicts ?thing . ?thing rdf:type ?type . ?type rdfs:label ?name . FILTER(REGEX(STR(?type),"Flower")) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($image, $thing, $ttype, $tname) = @{ $row }{qw(image thing type name)}; my $url = $image->uri_value; my $node = $thing->as_string; my $name = $tname->literal_value; my $type = $ttype->as_string; like( $type, qr/Flower/, "$node is a Flower" ); $count++; } is( $count, 3, "3 object depictions found" ); } { print "# FILTER isBLANK(?person)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: SELECT ?person ?name WHERE { ?person a foaf:Person . ?person foaf:name ?name . FILTER isBLANK(?person) . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { isa_ok( $row, 'HASH' ); my ($p,$n) = @{ $row }{qw(person name)}; isa_ok( $p, 'RDF::Trine::Node', $p->as_string . ' is a node' ); like( $n->literal_value, qr/^Gary|Lauren/, 'name' ); $count++; } is( $count, 1, "1 person (bnode) found" ); } { print "# FILTER isURI(?person)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: SELECT ?person ?name WHERE { ?person a foaf:Person . ?person foaf:name ?name . FILTER isURI(?person) . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { isa_ok( $row, "HASH" ); my ($p,$n) = @{ $row }{qw(person name)}; ok( (blessed($p) and $p->isa('RDF::Trine::Node')), $p->as_string . ' is a node' ); like( $n->literal_value, qr/^(Greg|Liz|Lauren)/, 'name' ); $count++; } is( $count, 3, "3 people (uris) found" ); } SKIP: { skip( "Need Geo::Distance 0.09 or higher to run these tests.", 4 ) unless ($GEO_DISTANCE_LOADED); print "# FILTER geo:distance(...)\n"; my $sparql = <<"END"; PREFIX rdf: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX mygeo: SELECT ?image ?point ?name ?lat ?long WHERE { ?image rdf:type foaf:Image . ?image dcterms:spatial ?point . ?point foaf:name ?name . ?point geo:lat ?lat . ?point geo:long ?long . FILTER( mygeo:distance(?point, 41.849331, -71.392) < 10 ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); $query->add_function( 'http://kasei.us/e/ns/geo#distance', sub { my $query = shift; my $geo = new Geo::Distance; my $point = shift; my $plat = get_first_literal( $model, $point, 'http://www.w3.org/2003/01/geo/wgs84_pos#lat' ); my $plon = get_first_literal( $model, $point, 'http://www.w3.org/2003/01/geo/wgs84_pos#long' ); my ($lat, $lon) = map { Scalar::Util::blessed($_) ? $_->literal_value : $_ } @_; my $dist = $geo->distance( 'kilometer', $lon, $lat, $plon, $plat ); # warn "\t-> ${dist} kilometers from Providence"; return RDF::Query::Node::Literal->new("$dist", undef, 'http://www.w3.org/2001/XMLSchema#float'); } ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my ($image, $point, $pname, $lat, $lon) = @{ $row }{qw(image point name lat long)}; my $url = $image->uri_value; my $name = $pname->literal_value; like( $name, qr/, (RI|MA|CT)$/, "$name ($url)" ); $count++; } is( $count, 3, "3 distance-based images found" ); }; { RDF::Query->add_function( 'http://kasei.us/e/ns/rdf#isa', sub { my $query = shift; my $node = shift; my $ntype = shift; my $model = $query->model; my $p_type = RDF::Query::Node::Resource->new( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ); my $p_sub = RDF::Query::Node::Resource->new( 'http://www.w3.org/2000/01/rdf-schema#subClassOf' ); my $stmts = $model->get_statements( $node, $p_type ); my %seen; my @types; while (my $s = $stmts->next) { push( @types, $s->object ); } while (my $type = shift @types) { if ($type->equal( $ntype )) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { next if ($seen{ $type->as_string }++); my $sub_stmts = $model->get_statements( $type, $p_sub, undef ); while (my $s = $sub_stmts->next) { push( @types, $s->object ); } } } return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); my $sparql = <<"END"; PREFIX rdf: PREFIX rdfs: PREFIX foaf: PREFIX myrdf: PREFIX wn: SELECT ?image ?thing ?type ?name WHERE { ?image foaf:depicts ?thing . ?thing rdf:type ?type . ?type rdfs:label ?name . FILTER myrdf:isa(?thing, wn:Object) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($image, $thing, $ttype, $tname) = @{ $row }{qw(image thing type name)}; my $url = $image->uri_value; my $node = $thing->as_string; my $name = $tname->literal_value; my $type = $ttype->as_string; ok( $name, "$node is a $name (${type} isa wn:Object)" ); $count++; } is( $count, 3, "3 object depictions found" ); } SKIP: { eval "require Digest::SHA"; if ($@) { skip "Digest::SHA required for jena:sha1sum tests", 2; } my $sparql = <<"END"; PREFIX foaf: PREFIX jena: SELECT ?p WHERE { ?p foaf:mbox ?mbox . FILTER ( jena:sha1sum( ?mbox ) = 'f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8' ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($node) = @{ $row }{qw(p)}; my $uri = $node->uri_value; is( $uri, 'http://kasei.us/about/foaf.xrdf#greg', 'jena:sha1sum' ); $count++; } is( $count, 1, "jena:sha1sum: 1 object found" ); } { my $sparql = <<"END"; PREFIX foaf: PREFIX xpath: SELECT ?p WHERE { ?p foaf:mbox ?mbox . FILTER ( xpath:matches(?p, "^http://kasei.us", "") ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($node) = @{ $row }{qw(p)}; my $uri = $node->uri_value; is( $uri, 'http://kasei.us/about/foaf.xrdf#greg', 'xpath:matches' ); $count++; } is( $count, 1, "xpath:matches: 1 object found" ); } SKIP: { local($RDF::Query::error) = 1; skip( "Need Geo::Distance 0.09 or higher to run these tests.", 4 ) unless ($GEO_DISTANCE_LOADED); my $sparql = <<"END"; PREFIX ldodds: PREFIX foaf: PREFIX dcterms: PREFIX geo: SELECT ?image ?point ?name ?lat ?long WHERE { ?image a foaf:Image . ?image dcterms:spatial ?point . ?point foaf:name ?name . ?point geo:lat ?lat . ?point geo:long ?long . FILTER( ldodds:Distance(?lat, ?long, 41.849331, -71.392) < 10 ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next()) { my ($image, $point, $pname, $lat, $lon) = @{ $row }{qw(image point name lat long)}; my $url = $image->uri_value; my $name = $pname->literal_value; like( $name, qr/, (RI|MA|CT)$/, "$name ($url)" ); $count++; } is( $count, 3, "ldodds:Distance: 3 objects found" ); } { my $sparql = <<"END"; PREFIX rdf: PREFIX jfn: PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX test: PREFIX kasei: SELECT ?data WHERE { kasei:greg test:mycollection ?col . ?list rdf:first ?data . FILTER( jfn:listMember( ?col, ?data ) ) . } END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); my $stream = $query->execute( $model ); my $count = 0; my %expect = map {$_=>1} (1..3); while (my $row = $stream->next) { my ($data) = @{ $row }{qw(data)}; ok( $data->isa('RDF::Trine::Node::Literal'), "literal list member" ); ok( exists($expect{ $data->literal_value }), , "expected literal value" ); delete $expect{ $data->literal_value }; $count++; } is( $count, 3, "jfn:listMember: 3 objects found" ); } } ###################################################################### sub get_first_literal { my $model = shift; my $node = get_first_obj( $model, @_ ); return $node ? $node->literal_value : undef; } sub get_first_obj { my $model = shift; my $node = shift; my $uri = shift; my @uris = UNIVERSAL::isa($uri, 'ARRAY') ? @{ $uri } : ($uri); my @preds = map { ref($_) ? $_ : iri( $_ ) } @uris; foreach my $pred (@preds) { my $stmts = $model->get_statements( $node, $pred ); while (my $s = $stmts->next) { my $node = $s->object; return $node if ($node); } } } RDF-Query-2.910/t/functions.rdf000644 000765 000024 00000001375 11707542735 016323 0ustar00gregstaff000000 000000 SHA-1 Hash sha1Hash Great Circle Distance gcdistance RDF-Query-2.910/t/functions.t000644 000765 000024 00000016075 11707542735 016016 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use lib qw(. t); BEGIN { require "models.pl"; } use Test::More; my $tests = 23; my @models = test_models( qw(data/foaf.xrdf data/about.xrdf) ); plan tests => 1 + ($tests * scalar(@models)); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; SKIP: { { print "# DATATYPE() comparison\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX xsd: PREFIX dc: SELECT ?image ?date WHERE { ?image a foaf:Image ; dc:date ?date . FILTER ( datatype(?date) = xsd:dateTime ) } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($image, $dt) = @{ $row }{qw(image date)}; my $url = $image->uri_value; my $date = $dt->literal_value; like( $date, qr/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d[-+]\d\d:\d\d$/, "valid date: $date" ); $count++; } is( $count, 2, "2 photo found with typed date" ); } { print "# LANG() comparison\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX xsd: PREFIX dc: SELECT ?person ?name WHERE { ?person a foaf:Person ; foaf:name ?name . FILTER ( LANG(?name) = "en" ) } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($p, $n) = @{ $row }{qw(person name)}; my $person = $p->as_string; my $name = $n->literal_value; is( $name, 'Gary P', "english name: $name" ); $count++; } is( $count, 1, "1 person found with an english-typed name" ); } { print "# LANGMATCHES()\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX xsd: PREFIX dc: SELECT ?person ?name WHERE { ?person a foaf:Person ; foaf:name ?name . FILTER ( LANGMATCHES(LANG(?name), "en") ) } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { my ($p, $n) = @{ $row }{qw(person name)}; my $person = $p->as_string; my $name = $n->literal_value; is( $name, 'Gary P', "english name: $name" ); $count++; } is( $count, 1, "1 person found with an english-typed name" ); } { print "# dateTime type promotion and equality\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX xsd: ASK { FILTER ( xsd:dateTime("1994-11-05T08:15:30-05:00") = "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } END my $count = 0; my $stream = $query->execute( $model ); my $ok = $stream->get_boolean(); ok( $ok, 'op:dateTime-equal' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX xsd: ASK { FILTER ( xsd:dateTime("1994-11-05T08:15:30-08:00") = "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } END my $count = 0; my $stream = $query->execute( $model ); my $ok = $stream->get_boolean(); ok( not($ok), 'not op:dateTime-equal' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX xsd: ASK { FILTER ( "1995-11-05"^^xsd:dateTime > "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } END my $count = 0; my $stream = $query->execute( $model ); my $ok = $stream->get_boolean(); ok( $ok, 'dateTime-greater-than' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX xsd: ASK { FILTER ( "1995-11-05"^^xsd:dateTime <= "1994-11-05T13:15:30Z"^^xsd:dateTime ) . } END my $count = 0; my $stream = $query->execute( $model ); my $ok = $stream->get_boolean(); ok( not($ok), 'not dateTime-less-than-or-equal' ); } { # coalesce my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT * WHERE { ?p a foaf:Person . OPTIONAL { ?p foaf:aimChatID ?aim . } FILTER(COALESCE(?aim,"unknown") = "unknown") } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next()) { $count++; } is($count, 3, 'coalesce simple'); } { # coalesce my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT (COALESCE(?aim) AS ?name) WHERE { ?p a foaf:Person . OPTIONAL { ?p foaf:aimChatID ?aim . } } END warn RDF::Query->error unless ($query); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next()) { $count++; } is($count, 4, 'coalesce type-error'); } { # IRI my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); SELECT (IRI("http://example.org/") AS ?i) WHERE {} END warn RDF::Query->error unless ($query); my $iter = $query->execute( $model ); my @r = $iter->get_all; my $iri = RDF::Query::Node::Resource->new('http://example.org/'); is_deeply( \@r, [RDF::Query::VariableBindings->new({i=>$iri})], 'IRI() cast' ); } { # BNODE my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); SELECT (BNODE("xyz") AS ?x) (BNODE("abc") AS ?y) (BNODE("xyz") AS ?z) WHERE {} END warn RDF::Query->error unless ($query); my $iter = $query->execute( $model ); my $row = $iter->next; isa_ok( $row->{'x'}, 'RDF::Query::Node::Blank' ); isa_ok( $row->{'y'}, 'RDF::Query::Node::Blank' ); isa_ok( $row->{'z'}, 'RDF::Query::Node::Blank' ); is( $row->{'x'}->blank_identifier, $row->{'z'}->blank_identifier ); isnt( $row->{'x'}->blank_identifier, $row->{'y'}->blank_identifier ); } { # IN my $query = new RDF::Query ( 'SELECT * WHERE { FILTER(2 IN (1, 2, 3)) }', { lang => 'sparql11' } ); my $iter = $query->execute( $model ); my $row = $iter->next; isa_ok( $row, 'RDF::Query::VariableBindings', 'IN' ); } { # IN my $query = new RDF::Query ( 'SELECT * WHERE { FILTER(2 IN (1, 3)) }', { lang => 'sparql11' } ); my $iter = $query->execute( $model ); my $row = $iter->next; ok( not(defined($row)), 'IN 2' ); } { # NOT IN my $query = new RDF::Query ( 'SELECT * WHERE { FILTER(2 NOT IN ()) }', { lang => 'sparql11' } ); my $iter = $query->execute( $model ); my $row = $iter->next; isa_ok( $row, 'RDF::Query::VariableBindings', 'NOT IN' ); } { # NOT IN my $query = new RDF::Query ( 'SELECT * WHERE { FILTER(2 NOT IN (1/0, 2)) }', { lang => 'sparql11' } ); my $iter = $query->execute( $model ); my $row = $iter->next; ok( not(defined($row)), 'NOT IN 2' ); } } } RDF-Query-2.910/t/hooks.t000644 000765 000024 00000003232 11707542735 015120 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use Test::More; use Test::Exception; use RDF::Query; if ($ENV{RDFQUERY_NETWORK_TESTS}) { plan( tests => 3 ); } else { plan skip_all => 'No network. Set RDFQUERY_NETWORK_TESTS to run these tests.'; return; } SKIP: { eval { require LWP::Simple }; skip "LWP::Simple is not available", 3 if $@; my $query = RDF::Query->new(<<"END", undef, undef, 'sparql'); PREFIX foaf: PREFIX geo: SELECT ?lat ?long FROM WHERE { ?point a geo:Point ; geo:lat ?lat ; geo:long ?long . } END $query->add_hook( 'http://kasei.us/code/rdf-query/hooks/post-create-model', sub { my $self = shift; my $model = shift; my $long = RDF::Trine::Node::Resource->new( 'http://www.w3.org/2003/01/geo/wgs84_pos#long' ); my $stream = $model->get_statements( undef, $long, undef ); while (my $stmt = $stream->next) { my $l = $stmt->object->literal_value; my $dt = $stmt->object->literal_datatype; $l = sprintf( '%0.6f', ++$l ); $model->remove_statement( $stmt ); my $lit = RDF::Trine::Node::Literal->new( $l, undef, $dt ); my $add = RDF::Trine::Statement->new( $stmt->subject, $stmt->predicate, $lit ); $model->add_statement( $add ); } } ); my $count = 0; my $stream = $query->execute(); while (my $row = $stream->next) { my ($lat, $long) = @{ $row }{qw(lat long)}; is( $lat->literal_value, '51.477222', 'existing latitude' ); is( $long->literal_value, '1.000000', 'modified longitude' ); } continue { ++$count }; is( $count, 1, 'expecting one statement in model' ); } __END__ RDF-Query-2.910/t/iterators.t000644 000765 000024 00000005567 11707542735 016026 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(foaf.xrdf); my (@data) = test_models_and_classes( @files ); use Test::More; plan tests => 1 + 15 * scalar(@data); use RDF::Trine::Iterator (qw(sgrep smap)); use_ok( 'RDF::Query' ); foreach my $data (@data) { my $model = $data->{'modelobj'}; print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?homepage WHERE { ?person a foaf:Person ; foaf:name "Gregory Todd Williams" ; foaf:homepage ?homepage . } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my @names = $stream->binding_names; is_deeply( \@names, [qw(person homepage)], 'binding_names' ); is( $stream->binding_name( 1 ), 'homepage', 'bindging_name' ); my $homepage = $stream->binding_value_by_name( 'person' ); ok( $homepage->isa('RDF::Trine::Node::Resource'), 'binding_value_by_name' ); is( $homepage->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'binding_value_by_name' ); my @values = $stream->binding_values(); is( scalar(@values), 2, 'binding_values' ); my ($p, $h) = @values; ok( $p->isa('RDF::Trine::Node::Resource'), 'binding_values' ); is( $p->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'binding_values' ); ok( $h->isa('RDF::Trine::Node::Resource'), 'binding_values' ); is( $h->uri_value, 'http://kasei.us/', 'binding_values' ); my $count = $stream->bindings_count; is( $count, 2, 'bindings_count' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person WHERE { ?person a foaf:Person ; foaf:name "Gregory Todd Williams" . } END my $stream = $query->execute( $model ); my $string = $stream->to_string; like( $string, qr/\A\Q DESCRIBE ?person WHERE { ?person a foaf:Person ; foaf:name "Gregory Todd Williams" . } END my $stream = $query->execute( $model ); my $string = $stream->as_xml; like( $string, qr/^\Q SELECT ?person WHERE { ?person a foaf:Person ; foaf:name "Gregory Todd Williams" . } END my $stream = $query->execute( $model ); my $string = $stream->to_string('http://www.w3.org/2001/sw/DataAccess/json-sparql/'); like( $string, qr/\A\Q\E/m, 'to_string json' ); } } RDF-Query-2.910/t/literals.t000644 000765 000024 00000003270 11707542736 015617 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use_ok( 'RDF::Query' ); use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { print "# language typed literal\n"; my $query = new RDF::Query ( <<'END', undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gary P"@en ; foaf:homepage ?homepage . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $current = $stream->current; isa_ok( $current, 'HASH' ); my ($p, $h) = @{ $current }{qw(person homepage)}; ok( $h->isa('RDF::Trine::Node::Resource'), 'Got a resource for homepage' ); is( $h->uri_value, 'http://www.realify.com/~gary/', 'Got homepage' ); } SKIP: { print "# datatyped literal\n"; my $query = new RDF::Query ( <<'END', undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: SELECT ?image WHERE { ?image dc:date "2005-04-07T18:27:56-04:00"^^ } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $current = $stream->current; isa_ok( $current, 'HASH' ); my ($h) = @{ $current }{image}; ok( $h->isa('RDF::Trine::Node::Resource'), 'Got a resource for image' ); is( $h->uri_value, 'http://kasei.us/pictures/2005/20050422-WCCS_Dinner/images/DSC_8057.jpg', 'Got image by typed date' ); } } done_testing; RDF-Query-2.910/t/models.pl000755 000765 000024 00000005564 11712071567 015441 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use RDF::Query; use File::Spec; use URI::file; sub test_models { my @models = test_models_and_classes( @_ ); return map { $_->{ 'modelobj' } } @models; } sub test_models_and_classes { my @files = map { File::Spec->rel2abs( $_ ) } @_; my @uris = map { URI::file->new_abs( $_ ) } @files; my @models; { my $store = RDF::Trine::Store::Memory->new(); my $model = RDF::Trine::Model->new( $store ); foreach my $i (0 .. $#files) { my $file = $files[ $i ]; my $uri = $uris[ $i ]; RDF::Trine::Parser->parse_url_into_model( $uri, $model ); } my $data = {}; $data->{ modelobj } = $model; push(@models, $data); } { my ($model, $dsn, $user, $pass); if ($ENV{RDFQUERY_DBI_DATABASE} and $ENV{RDFQUERY_DBI_USER} and $ENV{RDFQUERY_DBI_PASS}) { $dsn = "DBI:mysql:database=$ENV{RDFQUERY_DBI_DATABASE}"; $user = $ENV{RDFQUERY_DBI_USER} || 'test'; $pass = $ENV{RDFQUERY_DBI_PASS} || 'test'; my ($model, $dsn, $user, $pass); if ($ENV{RDFQUERY_DBI_DATABASE} and $ENV{RDFQUERY_DBI_USER} and $ENV{RDFQUERY_DBI_PASS}) { $dsn = "DBI:mysql:database=$ENV{RDFQUERY_DBI_DATABASE}"; $user = $ENV{RDFQUERY_DBI_USER} || 'test'; $pass = $ENV{RDFQUERY_DBI_PASS} || 'test'; $model = eval { my $store = RDF::Trine::Store::DBI->temporary_store($dsn, $user, $pass); $model = RDF::Trine::Model->new( $store ); }; } else { $model = eval { my $store = RDF::Trine::Store::DBI->temporary_store(); $model = RDF::Trine::Model->new( $store ); } } if (not $@) { foreach my $i (0 .. $#files) { my $file = $files[ $i ]; my $uri = $uris[ $i ]; RDF::Trine::Parser->parse_url_into_model( $uri, $model ); } my $data = {}; $data->{ modelobj } = $model; push(@models, $data); } else { warn "Couldn't connect to database: $dsn, $user, $pass" if ($RDF::Query::debug); } } else { warn "RDF::Trine::Store::DBI not loaded: $@\n" if ($RDF::Query::debug); } } if (not $ENV{RDFQUERY_NO_REDLAND}) { eval { require "RDF::Redland"; }; unless ($@) { my $storage = new RDF::Redland::Storage("hashes", "test", "new='yes',hash-type='memory',contexts='yes'"); my $model = new RDF::Redland::Model($storage, ""); my $tmodel = RDF::Trine::Model->new( RDF::Trine::Store->new_with_object( $model ) ); foreach my $uri (@uris) { RDF::Trine::Parser->parse_url_into_model( $uri, $tmodel ); } my $data = { model => 'RDF::Redland::Model', store => 'RDF::Redland::Storage', statement => 'RDF::Redland::Statement', node => 'RDF::Redland::Node', resource => 'RDF::Redland::Node', literal => 'RDF::Redland::Node', blank => 'RDF::Redland::Node', }; $data->{ modelobj } = $model; push(@models, $data); } } if (scalar(@models) == 0) { Carp::confess; } return @models; } 1; RDF-Query-2.910/t/named-graphs.t000644 000765 000024 00000021272 11707542736 016350 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } use Test::More; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.trine.store.dbi = TRACE, Screen # log4perl.category.rdf.query = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my @models = test_models(); my $alice = URI::file->new_abs( 'data/named_graphs/alice.rdf' ); my $bob = URI::file->new_abs( 'data/named_graphs/bob.rdf' ); my $meta = URI::file->new_abs( 'data/named_graphs/meta.rdf' ); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; SKIP: { { print "# variable named graph\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?src ?name FROM NAMED <${alice}> WHERE { GRAPH ?src { ?x foaf:name ?name } } END my ($src, $name) = $query->get( $model ); ok( $src, 'got source' ); ok( $name, 'got name' ); is( $src->uri_value, $alice, 'graph uri' ); is( $name->literal_value, 'Alice', 'name literal' ); } { print "# uri named graph (fail: graph)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?name FROM NAMED <${alice}> WHERE { GRAPH { ?x foaf:name ?name } } END my $stream = $query->execute( $model ); my $row = $stream->next; is( $row, undef, 'no results' ); } { print "# uri named graph (fail: pattern)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?src ?name FROM NAMED <${alice}> WHERE { GRAPH ?src { ?x ?name } } END my ($plan, $ctx) = $query->prepare( $model ); my $stream = $query->execute_plan( $plan, $ctx ); my $row = $stream->next; is( $row, undef, 'no results' ); } { print "# uri named graph with multiple graphs\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?mbox FROM NAMED <${alice}> FROM NAMED <${bob}> WHERE { GRAPH <$bob> { ?x foaf:mbox ?mbox } . } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { isa_ok( $row, 'HASH' ); my $mbox = $row->{mbox}; ok( $mbox, 'got mbox' ); my $uri = $mbox->uri_value; is( $uri, 'mailto:bob@oldcorp.example.org', "mbox uri: $uri" ); $count++; } is( $count, 1, 'one result' ); } { print "# variable named graph with multiple graphs; select from one\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?src ?mbox FROM NAMED <${alice}> FROM NAMED <${bob}> WHERE { GRAPH ?src { ?x foaf:name "Alice"; foaf:mbox ?mbox } . } END my ($plan, $ctx) = $query->prepare( $model ); my $iter = $query->execute_plan( $plan, $ctx ); while (my $row = $iter->next) { my $src = $row->{src}; my $mbox = $row->{mbox}; ok( $src, 'got source' ); ok( $mbox, 'got mbox' ); is( $src->uri_value, $alice, 'graph uri' ); is( $mbox->uri_value, 'mailto:alice@work.example', 'mbox uri' ); } is( $iter->seen_count, 1, 'expected result count' ); } { print "# variable named graph with multiple graphs; select from both\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?g ?name FROM NAMED <${alice}> FROM NAMED <${bob}> FROM <${meta}> WHERE { GRAPH ?g { ?x foaf:name ?name } . } END my %expected = ( $alice => "Alice", $bob => "Bob", ); my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->current) { $stream->next; isa_ok( $row, 'HASH' ); my ($graph, $name) = @{ $row }{qw(g name)}; my $uri = $graph->uri_value; ok( exists $expected{ $uri }, "Known GRAPH: $uri" ); my $expect = $expected{ $uri }; ok( $name, 'got name' ); my $l_name = $name->literal_value; is( $l_name, $expect, "got name: $l_name" ); $count++; } is( $count, 2, 'got results' ); } { print "# variable named graph with multiple graphs; non-named graph triples\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?g ?name ?topic FROM NAMED <${alice}> FROM NAMED <${bob}> FROM <${meta}> WHERE { GRAPH ?g { ?x foaf:name ?name } . ?g foaf:topic ?topic . } END my %expected = ( $alice => "Alice", $bob => "Bob", ); my $stream = $query->execute( $model ); while (my $row = $stream->next) { isa_ok( $row, 'HASH' ); my ($graph, $name, $topic) = @{ $row }{qw(g name topic)}; my $uri = $graph->uri_value; ok( exists $expected{ $uri }, "Known GRAPH: $uri" ); my $expect = $expected{ $uri }; ok( $name, 'got name' ); ok( $topic, 'got topic' ); my $l_name = $name->literal_value; my $l_topic = $topic->literal_value; is( $l_name, $expect, "got name: $l_name" ); is( $l_topic, $expect, "got topic: $l_topic" ); } is( $stream->seen_count, 2, 'got results' ); } } { print "# graph-1\n"; my $foaf = URI::file->new_abs( "data/foaf.xrdf" ); my $about = URI::file->new_abs( "data/about.xrdf" ); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX rdfs: PREFIX dcterms: SELECT DISTINCT ?s ?o FROM <$foaf> FROM NAMED <$about> WHERE { ?s dcterms:spatial ?o } END my $stream = $query->execute(); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $data = $stream->next) { $count++; } is( $count, 0, 'graph-1: BGP does not match NAMED data' ); } { print "# graph-2\n"; my $foaf = URI::file->new_abs( "data/foaf.xrdf" ); my $about = URI::file->new_abs( "data/about.xrdf" ); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX rdfs: PREFIX dcterms: SELECT DISTINCT ?g ?s FROM <$foaf> FROM NAMED <$about> WHERE { GRAPH ?g { ?s foaf:firstName "Gary" } } END my $stream = $query->execute(); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $data = $stream->next) { $count++; } is( $count, 0, 'graph-2: GRAPH does not match non-NAMED data' ); } { print "# graph-3\n"; my $foaf = URI::file->new_abs( "data/foaf.xrdf" ); my $about = URI::file->new_abs( "data/about.xrdf" ); my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX rdfs: SELECT DISTINCT ?p ?g ?img FROM <$foaf> FROM NAMED <$about> WHERE { ?p a foaf:Person . GRAPH ?g { ?img foaf:maker ?p } . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while ($stream and not $stream->finished) { my $row = $stream->current; my ($p,$g,$i) = @{ $row }{qw(p g img)}; ok( $g->isa('RDF::Trine::Node::Resource'), 'graph-3: context is resource' ); ok( $p->isa('RDF::Trine::Node::Resource'), 'graph-3: person is resource' ); is( $p->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'graph-3: correct person uri' ); like( $i->uri_value, qr/[.]jpg/, 'graph-3: made image' ); $count++; } continue { $stream->next } is( $count, 4, 'graph-3: expected count' ); } { print "# find all graph names\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ) or die RDF::Query->error; SELECT ?g FROM NAMED <${alice}> FROM NAMED <${bob}> WHERE { GRAPH ?g {} . } END my $count = 0; my $stream = $query->execute( $model ); while (my $row = $stream->next) { isa_ok( $row, 'HASH' ); my $g = $row->{g}; isa_ok( $g, 'RDF::Query::Node::Resource' ); like( $g->uri_value, qr/(alice|bob).rdf$/, 'expected graph name' ); $count++; } is( $count, 2, 'two expected graph names' ); } } done_testing(); RDF-Query-2.910/t/optional.t000644 000765 000024 00000011437 11707542736 015631 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 31); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?nick WHERE { ?person foaf:name "Lauren B" . OPTIONAL { ?person foaf:nick ?nick } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $row = $stream->next; isa_ok( $row, "HASH" ); my ($p,$n) = @{ $row }{qw(person nick)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node' ); is( $n, undef, 'missing nick' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?nick WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:nick ?nick } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); while (my $row = $stream->next) { isa_ok( $row, "HASH" ); my ($p,$n) = @{ $row }{qw(person nick)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node' ); ok( $n->isa('RDF::Trine::Node::Literal'), 'isa_literal(nick)' ); like( ($n and $n->as_string), qr/kasei|The Samo Fool/, ($n and $n->as_string) ); last; } } { print "# optional with trailing triples\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?nick ?page WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:nick ?nick } . ?person foaf:homepage ?page } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); while (my $row = $stream->next) { isa_ok( $row, "HASH" ); my ($p,$n,$h) = @{ $row }{qw(person nick page)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node' ); ok( $n->isa('RDF::Trine::Node::Literal'), 'isa_literal(nick)' ); ok( $h->isa('RDF::Trine::Node::Resource'), 'isa_resource(homepage)' ); is( $h->uri_value, 'http://kasei.us/' ); like( ($n and $n->as_string), qr/kasei|The Samo Fool/, ($n and $n->as_string) ); last; } } { print "# 1-triple optional\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: SELECT ?person ?h WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:homepage ?h . } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $row = $stream->next; isa_ok( $row, "HASH" ); my ($p,$h) = @{ $row }{qw(person h)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node(person)' ); ok( $h->isa('RDF::Trine::Node'), 'isa_node(homepage)' ); } { print "# 2-triple optional\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: SELECT ?person ?h ?title WHERE { ?person foaf:name "Gregory Todd Williams" . OPTIONAL { ?person foaf:homepage ?h . ?h dc:title ?title } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $row = $stream->next; isa_ok( $row, "HASH" ); my ($p,$h,$t) = @{ $row }{qw(person h title)}; ok( $p->isa('RDF::Trine::Node'), 'isa_node' ); is( $h, undef, 'no homepage' ); is( $t, undef, 'no homepage title' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?nick WHERE { ?person foaf:name "Lauren B" . OPTIONAL { ?person foaf:nick ?nick } . FILTER BOUND(?nick) . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $row = $stream->next; ok( not($row), 'no results: successful BOUND() filter' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?school WHERE { ?person a foaf:Person ; foaf:nick "kasei" . OPTIONAL { ?person foaf:schoolHomepage ?school . } . } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { my $school = $row->{school}; my $str = $school->as_string; like( $str, qr<(smmusd|wheatonma)>, "exected school: $str" ); $count++; } is( $count, 2, 'expected result count' ); } } RDF-Query-2.910/t/plan.t000644 000765 000024 00000055227 12054225575 014736 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use URI::file; use Test::More; use Scalar::Util qw(blessed); use RDF::Query::Node qw(iri); use RDF::Query::Error qw(:try); use lib qw(. t); BEGIN { require "models.pl"; } ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.quad = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my $file = 'data/foaf.xrdf'; my %named = map { $_ => URI::file->new_abs( File::Spec->rel2abs("data/named_graphs/$_") ) } qw(alice.rdf bob.rdf meta.rdf repeats1.rdf repeats2.rdf); my @models = test_models_and_classes($file); eval { require LWP::Simple }; if ($@) { plan skip_all => "LWP::Simple is not available for loading URLs"; return; } elsif (not exists $ENV{RDFQUERY_DEV_TESTS}) { plan skip_all => 'Developer tests. Set RDFQUERY_DEV_TESTS to run these tests.'; return; } use RDF::Query; use RDF::Trine::Namespace qw(rdf foaf); use RDF::Query::Plan; my $nil = RDF::Trine::Node::Nil->new(); ################################################################################ { { my $var = RDF::Trine::Node::Variable->new('s'); my $triple = RDF::Query::Plan::Quad->new( $var, $rdf->type, $foaf->Person, $nil ); my $plan = RDF::Query::Plan::Limit->new( 2, $triple ); is( _CLEAN_WS($plan->sse), '(limit 2 (quad ?s (nil)))', 'sse: triple' ) or die; } { my $var = RDF::Trine::Node::Variable->new('s'); my $triple = RDF::Query::Plan::Quad->new( $var, $rdf->type, $foaf->Person, $nil ); my $plan = RDF::Query::Plan::Offset->new( 2, $triple ); is( _CLEAN_WS($plan->sse), '(offset 2 (quad ?s (nil)))', 'sse: offset' ) or die; } { my $var = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $var, $foaf->homepage, RDF::Trine::Node::Variable->new('page'), $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $var, $foaf->name, RDF::Trine::Node::Variable->new('name'), $nil ); my $plan = RDF::Query::Plan::ThresholdUnion->new( 7, $plan_a, $plan_b ); is( _CLEAN_WS($plan->sse), '(threshold-union 7 (quad ?p ?page (nil)) (quad ?p ?name (nil)))', 'sse: threshold-union' ) or die; } { my $var = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $var, $rdf->type, $foaf->Person, $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $var, $foaf->homepage, RDF::Trine::Node::Variable->new('page'), $nil ); my $subplan = RDF::Query::Plan::Join::NestedLoop->new( $plan_a, $plan_b ); my $plan = RDF::Query::Plan::Service->new( 'http://kasei.us/sparql', $subplan, 0, 'PREFIX foaf: SELECT DISTINCT * WHERE { ?p a foaf:Person ; foaf:homepage ?page }' ); is( _CLEAN_WS($plan->sse), '(service "PREFIX foaf: SELECT DISTINCT * WHERE { ?p a foaf:Person ; foaf:homepage ?page }")', 'sse: service' ) or die; } } foreach my $data (@models) { my $model = $data->{modelobj}; print "\n#################################\n"; print "### Using model: $model\n\n"; unless ($model->isa('RDF::Trine::Model')) { $model = RDF::Trine::Model->new( RDF::Trine::Store->new_with_object( $model ) ); } my $parser = RDF::Trine::Parser->new('rdfxml'); foreach my $uri (values %named) { $parser->parse_url_into_model( "$uri", $model, context => iri("$uri") ); } my $context = RDF::Query::ExecutionContext->new( bound => {}, model => $model, ); { my $query = RDF::Query->new( <<"END", { lang => 'sparql11', force_no_optimization => 1 } ); # force_no_optimization because otherwise we'll get a model-optimized BGP instead of the bind-join PREFIX foaf: SELECT ?p ?name WHERE { ?p foaf:firstName ?name . } VALUES ?name { ("Gregory") ("Gary") } END my ($plan, $context) = $query->prepare(); is( _CLEAN_WS($plan->sse), '(project (p name) (bind-join (quad ?p ?name (nil)) (table (row [?name "Gregory"]) (row [?name "Gary"]))))', 'sse: constant' ) or die; } { my $parser = RDF::Query::Parser::SPARQL->new(); my $ns = { foaf => 'http://xmlns.com/foaf/0.1/' }; my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person }', undef, $ns)->patterns; my ($t) = $bgp->triples; my ($plan) = RDF::Query::Plan->generate_plans( $t, $context ); isa_ok( $plan, 'RDF::Query::Plan::Quad', 'quad algebra to plan' ); is( $plan->sse, '(quad ?p (nil))', 'sse: triple plan' ) or die; } { my $parser = RDF::Query::Parser::SPARQL->new(); my $ns = { foaf => 'http://xmlns.com/foaf/0.1/' }; my ($bgp) = $parser->parse_pattern('{ ?p a foaf:Person ; foaf:name ?name }', undef, $ns)->patterns; my ($plan) = RDF::Query::Plan->generate_plans( $bgp, $context ); isa_ok( $plan, 'RDF::Query::Plan::BasicGraphPattern', 'bgp algebra to plan' ); } { my $parser = RDF::Query::Parser::SPARQL->new(); my $parsed = $parser->parse( 'PREFIX foaf: SELECT * WHERE { ?p foaf:name ?name }' ); my $algebra = $parsed->{triples}[0]->pattern; my ($plan) = RDF::Query::Plan->generate_plans( $algebra, $context ); isa_ok( $plan, 'RDF::Query::Plan::Quad', 'ggp algebra to plan' ); } { my $parser = RDF::Query::Parser::SPARQL->new(); my $parsed = $parser->parse( 'PREFIX foaf: SELECT * WHERE { ?p foaf:name ?name } ORDER BY ?name' ); my $algebra = $parsed->{triples}[0]->pattern; my ($plan) = RDF::Query::Plan->generate_plans( $algebra, $context ); isa_ok( $plan, 'RDF::Query::Plan::Sort', 'sort algebra to plan' ); isa_ok( $plan->pattern, 'RDF::Query::Plan::Quad', 'triple algebra to plan' ); is( _CLEAN_WS($plan->sse), '(order (quad ?p ?name (nil)) ((asc ?name)))', 'sse: sort' ) or die; } { my $parser = RDF::Query::Parser::SPARQL->new(); my $parsed = $parser->parse( 'PREFIX foaf: SELECT * WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER(?name = "Greg") }' ); my $algebra = $parsed->{triples}[0]->pattern; my @plans = RDF::Query::Plan->generate_plans( $algebra, $context ); my ($plan) = sort @plans; isa_ok( $plan, 'RDF::Query::Plan::Filter', 'filter algebra to plan' ); isa_ok( $plan->pattern, 'RDF::Query::Plan::BasicGraphPattern', 'bgp algebra to plan' ); is( _CLEAN_WS($plan->sse), '(filter (== ?name "Greg") (bgp (quad ?p (nil)) (quad ?p ?name (nil))))', 'sse: filter' ); } ############################################################################ { # simple triple my @triple = ( RDF::Trine::Node::Variable->new('p'), $rdf->type, $foaf->Person, ); my $plan = RDF::Query::Plan::Quad->new( @triple, $nil ); my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); $count++; } $plan->close; is( $count, 4, "expected result count for triple (?p a foaf:Person)" ); } { # repeats of the same variable in one triple my @triple = ( RDF::Trine::Node::Variable->new('type'), RDF::Trine::Node::Variable->new('type'), RDF::Trine::Node::Variable->new('obj'), ); my $plan = RDF::Query::Plan::Quad->new( @triple, $nil ); foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); $count++; } is( $count, 1, "expected result count for triple with repeated variables (pass $pass)" ); $plan->close; } } { # simple quad my @quad = ( RDF::Trine::Node::Variable->new('p'), $foaf->name, RDF::Trine::Node::Variable->new('name'), RDF::Trine::Node::Variable->new('c'), ); my $plan = RDF::Query::Plan::Quad->new( @quad ); is( _CLEAN_WS($plan->sse), '(quad ?p ?name ?c)', 'sse: quad' ) or die; my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); my $name = lc($row->{name}->literal_value); like( $row->{name}, qr#(Alice|Bob|Gary|Gregory|Liz|Lauren)#i, 'expected person name from named graph' ); unless (blessed($row->{c}) and $row->{c}->isa('RDF::Trine::Node::Nil')) { like( $row->{c}, qr#${name}[.]rdf>$#, 'graph name matches person name' ); } $count++; } $plan->close; is( $count, 6, "expected result count for quad (?p foaf:name ?name ?c)" ); } { # simple quad converted from algebra my $parser = RDF::Query::Parser::SPARQL->new(); my $parsed = $parser->parse( 'PREFIX foaf: SELECT * WHERE { GRAPH ?c { ?p foaf:name ?name } }' ); my $algebra = $parsed->{triples}[0]; my ($plan) = RDF::Query::Plan->generate_plans( $algebra, $context ); my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); my $name = lc($row->{name}->literal_value); like( $row->{name}, qr#(Alice|Bob)#i, 'expected person name from named graph' ); like( $row->{c}, qr#${name}[.]rdf>$#, 'graph name matches person name' ); $count++; } $plan->close; is( $count, 2, "expected result count for quad (?p foaf:name ?name ?c)" ); } { # repeats of the same variable in one quad my @quad = ( RDF::Query::Node::Variable->new('prop'), RDF::Query::Node::Variable->new('prop'), RDF::Query::Node::Variable->new('obj'), RDF::Query::Node::Variable->new('c'), ); my $plan = RDF::Query::Plan::Quad->new( @quad ); foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); like( $row->{prop}, qr[#(type|label)>$], 'expected property' ); $count++; } is( $count, 3, "expected result count for quad with repeated variables (pass $pass)" ); $plan->close; } } { # repeats of the same variable in one quad, constrained to a specific graph my @quad = ( RDF::Trine::Node::Variable->new('prop'), RDF::Trine::Node::Variable->new('prop'), RDF::Trine::Node::Variable->new('obj'), RDF::Trine::Node::Resource->new("$named{'repeats1.rdf'}"), ); my $plan = RDF::Query::Plan::Quad->new( @quad ); foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); like( $row->{prop}, qr[#type>$], 'expected property' ); $count++; } is( $count, 1, "expected result count for quad with repeated variables (pass $pass)" ); $plan->close; } } { # nested loop join my $var = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $var, $foaf->homepage, RDF::Trine::Node::Variable->new('page'), RDF::Trine::Node::Nil->new() ); my $plan_b = RDF::Query::Plan::Quad->new( $var, $foaf->name, RDF::Trine::Node::Variable->new('name'), RDF::Trine::Node::Nil->new() ); my $plan = RDF::Query::Plan::Join::NestedLoop->new( $plan_a, $plan_b ); is( _CLEAN_WS($plan->sse), '(nestedloop-join (quad ?p ?page (nil)) (quad ?p ?name (nil)))', 'sse: nestedloop-join' ) or die; foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); like( $row->{p}, qr#^(_:|{page}, qr#^close; } } { # OPTIONAL nested loop join my $var = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $var, $foaf->name, RDF::Trine::Node::Variable->new('name'), $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $var, $foaf->homepage, RDF::Trine::Node::Variable->new('page'), $nil ); my $plan = RDF::Query::Plan::Join::PushDownNestedLoop->new( $plan_a, $plan_b, 1 ); is( _CLEAN_WS($plan->sse), '(bind-leftjoin (quad ?p ?name (nil)) (quad ?p ?page (nil)))', 'sse: bind-leftjoin' ) or die; foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); my @rows = $plan->get_all; my @names = map { $_->literal_value } grep { defined($_) } map { $_->{name} } @rows; my @pages = map { $_->uri_value } grep { defined($_) } map { $_->{page} } @rows; is( scalar(@names), 4, 'expected result count for names in OPTIONAL join' ); is( scalar(@pages), 2, 'expected result count for homepages in OPTIONAL join' ); $plan->close; } } { # union my $var = RDF::Trine::Node::Variable->new('s'); my $plan_a = RDF::Query::Plan::Quad->new( $var, $rdf->type, $foaf->Person, $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $var, $rdf->type, $foaf->PersonalProfileDocument, $nil ); my $plan = RDF::Query::Plan::Union->new( $plan_a, $plan_b ); is( _CLEAN_WS($plan->sse), '(union (quad ?s (nil)) (quad ?s (nil)))' ) or die; foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { $count++; } is( $count, 5, "expected result count for union (pass $pass)" ); $plan->close; } } # SKIP: { # skip "network tests. Set RDFQUERY_NETWORK_TESTS to run these tests.", 2 unless (exists $ENV{RDFQUERY_NETWORK_TESTS}); # # my $var = RDF::Trine::Node::Variable->new('p'); # my $plan_a = RDF::Query::Plan::Triple->new( $var, $rdf->type, $foaf->Person ); # my $plan_b = RDF::Query::Plan::Triple->new( $var, $foaf->homepage, RDF::Trine::Node::Variable->new('page') ); # my $subplan = RDF::Query::Plan::Join::NestedLoop->new( $plan_a, $plan_b ); # my $plan = RDF::Query::Plan::Service->new( 'http://kasei.us/sparql', $subplan, 'PREFIX foaf: SELECT DISTINCT * WHERE { ?p a foaf:Person ; foaf:homepage ?page }' ); # # foreach my $pass (1..2) { # my $count = 0; # $plan->execute( $context ); # while (my $row = $plan->next) { # if ($count == 0) { # isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); # } # $count++; # } # cmp_ok( $count, '>', 1, "positive result count for SERVICE plan (pass $pass)" ); # $plan->close; # } # } SKIP: { skip "network tests. Set RDFQUERY_NETWORK_TESTS to run these tests.", 2 unless (exists $ENV{RDFQUERY_NETWORK_TESTS}); my $parser = RDF::Query::Parser::SPARQL11->new(); my $parsed = $parser->parse( 'PREFIX foaf: SELECT * WHERE { SERVICE { ?p a foaf:Person ; foaf:homepage ?page } }' ); my $algebra = $parsed->{triples}[0]; my ($plan) = RDF::Query::Plan->generate_plans( $algebra, $context ); foreach my $pass (1..2) { my $skip = 2; my $count = 0; try { $plan->execute( $context ); while (my $row = $plan->next) { if ($count == 0) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); } $count++; } cmp_ok( $count, '>', 1, "positive result count for generated SERVICE plan (pass $pass)" ); $plan->close; $skip--; } catch RDF::Query::Error::ExecutionError with { my $e = shift; skip $e->text, $skip; }; } } { # project on ?p { ?s ?p foaf:Person } my $s = RDF::Trine::Node::Variable->new('s'); my $p = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $s, $p, $foaf->Person, $nil ); my $plan = RDF::Query::Plan::Project->new( $plan_a, ['p'] ); is( _CLEAN_WS($plan->sse), '(project (p) (quad ?s ?p (nil)))', 'sse: project' ) or die; foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); my @keys = $row->variables; is_deeply( \@keys, ['p'], 'projected variable list' ); $count++; } is( $count, 4, "expected result count for project (pass $pass)" ); $plan->close; } } { # { _:s rdf:first [] ; ?p [] } my $s = RDF::Trine::Node::Variable->new('__s'); my $f = RDF::Trine::Node::Variable->new('__f'); my $r = RDF::Trine::Node::Variable->new('__r'); my $p = RDF::Trine::Node::Variable->new('p'); my $plan_a = RDF::Query::Plan::Quad->new( $s, $rdf->first, $f, $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $s, $p, $r, $nil ); my $join = RDF::Query::Plan::Join::NestedLoop->new( $plan_a, $plan_b ); my $proj = RDF::Query::Plan::Project->new( $join, ['p'] ); my $plan = RDF::Query::Plan::Distinct->new( $proj ); is( _CLEAN_WS($plan->sse), '(distinct (project (p) (nestedloop-join (quad ?__s ?__f (nil)) (quad ?__s ?p ?__r (nil)))))', 'sse: distinct-project-join' ) or die; foreach my $pass (1..2) { my $count = 0; $plan->execute( $context ); while (my $row = $plan->next) { isa_ok( $row, 'RDF::Query::VariableBindings', 'variable bindings' ); like( $row->{p}, qr[^($], 'expected predicate URI' ); $count++; } is( $count, 2, "expected result count for distinct (pass $pass)" ); $plan->close; } } { # simple variable sort with numerics my $s = RDF::Trine::Node::Variable->new('__s'); my $var = RDF::Trine::Node::Variable->new('v'); my $triple = RDF::Query::Plan::Quad->new( $s, $rdf->first, $var, $nil ); my $proj = RDF::Query::Plan::Project->new( $triple, ['v'] ); foreach my $data ([0 => [1,2,3]], [1 => [3,2,1]]) { my ($order, $expect) = @$data; my $dir = ($order) ? 'DESC' : 'ASC'; my $plan = RDF::Query::Plan::Sort->new( $proj, [$var, $order] ); my $count = 0; $plan->execute( $context ); my @rows = $plan->get_all; my @values = map { $_->{ v }->literal_value } @rows; is_deeply( \@values, $expect, "expected $dir sort values on numerics" ); $plan->close; } } { # simple variable sort with plain literals my $s = RDF::Trine::Node::Variable->new('__s'); my $var = RDF::Trine::Node::Variable->new('v'); my $triple = RDF::Query::Plan::Quad->new( $s, $foaf->name, $var, $nil ); my $proj = RDF::Query::Plan::Project->new( $triple, ['v'] ); my $expr = RDF::Query::Expression::Function->new( 'sparql:str', $var ); my @expect = ('Gary P', 'Gregory Todd Williams', 'Lauren B', 'Liz F'); foreach my $data ([0 => [@expect]], [1 => [reverse @expect]]) { my ($order, $expect) = @$data; my $dir = ($order) ? 'DESC' : 'ASC'; my $plan = RDF::Query::Plan::Sort->new( $proj, [$expr, $order] ); my $count = 0; $plan->execute( $context ); my @rows = $plan->get_all; my @values = map { $_->{ v }->literal_value } @rows; is_deeply( \@values, $expect, "expected $dir sort values on plain literals" ); $plan->close; } } { # sort with multiple expressions my $s = RDF::Trine::Node::Variable->new('__s'); my $s2 = RDF::Trine::Node::Variable->new('__s2'); my $var = RDF::Trine::Node::Variable->new('v'); my $name = RDF::Trine::Node::Variable->new('name'); my $plan_a = RDF::Query::Plan::Quad->new( $s, $rdf->first, $var, $nil ); my $plan_b = RDF::Query::Plan::Quad->new( $s2, $foaf->name, $name, $nil ); my $join = RDF::Query::Plan::Join::NestedLoop->new( $plan_a, $plan_b ); my $proj = RDF::Query::Plan::Project->new( $join, [qw(v name)] ); my $expr1 = RDF::Query::Expression::Binary->new( '*', RDF::Query::Expression::Function->new( 'http://www.w3.org/2001/XMLSchema#integer', $var ), RDF::Query::Node::Literal->new('-1', undef, 'http://www.w3.org/2001/XMLSchema#integer') ); my $expr2 = RDF::Query::Expression::Function->new( 'sparql:str', $name ); my $plan = RDF::Query::Plan::Sort->new( $proj, [$expr1, 0], [$expr2, 1] ); my @expect_v = (3,2,1); my @expect_name = reverse('Gary P', 'Gregory Todd Williams', 'Lauren B', 'Liz F'); $plan->execute( $context ); my @rows = $plan->get_all; my @v = map { $_->{ v }->literal_value } @rows; my @names = map { $_->{ name }->literal_value } @rows; is_deeply( \@v, [map { ($_)x4 } @expect_v], "expected primary sort values on numerics" ); is_deeply( \@names, [(@expect_name)x3], "expected secondary sort values on plain literals" ); $plan->close; } { # filter my $parser = RDF::Query::Parser::SPARQL->new(); my $ns = { foaf => 'http://xmlns.com/foaf/0.1/' }; my ($algebra) = $parser->parse_pattern('{ ?p a foaf:Person ; foaf:firstName ?name . FILTER(?name = "Gregory") }', undef, $ns); my ($join) = RDF::Query::Plan->generate_plans( $algebra, $context ); my $plan = RDF::Query::Plan::Project->new( $join, [qw(name)] ); $plan->execute( $context ); my @rows = $plan->get_all; my @v = map { $_->{ name }->literal_value } @rows; is_deeply( \@v, ['Gregory'], "expected filtered values on literals" ); $plan->close; } { # construct my $parser = RDF::Query::Parser::SPARQL->new(); my $ns = { foaf => 'http://xmlns.com/foaf/0.1/' }; my ($algebra) = $parser->parse_pattern('{ ?p foaf:firstName ?name }', undef, $ns); my ($pat) = RDF::Query::Plan->generate_plans( $algebra, $context ); my $st = RDF::Trine::Statement->new( RDF::Trine::Node::Variable->new('p'), $foaf->name, RDF::Trine::Node::Variable->new('name') ); my $plan = RDF::Query::Plan::Construct->new( $pat, [ $st ] ); is( _CLEAN_WS($plan->sse), '(construct (quad ?p ?name (nil)) ((triple ?p ?name)))', 'sse: construct' ) or die; $plan->execute( $context ); my $count = 0; while (my $row = $plan->next) { isa_ok( $row, 'RDF::Trine::Statement' ); my $pred = $row->predicate(); my $obj = $row->object(); is( $pred->as_string, '', 'expected CONSTRUCT predicate' ); like( $obj->as_string, qr<"(.+)">, 'expected CONSTRUCT object' ); $count++; } is( $count, 4, 'expected CONSTRUCT triple count' ); $plan->close; } } done_testing; sub _CLEAN_WS { my $string = shift; for ($string) { s/\s+/ /g; } return $string; } RDF-Query-2.910/t/protocol-serialization.t000644 000765 000024 00000007775 11707542736 020532 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf Flower-2.rdf); my @models = test_models( @files ); use Test::More; eval "use Test::JSON 0.03; use JSON 2.0;"; my $run_json_tests = (not $@) ? 1 : 0; my $tests_per_model = 7 + ($run_json_tests ? 6 : 0); plan tests => 1 + ($tests_per_model * scalar(@models)); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER REGEX(STR(?homepage), "kasei") } LIMIT 1 END my $stream = $query->execute( $model ); ok( $stream->is_bindings, 'Bindings result' ); my $xml = $stream->as_xml; $xml =~ s/^.* http://kasei.us/about/foaf.xrdf#greg http://kasei.us/ END is( $xml, $expect, 'XML Bindings Results formatting' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: ASK { ?person foaf:name "Gregory Todd Williams" } END my $stream = $query->execute( $model ); ok( $stream->is_boolean, 'Boolean result' ); my $xml = $stream->as_xml; like( $xml, qr%true%sm, 'XML Boolean Results formatting' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: CONSTRUCT { _:somebody foaf:name ?name; foaf:made ?thing } WHERE { ?thing dc:creator ?name } END my $stream = $query->execute( $model ); ok( $stream->is_graph, 'Graph result' ); my $xml = $stream->as_xml; # XXX remove eval when removing the TODO! no warnings 'uninitialized'; like( $xml, qr%name.*?>Greg Williams<%ms, 'XML Results formatting' ); like( $xml, qr%made\s+.*?rdf:resource="http://kasei\.us/pictures/2004/20040909-Ireland/images/DSC_5705\.jpg"%ms, 'XML Results formatting' ); } ### JSON Tests sub JSON::true; sub JSON::false; if ($run_json_tests) { { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?person ?homepage WHERE { ?person foaf:name "Gregory Todd Williams" . ?person foaf:homepage ?homepage . FILTER REGEX(STR(?homepage), "kasei") } ORDER BY ?homepage LIMIT 1 END my $stream = $query->execute( $model ); ok( $stream->is_bindings, 'Bindings result' ); my $js = $stream->as_json(); my $expect = { head => { vars => [qw(person homepage)] }, results => { ordered => JSON::true, distinct => JSON::false, bindings => [ { person => { type => 'uri', value => 'http://kasei.us/about/foaf.xrdf#greg' }, homepage => { type => 'uri', value => 'http://kasei.us/' }, } ], } }; is_valid_json( $js, 'valid json syntax' ); is_json( $js, to_json($expect), 'expected json results' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: ASK { ?person foaf:name "Gregory Todd Williams" } END my $stream = $query->execute( $model ); ok( $stream->is_boolean, 'Boolean result' ); my $js = $stream->as_json; my $expect = { head => { vars => [] }, boolean => JSON::true, }; is_valid_json( $js, 'valid json syntax' ); is_json( $js, to_json($expect), 'expected json results' ); } } } RDF-Query-2.910/t/queryform-ask.t000644 000765 000024 00000002025 11707542736 016602 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 3); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: ASK { ?person foaf:name "Gregory Todd Williams" } END my $stream = $query->execute( $model ); ok( $stream->is_boolean, "Stream is boolean result" ); my $ok = $stream->get_boolean(); ok( $ok, 'Exists in model' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: ASK { ?person foaf:name "Rene Descartes" } END my $stream = $query->execute( $model ); my $ok = $stream->get_boolean(); ok( not($ok), 'Not in model' ); } } RDF-Query-2.910/t/queryform-construct.t000644 000765 000024 00000004344 11707542736 020056 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf Flower-2.rdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 23); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: CONSTRUCT { ?person foaf:name ?name } WHERE { ?person foaf:firstName ?name } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count = 0; while (my $stmt = $stream->next()) { my $p = $stmt->predicate; my $s = $p->as_string; ok( $s, "person with firstName: $s" ); $count++; } is( $count, 4, 'expected foaf:firstName in construct count' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: CONSTRUCT { _:somebody foaf:name ?name; foaf:made ?thing } WHERE { ?thing dc:creator ?name } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count = 0; while (my $stmt = $stream->next) { my $p = $stmt->predicate; my $s = $p->as_string; like( $s, qr#foaf/0.1/(name|made)#, "predicate looks good: $s" ); $count++; } is( $count, 8, 'expected dc:creator in construct count' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dc: CONSTRUCT { ?p a foaf:Person ; foaf:aimChatID ?a } WHERE { ?p a foaf:Person . OPTIONAL { ?p foaf:aimChatID ?a } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count = 0; while (my $stmt = $stream->next) { my $p = $stmt->predicate; my $s = $p->as_string; like( $s, qr!(foaf/0.1/aimChatID)|(rdf-syntax-ns#type)!, "predicate looks good: $s" ); $count++; } is( $count, 5, 'expected optional foaf:aimChatID in construct count' ); } } RDF-Query-2.910/t/queryform-describe.t000644 000765 000024 00000004261 11707542736 017610 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: DESCRIBE ?person WHERE { ?person foaf:name "Gregory Todd Williams" } END my $stream = $query->execute( $model ); ok( $stream->is_graph, "Stream is graph result" ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count = 0; while (my $stmt = $stream->next) { my $p = $stmt->predicate; my $s = $p->as_string; ok( $s, $s ); ++$count; } # is( $count, 54, 'describe person expected graph size' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: DESCRIBE ?person WHERE { ?image a foaf:Image ; foaf:maker ?person . } END my $stream = $query->execute( $model ); ok( $stream->is_graph, "Stream is graph result" ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count = 0; while (my $stmt = $stream->next) { my $p = $stmt->predicate; my $s = $p->as_string; ok( $s, $s ); ++$count; } # is( $count, 54, 'describe person expected graph size' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: DESCRIBE END my $stream = $query->execute( $model ); ok( $stream->is_graph, "Stream is graph result" ); isa_ok( $stream, 'RDF::Trine::Iterator', 'describe resource returns graph iterator' ); my $count = 0; while (my $stmt = $stream->next) { my $p = $stmt->predicate; like( $p->uri_value, qr<^(http://xmlns.com/foaf/0.1/maker|http://www.w3.org/1999/02/22-rdf-syntax-ns#type|http://xmlns.com/wot/0.1/assurance|http://purl.org/dc/elements/1.1/(title|description|date))$>, 'expected predicate' ); ++$count; } is( $count, 6, 'describe resource expected graph size' ); } } done_testing; RDF-Query-2.910/t/rdql.t000644 000765 000024 00000000744 12054225575 014740 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use Test::More tests => 2; use RDF::Query; my $got = RDF::Query->new(<<'GO', { lang => 'rdql' }) or diag(RDF::Query->error); SELECT ?s WHERE ( ?s ?p ?o ) GO my $expected = RDF::Query->new(<<'GO') or diag(RDF::Query->error); PREFIX rdf: SELECT ?s WHERE { ?s ?p ?o } GO isa_ok($got => 'RDF::Query') or BAIL_OUT; is($got->as_sparql, $expected->as_sparql); RDF-Query-2.910/t/resultforms.t000644 000765 000024 00000015023 11707542736 016364 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 26); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my %seen; { print "# foaf:Person ORDER BY name with LIMIT\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT DISTINCT ?p ?name WHERE { ?p a foaf:Person; foaf:name ?name . FILTER(lang(?name) = "") } ORDER BY ?name LIMIT 2 END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my ($count, $last); while (my $row = $stream->next) { my ($p, $node) = @{ $row }{qw(p name)}; my $name = $node->literal_value; $seen{ $name }++; if (defined($last)) { cmp_ok( $name, 'ge', $last, "In order: $name (" . $p->as_string . ")" ); } else { ok( $name, "First: $name (" . $p->as_string . ")" ); } $last = $name; } continue { ++$count }; is( $count, 2, 'good LIMIT' ); } { print "# foaf:Person ORDER BY name with LIMIT and OFFSET\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT DISTINCT ?p ?name WHERE { ?p a foaf:Person; foaf:name ?name . FILTER(lang(?name) = "") } ORDER BY ?name LIMIT 2 OFFSET 2 END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my ($count, $last); while (my $row = $stream->next) { my ($p, $node) = @{ $row }{qw(p name)}; my $name = $node->literal_value; is( exists($seen{ $name }), '', "not seen before with offset: $name" ); if (defined($last)) { cmp_ok( $name, 'ge', $last, "In order: $name (" . $p->as_string . ")" ); } else { ok( $name, "First: $name (" . $p->as_string . ")" ); } $last = $name; } continue { ++$count }; is( $count, 1, 'good LIMIT' ); } } { print "# foaf:Person with LIMIT\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT ?p ?name WHERE { ?p a foaf:Person; foaf:name ?name } LIMIT 2 END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my ($count); while (my $row = $stream->next) { my ($p, $node) = @{ $row }{qw(p name)}; my $name = $node->literal_value; ok( $name, "First: $name (" . $p->as_string . ")" ); } continue { ++$count }; is( $count, 2, 'good LIMIT' ); } { print "# foaf:Person with ORDER BY and OFFSET\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: SELECT DISTINCT ?p ?name WHERE { ?p a foaf:Person; foaf:nick ?nick; foaf:name ?name } ORDER BY ?name OFFSET 1 END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my ($count); while (my $row = $stream->next) { my ($p, $node) = @{ $row }{qw(p name)}; my $name = $node->literal_value; ok( $name, "Got person with nick: $name (" . $p->as_string . ")" ); } continue { ++$count }; is( $count, 1, "Good DISTINCT with OFFSET" ); } { print "# foaf:Image with OFFSET [2]\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX exif: PREFIX dc: SELECT DISTINCT ?name ?camera WHERE { ?img a foaf:Image . ?img dc:creator ?name . ?img exif:model ?camera } OFFSET 1 END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my ($count); while (my $row = $stream->next) { my ($n, $c) = @{ $row }{qw(name camera)}; my $name = $n->literal_value; ok( $name, "Got image creator: $name" ); } continue { ++$count }; is( $count, 1, "Good DISTINCT with LIMIT" ); } { print "# foaf:Image with ORDER BY ASC(expression) [1]\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX exif: PREFIX dc: PREFIX xsd: SELECT DISTINCT ?img ?long WHERE { ?img a foaf:Image . ?img dcterms:spatial ?point . ?point geo:long ?long . } ORDER BY ASC(xsd:float(xsd:decimal(?long) * -1)) END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my $min; while (my $row = $stream->next) { my ($i, $l) = @{ $row }{qw(img long)}; my $image = $i->uri_value; my $long = $l->literal_value; if (defined($min)) { cmp_ok( $long, '<=', $min, "decreasing longitude $long on $image" ); if ($long <= $min) { $min = $long; } } else { is( $image, 'http://kasei.us/pictures/2004/20040909-Ireland/images/DSC_5705.jpg' ); $min = $long; } } continue { last if ++$count == 2 }; is( $count, 2, "Good ORDER BY ASC(expression) [1]" ); } { print "# foaf:Image with ORDER BY DESC(expression) [2]\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX dcterms: PREFIX geo: PREFIX exif: PREFIX dc: PREFIX xsd: SELECT DISTINCT ?img ?long WHERE { ?img a foaf:Image . ?img dcterms:spatial ?point . ?point geo:long ?long . } ORDER BY DESC(xsd:float(xsd:decimal(?long) * -1)) END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my $max; while (my $row = $stream->next) { my ($i, $l) = @{ $row }{qw(img long)}; my $image = $i->uri_value; my $long = $l->literal_value; if (defined($max)) { cmp_ok( $long, '>=', $max, "increasing longitude $long on $image" ); if ($long >= $max) { $max = $long; } } else { cmp_ok( $long, '==', -71.3924 ); $max = $long; } } continue { last if ++$count == 2 }; is( $count, 2, "Good ORDER BY DESC(expression) [2]" ); } } RDF-Query-2.910/t/serialize.t000644 000765 000024 00000030153 12054225575 015762 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t); BEGIN { require "models.pl"; } use Test::Exception; use Test::More tests => 34; use_ok( 'RDF::Query' ); ################################################################################ ### AS_SPARQL TESTS { my $sparql = <<"END"; PREFIX ex: SELECT ?x (SUM(?o) AS ?sum) WHERE { ?x ex:price ?o } GROUP BY ?x HAVING (SUM(?o) > 5) ORDER BY ?sum END my $query = new RDF::Query ( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, "PREFIX ex: SELECT ?x (SUM(?o) AS ?sum) WHERE { ?x ex:price ?o . } GROUP BY ?x HAVING (SUM(?o) > 5) ORDER BY ?sum", 'sparql to sparql aggregate' ); } { my $rdql = qq{SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR }; my $query = new RDF::Query ( $rdql, undef, undef, 'rdql' ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, 'PREFIX foaf: PREFIX rdf: SELECT * WHERE { ?person foaf:name "Gregory Todd Williams" . }', 'rdql to sparql' ); } { my $sparql = "PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY ?name"; my $query = new RDF::Query ( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, "PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person . ?person foaf:name ?name . } ORDER BY ?name", 'sparql to sparql' ); } { my $sparql = 'PREFIX foaf: SELECT ?p WHERE { ?p a foaf:Person ; foaf:homepage ?homepage . FILTER( REGEX( STR(?homepage), "^http://www.rpi.edu/.+") ) }'; my $query = new RDF::Query ( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, 'PREFIX foaf: SELECT ?p WHERE { ?p a foaf:Person . ?p foaf:homepage ?homepage . FILTER( REGEX(STR(?homepage), "^http://www.rpi.edu/.+") ) . }', 'sparql to sparql with regex filter' ); }; { my $sparql = "PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name . FILTER( ?name < 'Greg' ) }"; my $query = new RDF::Query ( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, 'PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person . ?person foaf:name ?name . FILTER( (?name < "Greg") ) . }', 'sparql to sparql with less-than filter' ); } { my $sparql = "PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person; foaf:name ?name } ORDER BY ?name LIMIT 5 OFFSET 5"; my $query = new RDF::Query ( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, "PREFIX foaf: SELECT ?name WHERE { ?person a foaf:Person . ?person foaf:name ?name . } ORDER BY ?name OFFSET 5 LIMIT 5", 'sparql to sparql with slice' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR END my $sparql = $query->as_sparql; my $query2 = RDF::Query->new( $sparql ); my $again = $query2->as_sparql; is( $sparql, $again, 'as_sparql: rdql round trip: select' ); } { my $rquery = new RDF::Query ( <<"END", undef, undef, 'rdql' ); SELECT ?person WHERE (?person foaf:name "Gregory Todd Williams") USING foaf FOR END my $squery = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX rdf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams" } END is( $squery->as_sparql, $rquery->as_sparql, 'as_sparql: rdql-sparql equality' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: CONSTRUCT { ?p foaf:name ?name } WHERE { ?p foaf:firstname ?name } END my $sparql = $query->as_sparql; my $again = RDF::Query->new( $sparql )->as_sparql; is( $sparql, $again, 'as_sparql: sparql round trip: construct' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: DESCRIBE ?p WHERE { ?p foaf:name ?name } END my $sparql = $query->as_sparql; my $again = RDF::Query->new( $sparql )->as_sparql; is( $sparql, $again, 'as_sparql: sparql round trip: describe' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: ASK WHERE { [ foaf:name "Gregory Todd Williams" ] } END my $sparql = $query->as_sparql; my $again = RDF::Query->new( $sparql )->as_sparql; is( $sparql, $again, 'as_sparql: sparql round trip: ask' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?name FROM NAMED WHERE { GRAPH ?g { [ foaf:name "Gregory Todd Williams" ] } } END my $sparql = $query->as_sparql; my $again = RDF::Query->new( $sparql )->as_sparql; is( $sparql, $again, 'as_sparql: sparql round trip: select with named graph' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT * WHERE { { ?person foaf:name ?name } UNION { ?person foaf:nick ?name } } END my $sparql = $query->as_sparql; my $query2 = RDF::Query->new( $sparql ); my $again = $query2->as_sparql; is( $sparql, $again, 'as_sparql: union' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name ?name . FILTER( !BOUND(?name) ) } END my $sparql = $query->as_sparql; my $again = RDF::Query->new( $sparql )->as_sparql; is( $sparql, $again, 'as_sparql: select with filter !BOUND' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT DISTINCT ?name WHERE { ?person foaf:name ?name . } END my $sparql = $query->as_sparql; my $qagain = RDF::Query->new( $sparql ); my $again = $qagain->as_sparql; is( $sparql, $again, 'as_sparql: select DISTINCT' ); } { my $sparql = <<"END"; PREFIX bench: PREFIX rdf: SELECT ?article WHERE { ?article a bench:Article . ?article ?property ?value . FILTER( (?property = ) ) . } END chomp($sparql); $sparql =~ s/\s+/ /gms; my $query = RDF::Query->new( $sparql ); my $string = $query->as_sparql; $string =~ s/\s+/ /gms; is( $string, $sparql, 'sparql to sparql with filter equality test' ); } ################################################################################ ### SSE TESTS { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams" } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?person) (BGP (triple ?person foaf:name "Gregory Todd Williams"))))', 'sse: select' ); # my $alg = RDF::Query::Algebra->from_sse( my $string = $sse ); # is( _CLEAN_WS($alg->sse), _CLEAN_WS($sse), 'sse: re-serialization of expression' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?name FROM NAMED WHERE { GRAPH ?g { [ foaf:name "Gregory Todd Williams" ] } } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?name) (namedgraph ?g (BGP (triple _:a1 foaf:name "Gregory Todd Williams")))))', 'sse: select with named graph' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: PREFIX dc: SELECT ?name WHERE { { [ foaf:name ?name ] } UNION { [ dc:title ?name ] } } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((dc: ) (foaf: )) (project (?name) (union (BGP (triple _:a1 foaf:name ?name)) (BGP (triple _:a2 dc:title ?name)))))', 'sse: select with union' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name ?name . FILTER( ?name < "Greg" ) } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?person) (filter (< ?name "Greg") (BGP (triple ?person foaf:name ?name)))))', 'sse: select with filter <' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name ?name . FILTER( !BOUND(?name) ) } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?person) (filter (! (bound ?name)) (BGP (triple ?person foaf:name ?name)))))', 'sse: select with filter !BOUND' ); } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: SELECT ?person WHERE { ?person foaf:name ?name . FILTER( REGEX(?name, "Greg") ) } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?person) (filter (regex ?name "Greg") (BGP (triple ?person foaf:name ?name)))))', 'sse: select with filter regex' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT * WHERE { { ?person foaf:name ?name } UNION { ?person foaf:nick ?name } } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(prefix ((foaf: )) (project (?person ?name) (union (BGP (triple ?person foaf:name ?name)) (BGP (triple ?person foaf:nick ?name)))))', 'sse: select with filter regex' ); } { my $query = new RDF::Query ( <<"END" ); BASE PREFIX foaf: SELECT ?person WHERE { ?person foaf:name "Gregory Todd Williams" } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(base (prefix ((foaf: )) (project (?person) (BGP (triple ?person foaf:name "Gregory Todd Williams")))))', 'sse: select' ); } { my $query = new RDF::Query ( <<"END", { update => 1 } ); LOAD INTO GRAPH END my $sse = $query->sse; is( _CLEAN_WS($sse), '(load )', 'sse: load' ); } { my $query = new RDF::Query ( <<"END", { update => 1 } ); LOAD ; SELECT * WHERE { ?s ?p ?o } END my $sse = $query->sse; is( _CLEAN_WS($sse), '(sequence (load ) (project (?s ?p ?o) (BGP (triple ?s ?p ?o))))', 'sse: sequence' ); } { my $sse = '(triple _:a foaf:name "foo\\\\\\tbar\\nbaz"^^)'; my $ctx = { namespaces => { foaf => 'http://xmlns.com/foaf/0.1/' } }; my $st = RDF::Query::Algebra::Triple->from_sse( my $string = $sse, $ctx ); is( $st->sse( $ctx ), $sse, 'sse: parse triple' ); } { my $sse = '(BGP (triple _:a foaf:name "foo\\\\\\tbar\\nbaz"^^))'; my $ctx = { namespaces => { foaf => 'http://xmlns.com/foaf/0.1/' } }; my $bgp = RDF::Query::Algebra->from_sse( my $string = $sse, $ctx ); isa_ok( $bgp, 'RDF::Query::Algebra::BasicGraphPattern' ); is( _CLEAN_WS($bgp->sse( $ctx )), $sse, 'sse: parse BGP' ); } ################################################################################ ### VARIABLEBINDINGS TESTS { my $a = RDF::Query::Node::Literal->new('a'); my $b = RDF::Query::Node::Resource->new('http://b/'); my $c = RDF::Query::Node::Blank->new('c'); { my $binding = RDF::Query::VariableBindings->new({ 'a' => $a }); is( "$binding", '{ a="a" }', 'variable binding (literal)' ); } { my $binding = RDF::Query::VariableBindings->new({ 'b' => $b }); is( "$binding", '{ b= }', 'variable binding (resource)' ); } { my $binding = RDF::Query::VariableBindings->new({ 'c' => $c }); is( "$binding", '{ c=(c) }', 'variable binding (blank)' ); } { my $binding = RDF::Query::VariableBindings->new({ 'a' => $a, b => undef, c => $c }); is( "$binding", '{ a="a", b=(), c=(c) }', 'variable binding (literal, blank, (undef))' ); } } sub _CLEAN_WS { my $string = shift; for ($string) { s/\s+/ /g; 1 while s/[)]\s+[)]/))/g; } return $string; } __END__ RDF-Query-2.910/t/sha1.js000644 000765 000024 00000006454 11707542736 015014 0ustar00gregstaff000000 000000 function META () { return { name: "sha1Hash", description: "SHA-1 Hash" }; } function str (x) { warn(x); if (x instanceof Object) { warn('-> Node'); if (x.is_literal()) { warn('-> literal'); warn(x.literal_value); return x.literal_value; } else if (x.is_resource()) { warn('-> resource'); return x.uri_value; } else { warn('-> blank'); return x.blank_identifier; } } else { warn('-> Non-Node'); return x; } } function sha1Hash(msg) { msg = str(msg); // constants [4.2.1] var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; // PREPROCESSING msg += String.fromCharCode(0x80); // add trailing '1' bit to string [5.1.1] // convert string msg into 512-bit/16-integer blocks arrays of ints [5.2.1] var l = Math.ceil(msg.length/4) + 2; // long enough to contain msg plus 2-word length var N = Math.ceil(l/16); // in N 16-int blocks var M = new Array(N); for (var i=0; i>> 32, but since JS converts // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]) M[N-1][15] = ((msg.length-1)*8) & 0xffffffff; // set initial hash value [5.3.1] var H0 = 0x67452301; var H1 = 0xefcdab89; var H2 = 0x98badcfe; var H3 = 0x10325476; var H4 = 0xc3d2e1f0; // HASH COMPUTATION [6.1.2] var W = new Array(80); var a, b, c, d, e; for (var i=0; i>>(32-n)); } // // extend Number class with a tailored hex-string method // (note toString(16) is implementation-dependant, and // in IE returns signed numbers when used on full words) // Number.prototype.toHexStr = function() { var s="", v; for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); } return s; } RDF-Query-2.910/t/sha1.js.asc000644 000765 000024 00000000272 11707542736 015551 0ustar00gregstaff000000 000000 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.4 (Darwin) iD8DBQBF9L56hPK6VMqoyC0RAqNbAJ4qE3dWRmRF6sCR1f6w+kIb16HMrQCePsKo 9zR3dkywIYnsF924FUYHPyY= =A83j -----END PGP SIGNATURE----- RDF-Query-2.910/t/sparql11-aggregates.t000644 000765 000024 00000033540 11760736733 017557 0ustar00gregstaff000000 000000 use strict; use warnings; no warnings 'redefine'; use Test::More; use Test::Exception; use Scalar::Util qw(blessed); use RDF::Trine qw(literal); use lib qw(. t); require "models.pl"; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.exists = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my @files = map { "data/$_" } qw(t-sparql11-aggregates-1.rdf foaf.xrdf about.xrdf); my @models = test_models( @files ); my $tests = (scalar(@models) * 91); plan tests => $tests; foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { print "# SELECT SUM aggregate with GROUP BY and HAVING\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT (SUM(?lprice) AS ?totalPrice) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?org HAVING (SUM(?lprice) > 10) END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { $count++; my $tp = $row->{totalPrice}; isa_ok( $tp, 'RDF::Trine::Node::Literal', 'got ?totalPrice value' ); is( $tp->literal_value, '21', 'expected literal value' ); is( $tp->literal_datatype, 'http://www.w3.org/2001/XMLSchema#integer', 'expected integer datatype' ); } is( $count, 1, 'expected result count with aggregation' ); } { print "# SELECT GROUPED Variable with GROUP BY and HAVING\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT ?org WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?org HAVING (SUM(?lprice) > 10) END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { $count++; my $org = $row->{org}; isa_ok( $org, 'RDF::Trine::Node::Resource', 'got ?org value' ); } is( $count, 1, 'expected result count with aggregation' ); } { print "# SELECT MIN with GROUP BY\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT ?auth (MIN(?lprice) AS ?min) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?auth END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my %expect = ( 'http://books.example/auth1' => 5, 'http://books.example/auth2' => 7, 'http://books.example/auth3' => 7, ); while (my $row = $stream->next) { $count++; my $auth = $row->{auth}; my $val = $row->{min}; isa_ok( $auth, 'RDF::Trine::Node::Resource', 'got ?auth value' ); isa_ok( $val, 'RDF::Trine::Node::Literal', 'got ?min value' ); my $expect = $expect{ $auth->uri_value }; cmp_ok( $val->literal_value, '==', $expect, 'expected MIN value' ); } is( $count, 3, 'expected result count with aggregation' ); } { print "# SELECT MAX with GROUP BY\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT ?auth (MAX(?lprice) AS ?max) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?auth END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my %expect = ( 'http://books.example/auth1' => 9, 'http://books.example/auth2' => 7, 'http://books.example/auth3' => 7, ); while (my $row = $stream->next) { $count++; my $auth = $row->{auth}; my $val = $row->{max}; isa_ok( $auth, 'RDF::Trine::Node::Resource', 'got ?auth value' ); isa_ok( $val, 'RDF::Trine::Node::Literal', 'got ?max value' ); my $expect = $expect{ $auth->uri_value }; cmp_ok( $val->literal_value, '==', $expect, 'expected MAX value' ); } is( $count, 3, 'expected result count with aggregation' ); } { print "# SELECT MAX with GROUP BY and ORDER BY DESC\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT ?auth (MAX(?lprice) AS ?max) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?auth ORDER BY DESC(?max) END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my @expect = (9, 7, 7); while (my $row = $stream->next) { $count++; my $val = $row->{max}; isa_ok( $val, 'RDF::Trine::Node::Literal', 'got ?max value' ); my $expect = shift(@expect); cmp_ok( $val->literal_value, '==', $expect, 'expected DESC MAX value' ); } is( $count, 3, 'expected result count with aggregation' ); } { print "# SELECT MAX with GROUP BY and ORDER BY ASC\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX : SELECT ?auth (MAX(?lprice) AS ?max) WHERE { ?org :affiliates ?auth . ?auth :writesBook ?book . ?book :price ?lprice . } GROUP BY ?auth ORDER BY ASC(?max) END warn RDF::Query->error unless ($query); my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; my @expect = (7, 7, 9); while (my $row = $stream->next) { $count++; my $val = $row->{max}; isa_ok( $val, 'RDF::Trine::Node::Literal', 'got ?max value' ); my $expect = shift(@expect); cmp_ok( $val->literal_value, '==', $expect, 'expected ASC MAX value' ); } is( $count, 3, 'expected result count with aggregation' ); } { print "# SELECT COUNT(VAR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX exif: PREFIX foaf: SELECT (COUNT(?aperture) AS ?count) WHERE { ?image a foaf:Image ; exif:fNumber ?aperture } END isa_ok( $query, 'RDF::Query' ) or warn RDF::Query->error; my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $expect = RDF::Query::VariableBindings->new({ count => literal('4', undef, 'http://www.w3.org/2001/XMLSchema#integer') }); is_deeply( $row, $expect, 'value for count apertures' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT COUNT(DISTINCT VAR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX exif: PREFIX foaf: SELECT (COUNT(DISTINCT ?aperture) AS ?count) WHERE { ?image a foaf:Image ; exif:fNumber ?aperture } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $expect = RDF::Query::VariableBindings->new({ count => literal('2', undef, 'http://www.w3.org/2001/XMLSchema#integer') }); is_deeply( $row, $expect, 'value for count distinct apertures' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT MIN(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT (MIN(?mbox) AS ?min) WHERE { [ a foaf:Person ; foaf:mbox_sha1sum ?mbox ] } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $expect = RDF::Query::VariableBindings->new({ min => literal('19fc9d0234848371668cf10a1b71ac9bd4236806') }); is_deeply( $row, $expect, 'value for min mbox_sha1sum' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT COUNT(VAR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT (COUNT(?nick) AS ?count) WHERE { ?p a foaf:Person . OPTIONAL { ?p foaf:nick ?nick } } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $expect = RDF::Query::VariableBindings->new({ count => RDF::Query::Node::Literal->new('3', undef, 'http://www.w3.org/2001/XMLSchema#integer') }); is_deeply( $row, $expect, 'COUNT() on sometimes unbound variable' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT COUNT(VAR) with GROUP BY\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT ?name (COUNT(?nick) AS ?count) WHERE { ?p a foaf:Person ; foaf:name ?name; foaf:nick ?nick . } GROUP BY ?name END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; my %expect = ( 'Gregory Todd Williams' => 2, 'Gary P' => 1 ); while (my $row = $stream->next) { my $name = $row->{name}->literal_value; my $expect = $expect{ $name }; cmp_ok( $row->{count}->literal_value, '==', $expect, 'expected COUNT() value for variable GROUP' ); $count++; } is( $count, 2, 'two aggreate groups' ); } { print "# SELECT AVG(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX exif: SELECT (AVG(?f) AS ?avg) WHERE { ?image exif:fNumber ?f } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $value = (values %$row)[0]; isa_ok( $value, 'RDF::Query::Node::Literal' ); ok( $value->is_numeric_type, 'avg produces a numeric type' ); is( $value->numeric_value, 6.125, 'expected average value' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT MIN(STR), MAX(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: PREFIX exif: SELECT (MIN(?e) AS ?min) (MAX(?e) AS ?max) WHERE { [] foaf:mbox_sha1sum ?e } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $min = $row->{min}; my $max = $row->{max}; isa_ok( $min, 'RDF::Query::Node::Literal' ); isa_ok( $max, 'RDF::Query::Node::Literal' ); is( $min->literal_value, '19fc9d0234848371668cf10a1b71ac9bd4236806', 'expected MIN plain-literal' ); is( $max->literal_value, 'f8677979059b73385c9d14cadf7d1e3652b205a8', 'expected MAX plain-literal' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT MIN(STR), MAX(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX dc: SELECT (MIN(?d) AS ?min) (MAX(?d) AS ?max) WHERE { [] dc:date ?d } END isa_ok( $query, 'RDF::Query' ); # without strict errors, non-datatyped dates (in human readable form) will # be string-compared with dateTime-datatyped W3CDTF literals my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $min = $row->{min}; my $max = $row->{max}; isa_ok( $min, 'RDF::Query::Node::Literal' ); isa_ok( $max, 'RDF::Query::Node::Literal' ); is( $min->literal_value, '2004-09-06T15:19:20+01:00', 'expected MIN plain-literal' ); is( $max->literal_value, 'Sat, 4 Oct 2003 20:02:22 PDT-0700', 'expected MAX plain-literal' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT GROUP_CONCAT(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX dc: SELECT (GROUP_CONCAT(?d) AS ?dates) WHERE { [] dc:date ?d } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $dates = $row->{dates}; isa_ok( $dates, 'RDF::Query::Node::Literal' ); my $lit = $dates->literal_value; like( $lit, qr/2004-09-06T15:19:20[+]01:00/, 'expected GROUP_CONCAT plain-literal' ); like( $lit, qr/2005-04-07T18:27:37-04:00/, 'expected GROUP_CONCAT plain-literal' ); like( $lit, qr/2005-04-07T18:27:50-04:00/, 'expected GROUP_CONCAT plain-literal' ); like( $lit, qr/2005-04-07T18:27:56-04:00/, 'expected GROUP_CONCAT plain-literal' ); like( $lit, qr/Sat, 4 Oct 2003 20:02:22 PDT-0700/, 'expected GROUP_CONCAT plain-literal' ); $count++; } is( $count, 1, 'one aggreate row' ); } { print "# SELECT SAMPLE(STR)\n"; my $query = new RDF::Query ( <<"END", undef, undef, 'sparql11' ); PREFIX dc: SELECT (SAMPLE(?d) AS ?date) WHERE { [] dc:date ?d } END isa_ok( $query, 'RDF::Query' ); my $stream = $query->execute( $model ); my $count = 0; while (my $row = $stream->next) { my $date = $row->{date}; isa_ok( $date, 'RDF::Query::Node::Literal' ); is( $date->literal_value, '2004-09-06T15:19:20+01:00', 'expected SAMPLE plain-literal' ); $count++; } is( $count, 1, 'one aggreate row' ); } } RDF-Query-2.910/t/sparql11-federation.t000644 000765 000024 00000005251 12054225575 017556 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use Test::More; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); my $tests = scalar(@models) * 10; plan tests => $tests; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.costmodel = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { print "# VALUES (one var)\n"; my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT ?p ?name WHERE { ?p a foaf:Person ; foaf:firstName ?name . } VALUES ?name { "Gregory" "Gary" } END my $count = 0; my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); while (my $d = $stream->next) { isa_ok( $d, 'HASH' ); if ($d->{p}->isa('RDF::Trine::Node::Resource')) { is( $d->{p}->uri_value, 'http://kasei.us/about/foaf.xrdf#greg', 'expected (URI) node' ); } elsif ($d->{p}->isa('RDF::Trine::Node::Blank')) { my $name = $d->{name}->literal_value; is( $name, 'Gary', 'expected (blank) node' ); } else { fail(); } $count++; } is( $count, 2, 'expected result count' ); } { print "# VALUES (two var)\n"; my $query = RDF::Query->new( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT ?p WHERE { ?p a foaf:Person ; foaf:name ?name ; foaf:mbox_sha1sum ?email . } VALUES (?name ?email) { ("Gregory Todd Williams" "2057969209f1dfdad832de387cf13e6ff8c93b12") } END my $count = 0; my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); while (my $d = $stream->next) { $count++; } is( $count, 0, 'expected result count' ); } { print "# VALUES with UNDEF\n"; my $query = RDF::Query->new( <<"END", undef, undef, 'sparql11' ); PREFIX foaf: SELECT * WHERE { ?p a foaf:Person ; foaf:name ?name ; foaf:schoolHomepage ?school . } VALUES (?name ?school) { (UNDEF ) } END my $count = 0; my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); while (my $d = $stream->next) { $count++; } is( $count, 4, 'expected result count' ); } } RDF-Query-2.910/t/sparql11-negation.t000644 000765 000024 00000003620 11707542736 017245 0ustar00gregstaff000000 000000 use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.filter = TRACE, Screen # log4perl.category.rdf.query.functions.exists = TRACE, Screen # log4perl.category.rdf.query.plan.basicgraphpattern = TRACE, Screen # log4perl.category.rdf.query.plan.triple = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); my $tests = (scalar(@models) * 11); plan tests => $tests; foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: PREFIX rdfs: SELECT * WHERE { ?p a foaf:Person ; foaf:name ?name . FILTER( NOT EXISTS { ?p foaf:mbox_sha1sum "f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8" . } ). } END my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { $count++; isa_ok( $row->{p}, 'RDF::Trine::Node', 'got person node' ); isa_ok( $row->{name}, 'RDF::Trine::Node::Literal', 'got person name' ); like( $row->{name}->literal_value, qr/^(Gary|Lauren|Liz)/, 'expected person name' ); } is( $count, 3, 'expected result count with negation' ); } } RDF-Query-2.910/t/sparql11-propery_paths.t000755 000765 000024 00000011761 12054225575 020343 0ustar00gregstaff000000 000000 use Test::More tests => 32; use strict; use warnings; # use lib qw(. t); # BEGIN { require "models.pl"; } # # my @files = map { "data/$_" } qw(foaf.xrdf); # my @models = test_models( @files ); use RDF::Query; use RDF::Query::Node qw(iri); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.path = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ { my $model = RDF::Trine::Model->temporary_model; RDF::Query->new(<<"END", { update => 1 })->execute($model); PREFIX rdf: INSERT DATA { . rdf:first 1 ; rdf:rest . rdf:first 2 ; rdf:rest . rdf:first 3 ; rdf:rest rdf:nil . } END RDF::Query->new(<<"END", { update => 1 })->execute($model); PREFIX foaf: INSERT DATA { foaf:name "Bob" . foaf:name "Alice" . foaf:knows , . } END { print "# /-path\n"; my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT ?name WHERE { ?p foaf:knows/foaf:name ?name } END my $count = 0; my $iter = $query->execute( $model ); isa_ok( $iter, 'RDF::Trine::Iterator' ); while (my $row = $iter->next) { isa_ok( $row->{name}, 'RDF::Query::Node::Literal' ); like($row->{name}->literal_value, qr/Alice|Bob/, 'expected person name'); $count++; } is( $count, 2, 'expected result count' ); } { print "# rdf:List +-path\n"; my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX rdf: PREFIX test: SELECT * WHERE { ?list . ?list rdf:rest+/rdf:first ?value . } END my $count = 0; my $iter = $query->execute( $model ); isa_ok( $iter, 'RDF::Trine::Iterator' ); my @got; while (my $row = $iter->next) { my $value = $row->{value}; isa_ok( $value, 'RDF::Query::Node::Literal' ); like($value->literal_value, qr/^[123]$/, 'expected list value'); $got[ $value->literal_value - 1 ] = $value->literal_value; $count++; } is_deeply( \@got, [undef, 2, 3], 'all expected values seen' ); } { print "# rdf:List *-path\n"; my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX rdf: PREFIX test: SELECT * WHERE { ?list . ?list rdf:rest*/rdf:first ?value . } END my $count = 0; my $iter = $query->execute( $model ); isa_ok( $iter, 'RDF::Trine::Iterator' ); my @got; while (my $row = $iter->next) { my $value = $row->{value}; isa_ok( $value, 'RDF::Query::Node::Literal' ); like($value->literal_value, qr/^[123]$/, 'expected list value'); $got[ $value->literal_value - 1 ] = $value->literal_value; $count++; } is_deeply( \@got, [1, 2, 3], 'all expected values seen' ); } } { print "# property path in GRAPH\n"; my $model = RDF::Trine::Model->temporary_model; my $insert = RDF::Query->new(<<"END", { update => 1 }); PREFIX foaf: INSERT DATA { GRAPH { foaf:knows , . foaf:name "Alice" . foaf:name "Bob" . } GRAPH { _:x foaf:knows . foaf:name "Eve" . } } END my ($p, $c) = $insert->prepare( $model ); $insert->execute_plan( $p, $c ); { my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT * WHERE { GRAPH { ?p foaf:knows/foaf:name ?name . } } END my ($p, $c) = $query->prepare( $model ); my $iter = $query->execute_plan( $p, $c ); isa_ok( $iter, 'RDF::Trine::Iterator' ); my @got; while (my $row = $iter->next) { like( $row->{name}, qr/Bob|Alice/, 'expected property path value restricted to graph' ); } is( $iter->seen_count, 2, 'expected result count' ); } { my $query = RDF::Query->new( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT * WHERE { GRAPH ?g { ?p foaf:knows/foaf:name ?name . } } END my ($p, $c) = $query->prepare( $model ); # warn $query->pattern->sse; # warn $p->sse; my $iter = $query->execute_plan( $p, $c ); isa_ok( $iter, 'RDF::Trine::Iterator' ); my @got; my %expect = ( 'g1' => qr/Alice|Bob/, 'g2' => qr/Eve/, ); while (my $row = $iter->next) { my $g = $row->{g}->uri_value; like( $g, qr/^g[12]$/, 'expected graph binding' ); my $pat = $expect{ $g }; like( $row->{name}, $pat, 'expected property path value for graph ' . $g ); } is( $iter->seen_count, 3, 'expected result count' ); } } RDF-Query-2.910/t/sparql11-select_expressions.t000644 000765 000024 00000007036 11707542736 021367 0ustar00gregstaff000000 000000 use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.exists = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); my $tests = (scalar(@models) * 20); plan tests => $tests; foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX rdf: PREFIX xsd: SELECT ?list (xsd:integer(?value) AS ?v) WHERE { ?list rdf:first ?value ; rdf:rest rdf:nil . } END my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); my $count = 0; while (my $row = $stream->next) { $count++; isa_ok( $row->{list}, 'RDF::Trine::Node' ); } is( $count, 1, 'expected result count with select expression' ); } { my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: PREFIX xsd: SELECT (xsd:string(?homepage) AS ?page) WHERE { ?p a foaf:Person ; foaf:firstName ?name ; foaf:homepage ?homepage . } ORDER BY ASC(?name) END my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); my $count = 0; my @expect = qw(http://www.realify.com/~gary/ http://kasei.us/); while (my $row = $stream->next) { $count++; my $page = $row->{page}; is_deeply([$row->variables], [qw(page)], 'expected variable list after projection'); isa_ok( $page, 'RDF::Trine::Node::Literal', 'expected literal cast from a resource' ); is( $page->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', 'expected xsd:string datatype' ); is( $page->literal_value, shift(@expect), 'expected literal value in ASC order' ); } is( $count, 2, 'expected result count with select expression and ASC order' ); } { my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: PREFIX xsd: SELECT (xsd:string(?homepage) AS ?page) WHERE { ?p a foaf:Person ; foaf:firstName ?name ; foaf:homepage ?homepage . } ORDER BY DESC(?name) END my ($plan, $ctx) = $query->prepare( $model ); my $pattern = $query->pattern; my $stream = $query->execute_plan( $plan, $ctx ); my $count = 0; my @expect = qw(http://kasei.us/ http://www.realify.com/~gary/); while (my $row = $stream->next) { $count++; my $page = $row->{page}; is_deeply([$row->variables], [qw(page)], 'expected variable list after projection'); isa_ok( $page, 'RDF::Trine::Node::Literal', 'expected literal cast from a resource' ); is( $page->literal_datatype, 'http://www.w3.org/2001/XMLSchema#string', 'expected xsd:string datatype' ); is( $page->literal_value, shift(@expect), 'expected literal value in DESC order' ); } is( $count, 2, 'expected result count with select expression and DESC order' ); } } RDF-Query-2.910/t/sparql11-subselect.t000644 000765 000024 00000002105 11707542736 017427 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(foaf.xrdf); my @models = test_models( @files ); use Test::More; plan tests => (5 * scalar(@models)); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { print "# subselect\n"; my $query = new RDF::Query ( <<"END", { lang => 'sparql11' } ); PREFIX foaf: SELECT * WHERE { ?p foaf:schoolHomepage ?school { SELECT ?p WHERE { ?p a foaf:Person . } LIMIT 1 } } END isa_ok( $query, 'RDF::Query' ); warn RDF::Query->error unless ($query); my $iter = $query->execute( $model ); my @results = $iter->get_all; is( scalar(@results), 2, 'expected result count' ); isa_ok( $results[0], 'HASH' ); is_deeply( $results[0]{p}, $results[1]{p}, 'same value bound to ?p' ); isnt( $results[0]{school}->uri_value, $results[1]{school}->uri_value, 'different values bound to ?school' ); } } RDF-Query-2.910/t/sparql11-update.t000644 000765 000024 00000015245 12054225575 016724 0ustar00gregstaff000000 000000 use Test::More tests => 58; use strict; use warnings; use RDF::Query; use RDF::Trine; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan.update = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ { { my $sparql = "SELECT * WHERE { ?x a }"; my $update = RDF::Query->new($sparql, { update => 1 }); ok( not($update->specifies_update_dataset), 'query specifies_update_dataset() is false' ); } { my $sparql = "DELETE { GRAPH { ?x } } INSERT { GRAPH { ?x } } WHERE { ?x a }"; my $update = RDF::Query->new($sparql, { update => 1 }); ok( not($update->specifies_update_dataset), 'update specifies_update_dataset() is false' ); } { my $sparql = "DELETE { GRAPH { ?x } } INSERT { GRAPH { ?x } } USING WHERE { ?x a }"; my $update = RDF::Query->new($sparql, { update => 1 }); ok( $update->specifies_update_dataset, 'update specifies_update_dataset() is true' ); } } { print "# insert data\n"; my $model = RDF::Trine::Model->temporary_model; is( $model->size, 0, 'empty model' ); my $insert = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: INSERT DATA { a foaf:Person ; foaf:name "Greg" . } END isa_ok( $insert, 'RDF::Query' ); warn RDF::Query->error unless ($insert); ok( $insert->is_update, 'query is_update' ); $insert->execute( $model ); is( $model->size, 2, 'expected model size' ); { my $query = RDF::Query->new('SELECT * WHERE { ?s a ?type }'); my $iter = $query->execute( $model ); my @rows = $iter->get_all; is( scalar(@rows), 1, 'expected result count' ); isa_ok( $rows[0], 'RDF::Query::VariableBindings', 'expected variablebindings' ); isa_ok( $rows[0]->{'s'}, 'RDF::Query::Node::Resource', 'expected subject resource' ); isa_ok( $rows[0]->{'type'}, 'RDF::Query::Node::Resource', 'expected object resource' ); is( $rows[0]->{'s'}->uri_value, 'greg', 'expected subject URI' ); is( $rows[0]->{'type'}->uri_value, 'http://xmlns.com/foaf/0.1/Person', 'expected object Class' ); } { my $query = RDF::Query->new('SELECT * WHERE { ?p ?o }'); my $iter = $query->execute( $model ); my @rows = $iter->get_all; is( scalar(@rows), 2, 'expected result count' ); foreach my $row (@rows) { isa_ok( $row, 'RDF::Query::VariableBindings', 'expected variablebindings' ); isa_ok( $row->{'p'}, 'RDF::Query::Node::Resource', 'expected subject resource' ); if ($row->{'p'}->uri_value eq 'http://xmlns.com/foaf/0.1/name') { isa_ok( $row->{'o'}, 'RDF::Query::Node::Literal', 'expected object literal' ); is( $row->{'o'}->literal_value, 'Greg' ); } else { isa_ok( $row->{'o'}, 'RDF::Query::Node::Resource', 'expected object resource' ); is( $row->{'o'}->uri_value, 'http://xmlns.com/foaf/0.1/Person' ); } } } } { print "# delete-insert update\n"; my $model = RDF::Trine::Model->temporary_model; is( $model->size, 0, 'empty model' ); my $insert = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: INSERT DATA { a foaf:Person ; foaf:firstName "Bill" . a foaf:Person ; foaf:firstName "Bill" . } END isa_ok( $insert, 'RDF::Query' ); warn RDF::Query->error unless ($insert); $insert->execute( $model ); is( $model->size, 4, 'expected model size' ); my $update = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: DELETE { ?person foaf:firstName 'Bill' } INSERT { ?person foaf:firstName 'William' } WHERE { ?person a foaf:Person ; foaf:firstName 'Bill' } END $update->execute( $model ); is( $model->size, 4, 'expected model size' ); my $query = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: SELECT * WHERE { ?person foaf:firstName ?name . } END my $iter = $query->execute( $model ); my %expect = map { $_ => 1 } qw(william bill); while (my $row = $iter->next) { isa_ok( $row, 'RDF::Query::VariableBindings' ); isa_ok( $row->{'person'}, 'RDF::Query::Node::Resource' ); isa_ok( $row->{'name'}, 'RDF::Query::Node::Literal' ); is( $row->{'name'}->literal_value, 'William' ); delete $expect{ $row->{person}->uri_value }; } my @keys = keys %expect; is_deeply( \@keys, [], 'seen 2 expected values' ); } { print "# delete-insert update with WITH\n"; my $model = RDF::Trine::Model->temporary_model; is( $model->size, 0, 'empty model' ); { my $insert = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: INSERT DATA { GRAPH { a foaf:Person ; foaf:firstName "Bill" . a foaf:Person ; foaf:firstName "Bill" . } GRAPH { a foaf:Person ; foaf:firstName "Bill" . } } END isa_ok( $insert, 'RDF::Query' ); warn RDF::Query->error unless ($insert); my ($plan, $ctx) = $insert->prepare( $model ); $insert->execute_plan( $plan, $ctx ); is( $model->size, 6, 'expected model size' ); } { my $update = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: WITH DELETE { ?person foaf:firstName 'Bill' } INSERT { ?person foaf:firstName 'William' } WHERE { ?person a foaf:Person ; foaf:firstName 'Bill' } END my ($plan, $ctx) = $update->prepare( $model ); $update->execute_plan( $plan, $ctx ); is( $model->size, 6, 'expected model size' ); } { my $query = new RDF::Query ( <<"END", { lang => 'sparql11', update => 1 } ); PREFIX foaf: SELECT * WHERE { GRAPH ?g { ?person foaf:firstName ?name . } } END my $iter = $query->execute( $model ); my %expect = ( william => [qw(g1 Bill)], bill => [qw(g1 Bill)], billy => [qw(g2 William)], ); while (my $row = $iter->next) { isa_ok( $row, 'RDF::Query::VariableBindings' ); isa_ok( $row->{'person'}, 'RDF::Query::Node::Resource' ); isa_ok( $row->{'g'}, 'RDF::Query::Node::Resource' ); isa_ok( $row->{'name'}, 'RDF::Query::Node::Literal' ); my $data = delete $expect{ $row->{'person'}->uri_value }; my ($eg, $en) = @$data; is( $row->{'g'}->uri_value, $eg, 'expected graph name' ); is( $row->{'name'}->literal_value, $en, 'expected person name' ); } my @keys = keys %expect; is_deeply( \@keys, [], 'seen 3 expected values' ); } } RDF-Query-2.910/t/streams.t.deprecated000644 000765 000024 00000002675 11707542736 017565 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = 1 + (scalar(@models) * 6); plan tests => $tests; use_ok( 'RDF::Query' ); foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { [ a geo:Point; foaf:name ?name ] } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count; while (not $stream->finished) { my ($node) = $stream->binding_value( 0 ); my $name = $node->as_string; ok( $name, $name ); } continue { last if ++$count >= 100; $stream->next_result; }; } { my $query = new RDF::Query ( <<"END" ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { [ a geo:Point; foaf:name ?name ] } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator', 'stream' ); my $count; while (my $row = $stream->next) { my ($node) = $row->{name}; my $name = $node->as_string; ok( $name, $name ); } continue { last if ++$count >= 100 }; } } RDF-Query-2.910/t/syntactic_forms.t000644 000765 000024 00000006627 11707542736 017220 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use URI::file; use lib qw(. t); BEGIN { require "models.pl"; } my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); use Test::More; plan tests => 1 + (12 * scalar(@models)); use_ok( 'RDF::Query' ); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.algebra = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n"; # my $s = $model->as_stream; # while ($s and not $s->end) { # my $st = $s->current; # warn $st->as_string; # } continue { $s->next } # - Collections: (1 ?x 3) { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX rdf: SELECT ?x WHERE { ?a1 rdf:first "1"; rdf:rest ?a2 . ?a2 rdf:first ?x; rdf:rest ?a3 . ?a3 rdf:first "3"; rdf:rest rdf:nil . } END my ($x) = $query->get( $model ); ok( $x, 'got collection element' ); is( $x->literal_value, 2 ); } # - Collections: (1 ?x 3) { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); SELECT ?x WHERE { ("1" ?x "3") } END my ($x) = $query->get( $model ); ok( $x, 'got collection triples' ); is( $x->literal_value, 2 ); } # - Collections: ?s ?p (1 ?x 3) { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX test: SELECT ?x WHERE { test:mycollection ("1" ?x "3") . } END my ($x) = $query->get( $model ); ok( $x, 'got object collection triples' ); is( $x->literal_value, 2 ); } # - Object Lists: ?x foaf:nick "kasei", "kasei_" . { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { ?x foaf:nick "kasei", "The Samo Fool" . ?x foaf:name ?name } END my ($name) = $query->get( $model ); ok( $name, 'got name' ); is( $name->literal_value, 'Gregory Todd Williams', 'Gregory Todd Williams' ); } # - Blank Nodes: [ :p "v" ] and [] :p "v" . { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { [ a geo:Point; geo:lat "52.972770"; foaf:name ?name ] } END my ($name) = $query->get( $model ); ok( $name, 'got name' ); is( $name->literal_value, 'Cliffs of Moher, Ireland', 'Cliffs of Moher, Ireland' ); } # - 'a': ?x a :Class . [ a :myClass ] :p "v" . { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT ?name WHERE { [ a geo:Point; geo:lat "52.972770"; foaf:name ?name ] } END my ($name) = $query->get( $model ); ok( $name, 'got name' ); is( $name->literal_value, 'Cliffs of Moher, Ireland', 'Cliffs of Moher, Ireland' ); } } RDF-Query-2.910/t/union.t000644 000765 000024 00000004626 11707542736 015136 0ustar00gregstaff000000 000000 use strict; use warnings; no warnings 'redefine'; use Test::More; use lib qw(. t); require "models.pl"; use RDF::Query; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.algebra.union = TRACE, Screen # # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my @files = map { "data/$_" } qw(about.xrdf foaf.xrdf); my @models = test_models( @files ); my $tests = (scalar(@models) * 40); plan tests => $tests; foreach my $model (@models) { print "\n#################################\n"; print "### Using model: $model\n\n"; { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT ?thing ?name WHERE { { ?thing a foaf:Person; foaf:name ?name } UNION { ?thing a geo:Point; foaf:name ?name } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { $count++; my ($thing, $name) = @{ $row }{qw(thing name)}; like( $thing->as_string, qr/^[<(]/, 'union person|point' ); ok( $thing->isa('RDF::Trine::Node'), 'node: ' . $thing->as_string ); ok( $name->isa('RDF::Trine::Node::Literal'), 'name: ' . $name->as_string ); } is( $count, 6, 'expected result count' ); } { my $query = new RDF::Query ( <<"END", undef, undef, 'sparql' ); PREFIX foaf: PREFIX geo: SELECT DISTINCT ?thing ?name WHERE { { ?thing a foaf:Person; foaf:name ?name } UNION { ?thing a geo:Point; foaf:name ?name } } END my $stream = $query->execute( $model ); isa_ok( $stream, 'RDF::Trine::Iterator' ); my $count = 0; while (my $row = $stream->next) { $count++; my ($thing, $name) = @{ $row }{qw(thing name)}; like( $thing->as_string, qr/^[<(]/, 'union person|point' ); ok( $thing->isa('RDF::Trine::Node'), 'node: ' . $thing->as_string ); ok( $name->isa('RDF::Trine::Node::Literal'), 'name: ' . $name->as_string ); } is( $count, 6, 'expected distinct result count' ); } } RDF-Query-2.910/lib/RDF/000755 000765 000024 00000000000 12173312223 014507 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/000755 000765 000024 00000000000 12173312223 015614 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query.pm000644 000765 000024 00000115450 12173312155 016164 0ustar00gregstaff000000 000000 # RDF::Query # ----------------------------------------------------------------------------- =head1 NAME RDF::Query - A complete SPARQL 1.1 Query and Update implementation for use with RDF::Trine. =head1 VERSION This document describes RDF::Query version 2.910. =head1 SYNOPSIS # SPARQL SELECT Query my $query = RDF::Query->new( 'SELECT * WHERE ...' ); my $iterator = $query->execute( $model ); while (my $row = $iterator->next) { # $row is a HASHref containing variable name -> RDF Term bindings print $row->{ 'var' }->as_string; } # SPARQL CONSTRUCT/DESCRIBE Query my $query = RDF::Query->new( 'CONSTRUCT { ... } WHERE ...' ); my $iterator = $query->execute( $model ); while (my $st = $iterator->next) { # $st is a RDF::Trine::Statement object representing an RDF triple print $st->as_string; } # SPARQL ASK Query my $query = RDF::Query->new( 'ASK WHERE ...' ); my $iterator = $query->execute( $model ); my $bool = $iterator->get_boolean; if ($bool) { print "Yes!\n"; } # RDQL Query my $query = new RDF::Query ( $rdql, { lang => 'rdql' } ); my @rows = $query->execute( $model ); # in list context, returns all results =head1 DESCRIPTION RDF::Query allows SPARQL and RDQL queries to be run against an RDF model, returning rows of matching results. See L for more information on SPARQL. See L for more information on RDQL. =head1 CHANGES IN VERSION 2.900 The 2.9xx versions of RDF::Query introduce some significant changes that will lead to a stable 3.000 release supporting SPARQL 1.1. Version 2.902 introduces the SPARQL 1.1 features up to date with the SPARQL 1.1 working drafts as of its release date. Version 2.902 also is the first version to require use of RDF::Trine for the underlying RDF store. This change means that RDF::Core is no longer supported, and while Redland is still supported, its handling of "contexts" (named graphs) means that existing RDF triples stored in Redland without associated contexts will not be accessible from RDF::Query. See L for more information on supported backend stores. =head1 CHANGES IN VERSION 2.000 There are many changes in the code between the 1.x and 2.x releases. Most of these changes will only affect queries that should have raised errors in the first place (SPARQL parsing, queries that use undefined namespaces, etc.). Beyond these changes, however, there are some significant API changes that will affect all users: =over 4 =item Use of RDF::Trine objects All nodes and statements returned by RDF::Query are now RDF::Trine objects (more specifically, RDF::Trine::Node and RDF::Trine::Statement objects). This differes from RDF::Query 1.x where nodes and statements were of the same type as the underlying model (Redland nodes from a Redland model and RDF::Core nodes from an RDF::Core model). In the past, it was possible to execute a query and not know what type of nodes were going to be returned, leading to overly verbose code that required examining all nodes and statements with the bridge object. This new API brings consistency to both the execution model and client code, greatly simplifying interaction with query results. =item Binding Result Values Binding result values returned by calling C<< $iterator->next >> are now HASH references (instead of ARRAY references), keyed by variable name. Where prior code might use this code (modulo model definition and namespace declarations): my $sparql = 'SELECT ?name ?homepage WHERE { [ foaf:name ?name ; foaf:homepage ?homepage ] }'; my $query = RDF::Query->new( $sparql ); my $iterator = $query->execute( $model ); while (my $row = $iterator->()) { my ($name, $homepage) = @$row; # ... } New code using RDF::Query 2.000 and later should instead use: my $sparql = 'SELECT ?name ?homepage WHERE { [ foaf:name ?name ; foaf:homepage ?homepage ] }'; my $query = RDF::Query->new( $sparql ); my $iterator = $query->execute( $model ); while (my $row = $iterator->next) { my $name = $row->{ name }; my $homepage = $row->{ homepage }; # ... } (Also notice the new method calling syntax for retrieving rows.) =back =cut package RDF::Query; use strict; use warnings; no warnings 'redefine'; use Carp qw(carp croak confess); use Data::Dumper; use LWP::UserAgent; use I18N::LangTags; use List::Util qw(first); use Scalar::Util qw(blessed reftype looks_like_number); use DateTime::Format::W3CDTF; use Log::Log4perl qw(:easy); if (! Log::Log4perl::initialized()) { Log::Log4perl->easy_init($ERROR); } no warnings 'numeric'; use RDF::Trine 0.135; require RDF::Query::Functions; # (needs to happen at runtime because some of the functions rely on RDF::Query being fully loaded (to call add_hook(), for example)) # all the built-in functions including: # datatype casting, language ops, logical ops, # numeric ops, datetime ops, and node type testing # also, custom functions including: # jena:sha1sum, jena:now, jena:langeq, jena:listMember # ldodds:Distance, kasei:warn use RDF::Query::Expression; use RDF::Query::Algebra; use RDF::Query::Node qw(iri); use RDF::Query::Parser::RDQL; use RDF::Query::Parser::SPARQL; use RDF::Query::Parser::SPARQL11; use RDF::Query::Compiler::SQL; use RDF::Query::Error qw(:try); use RDF::Query::Plan; ###################################################################### our ($VERSION, $DEFAULT_PARSER); BEGIN { $VERSION = '2.910'; $DEFAULT_PARSER = 'sparql11'; } ###################################################################### =head1 METHODS =over 4 =item C<< new ( $query, \%options ) >> Returns a new RDF::Query object for the specified C<$query>. The query language defaults to SPARQL 1.1, but may be set specifically with the appropriate C<< %options >> value. Valid C<< %options >> are: * lang Specifies the query language. Acceptable values are 'sparql11', 'sparql', or 'rdql'. * base_uri Specifies the base URI used in parsing the query. * update A boolean value indicating whether update operations are allowed during query execution. * load_data A boolean value indicating whether URIs used in SPARQL FROM and FROM NAMED clauses should be dereferenced and the resulting RDF content used to construct the dataset against which the query is run. =cut sub new { my $class = shift; my $query = shift; my ($base_uri, $languri, $lang, %options); if (@_ and ref($_[0])) { %options = %{ shift() }; $lang = delete $options{ lang }; $base_uri = $options{ base_uri } || $options{ base } ; delete $options{ base_uri }; delete $options{ base }; } else { ($base_uri, $languri, $lang, %options) = @_; } $class->clear_error; my $l = Log::Log4perl->get_logger("rdf.query"); no warnings 'uninitialized'; my %names = ( rdql => 'RDF::Query::Parser::RDQL', sparql => 'RDF::Query::Parser::SPARQL', sparql11 => 'RDF::Query::Parser::SPARQL11', ); my %uris = ( 'http://jena.hpl.hp.com/2003/07/query/RDQL' => 'RDF::Query::Parser::RDQL', 'http://www.w3.org/TR/rdf-sparql-query/' => 'RDF::Query::Parser::SPARQL', 'http://www.w3.org/ns/sparql-service-description#SPARQL10Query' => 'RDF::Query::Parser::SPARQL', 'http://www.w3.org/ns/sparql-service-description#SPARQL11Query' => 'RDF::Query::Parser::SPARQL11', 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update' => 'RDF::Query::Parser::SPARQL11', ); if ($base_uri) { $base_uri = RDF::Query::Node::Resource->new( $base_uri ); } my %pargs; if ($options{canonicalize}) { $pargs{canonicalize} = 1; } my $update = ((delete $options{update}) ? 1 : 0); my $pclass = $names{ $lang } || $uris{ $languri } || $names{ $DEFAULT_PARSER }; my $parser = $pclass->new( %pargs ); my $parsed = $parser->parse( $query, $base_uri, $update ); my $self = $class->_new( base_uri => $base_uri, parser => $parser, parsed => $parsed, query_string => $query, update => $update, options => { %options }, ); if (exists $options{load_data}) { $self->{load_data} = delete $options{load_data}; } elsif ($pclass =~ /^RDF::Query::Parser::(RDQL|SPARQL)$/) { $self->{load_data} = 1; } else { $self->{load_data} = 0; } unless ($parsed->{'triples'}) { $class->set_error( $parser->error ); $l->debug($parser->error); return; } if (defined $options{defines}) { @{ $self->{options} }{ keys %{ $options{defines} } } = values %{ delete $options{defines} }; } if ($options{logger}) { $l->debug("got external logger"); $self->{logger} = delete $options{logger}; } if (my $opt = delete $options{optimize}) { $l->debug("got optimization flag: $opt"); $self->{optimize} = $opt; } else { $self->{optimize} = 0; } if (my $opt = delete $options{force_no_optimization}) { $l->debug("got force_no_optimization flag"); $self->{force_no_optimization} = 1; } if (my $time = delete $options{optimistic_threshold_time}) { $l->debug("got optimistic_threshold_time flag"); $self->{optimistic_threshold_time} = $time; } # add rdf as a default namespace to RDQL queries if ($pclass eq 'RDF::Query::Parser::RDQL') { $self->{parsed}{namespaces}{rdf} = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; } return $self; } sub _new { my $class = shift; my $self = bless( { @_ }, $class ); return $self; } =item C<< get ( $model ) >> Executes the query using the specified model, and returns the first matching row as a LIST of values. =cut sub get { my $self = shift; my $stream = $self->execute( @_ ); my $row = $stream->next; if (ref($row)) { return @{ $row }{ $self->variables }; } else { return undef; } } =item C<< prepare ( $model ) >> Prepares the query, constructing a query execution plan, and returns a list containing ($plan, $context). To execute the plan, call C<< execute_plan( $plan, $context ) >>. =cut sub prepare { my $self = shift; my $_model = shift; my %args = @_; my $l = Log::Log4perl->get_logger("rdf.query"); $self->{_query_cache} = {}; # a new scratch hash for each execution. my %bound; if ($args{ 'bind' }) { %bound = %{ $args{ 'bind' } }; } my $delegate; if (defined $args{ 'delegate' }) { $delegate = delete $args{ 'delegate' }; if ($delegate and not blessed($delegate)) { $delegate = $delegate->new(); } } my $errors = ($args{ 'strict_errors' }) ? 1 : 0; my $parsed = $self->{parsed}; my @vars = $self->variables( $parsed ); local($self->{model}) = $self->{model}; my $model = $self->{model} || $self->get_model( $_model, %args ); if ($model) { $self->model( $model ); $l->debug("got model $model"); } else { throw RDF::Query::Error::ModelError ( -text => "Could not create a model object." ); } if ($self->{load_data}) { $l->trace("loading data"); $self->load_data(); } $model = $self->model(); # reload the model object, because load_data might have changed it. my $dataset = ($model->isa('RDF::Trine::Model::Dataset')) ? $model : RDF::Trine::Model::Dataset->new($model); $l->trace("constructing ExecutionContext"); my $context = RDF::Query::ExecutionContext->new( bound => \%bound, model => $dataset, query => $self, base_uri => $parsed->{base_uri}, ns => $parsed->{namespaces}, logger => $self->logger, optimize => $self->{optimize}, force_no_optimization => $self->{force_no_optimization}, optimistic_threshold_time => $self->{optimistic_threshold_time} || 0, requested_variables => \@vars, strict_errors => $errors, options => $self->{options}, delegate => $delegate, ); $self->{model} = $model; $l->trace("getting QEP..."); my %plan_args = %{ $args{ planner_args } || {} }; my $plan = $self->query_plan( $context, %plan_args ); $l->trace("-> done."); unless ($plan) { throw RDF::Query::Error::CompilationError -text => "Query didn't produce a valid execution plan"; } return ($plan, $context); } =item C Executes the query using the specified RDF C<< $model >>. If called in a list context, returns an array of rows, otherwise returns an L object. The iterator returned may be an instance of several subclasses of L: * A L object is returned for query forms producing variable binding results (SELECT queries). * A L object is returned for query forms producing in an RDF graph result (DESCRIBE and CONSTRUCT queries). * A L object is returned for query forms producing a true/false result (ASK queries). =cut sub execute { my $self = shift; my $model = shift; my %args = @_; my $l = Log::Log4perl->get_logger("rdf.query"); $l->debug("executing query with model " . ($model or '')); my $lang_iri = ''; my $parser = $self->{parser}; my $name; if ($parser->isa('RDF::Query::Parser::SPARQL11')) { if ($self->is_update) { $name = 'SPARQL 1.1 Update'; $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update'; } else { $name = 'SPARQL 1.1 Query'; $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL11Query'; } } elsif ($parser->isa('RDF::Query::Parser::SPARQL')) { $name = 'SPARQL 1.0 Query'; $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL10Query'; } local($self->{model}) = $self->{model}; # warn "model: $self->{model}"; # warn "passthrough checking if model supports $lang_iri\n"; if ($self->{options}{allow_passthrough} and $model->supports($lang_iri)) { $l->info("delegating $name execution to the underlying model"); return $model->get_sparql( $self->{query_string} ); } else { my ($plan, $context) = $self->prepare( $model, %args ); if ($l->is_trace) { $l->trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); $l->trace($self->as_sparql); $l->trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } return $self->execute_plan( $plan, $context ); } } =item C<< execute_plan ( $plan, $context ) >> Executes the query plan generated by the C<> method using the supplied L> object. Return value(s) are the same as for the C<> method. =cut sub execute_plan { my $self = shift; my $plan = shift; my $context = shift; my $model = $context->model; my $parsed = $self->{parsed}; my @vars = $self->variables( $parsed ); my $l = Log::Log4perl->get_logger("rdf.query"); my $pattern = $self->pattern; # $l->trace("calling fixup()"); # my $cpattern = $self->fixup(); my @funcs = $pattern->referenced_functions; foreach my $f (@funcs) { $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/function_init', $f ); } # RUN THE QUERY! $l->debug("executing the graph pattern"); my $options = $parsed->{options} || {}; if ($self->{options}{plan}) { warn $plan->sse({}, ''); } $plan->execute( $context ); my $stream = $plan->as_iterator( $context ); if ($parsed->{'method'} eq 'DESCRIBE') { $stream = $self->describe( $stream, $context ); } elsif ($parsed->{'method'} eq 'ASK') { $stream = $self->ask( $stream, $context ); } $l->debug("going to call post-execute hook"); $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/post-execute', $model, $stream ); if (wantarray) { return $stream->get_all(); } else { return $stream; } } =item C<< prepare_with_named_graphs ( $model, @uris ) >> =cut sub prepare_with_named_graphs { my $self = shift; my $_model = shift; my @graphs = @_; my $l = Log::Log4perl->get_logger("rdf.query"); # $self->{model} = $model; my $model = $self->get_model( $_model ); if ($model) { $self->model( $model ); } else { throw RDF::Query::Error::ModelError ( -text => "Could not create a model object." ); } foreach my $gdata (@graphs) { my $url = (blessed($gdata)) ? $gdata->uri_value : $gdata; $l->debug("-> adding graph data $url"); $self->parse_url( $url, 1 ); } return $self->prepare( $model ); } =item C<< execute_with_named_graphs ( $model, @uris ) >> Executes the query using the specified RDF C<< $model >>, loading the contents of the specified C<@uris> into named graphs immediately prior to matching the query. Otherwise, acts just like C<< execute >>. =cut sub execute_with_named_graphs { my $self = shift; my $_model = shift; my @graphs; my @options; if (scalar(@_)) { if (not(blessed($_[0])) and reftype($_[0]) eq 'ARRAY') { @graphs = @{ shift(@_) }; @options = @_; } else { @graphs = @_; } } my ($plan, $ctx) = $self->prepare_with_named_graphs( $_model, @graphs ); return $self->execute_plan( $plan, $ctx ); } =begin private =item C<< query_plan ( $execution_context ) >> Returns a RDF::Query::Plan object that is (hopefully) the optimal QEP for the current query. =end private =cut sub query_plan { my $self = shift; my $context = shift; my %args = @_; my $parsed = $self->{parsed}; my $bound = $context->bound; my @bkeys = keys %{ $bound }; my $model = $context->model; if (not exists $self->{options}{'rdf.query.plan.delegate'} or $self->{options}{'rdf.query.plan.delegate'}) { my $delegate_key = $self->{update} ? 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update' : "http://www.w3.org/ns/sparql-service-description#SPARQL10Query"; # TODO: need to determine if the query is only 1.0, and if so, check for 1.0 support. otherwise check for 1.1 support if (scalar(@bkeys) == 0 and $model->supports($delegate_key)) { my $plan = RDF::Query::Plan::Iterator->new( sub { my $context = shift; my $model = $context->model; my $iter = $model->get_sparql( $self->{query_string} ); return $iter; } ); return $plan; } } my %constant_plan; if (my $b = $self->{parsed}{bindings}) { my $vars = $b->{vars}; my $values = $b->{terms}; my @names = map { $_->name } @{ $vars }; my @constants; while (my $values = shift(@{ $b->{terms} })) { my %bound; # @bound{ @names } = @{ $values }; foreach my $i (0 .. $#names) { my $k = $names[$i]; my $v = $values->[$i]; next unless defined($v); $bound{ $k } = $v; } my $bound = RDF::Query::VariableBindings->new( \%bound ); push(@constants, $bound); } my $constant_plan = RDF::Query::Plan::Constant->new( @constants ); %constant_plan = ( constants => [ $constant_plan ] ); } my $algebra = $self->pattern; my $pclass = $self->plan_class; my @plans = $pclass->generate_plans( $algebra, $context, %args, %constant_plan ); my $l = Log::Log4perl->get_logger("rdf.query.plan"); if (wantarray) { return @plans; } else { my ($plan) = @plans; # XXX need to figure out what's the 'best' plan here if ($l->is_debug) { $l->debug("using query plan: " . $plan->sse({}, '')); } return $plan; } } =begin private =item C<< plan_class >> Returns the class name for Plan generation. This method should be overloaded by RDF::Query subclasses if the implementation also provides a subclass of RDF::Query::Plan. =end private =cut sub plan_class { return 'RDF::Query::Plan'; } =begin private =item C<< describe ( $iter, $context ) >> Takes a stream of matching statements and constructs a DESCRIBE graph. =end private =cut sub describe { my $self = shift; my $stream = shift; my $context = shift; my $model = $context->model; my @nodes; my %seen; while (my $row = $stream->next) { foreach my $v (@{ $self->{parsed}{variables} }) { if ($v->isa('RDF::Query::Node::Variable')) { my $node = $row->{ $v->name }; my $string = blessed($node) ? $node->as_string : ''; push(@nodes, $node) unless ($seen{ $string }++); } elsif ($v->isa('RDF::Query::Node::Resource')) { my $string = blessed($v) ? $v->as_string : ''; push(@nodes, $v) unless ($seen{ $string }++); } } } my @streams; $self->{'describe_nodes'} = []; foreach my $node (@nodes) { push(@{ $self->{'describe_nodes'} }, $node); push(@streams, $model->bounded_description( $node )); } my $ret = sub { while (@streams) { my $val = $streams[0]->next; if (defined $val) { return $val; } else { shift(@streams); return undef if (not @streams); } } }; return RDF::Trine::Iterator::Graph->new( $ret ); } =begin private =item C Takes a stream of matching statements and returns a boolean query result stream. =end private =cut sub ask { my $self = shift; my $stream = shift; my $context = shift; my $value = $stream->next; my $bool = ($value) ? 1 : 0; return RDF::Trine::Iterator::Boolean->new( [ $bool ] ); } ###################################################################### =item C<< pattern >> Returns the RDF::Query::Algebra::GroupGraphPattern algebra pattern for this query. =cut sub pattern { my $self = shift; my $parsed = $self->parsed; my @triples = @{ $parsed->{triples} }; if (scalar(@triples) == 1 and ($triples[0]->isa('RDF::Query::Algebra::GroupGraphPattern') or $triples[0]->isa('RDF::Query::Algebra::Filter') or $triples[0]->isa('RDF::Query::Algebra::Sort') or $triples[0]->isa('RDF::Query::Algebra::Limit') or $triples[0]->isa('RDF::Query::Algebra::Offset') or $triples[0]->isa('RDF::Query::Algebra::Distinct') or $triples[0]->isa('RDF::Query::Algebra::Project') or $triples[0]->isa('RDF::Query::Algebra::Construct') or $triples[0]->isa('RDF::Query::Algebra::Load') or $triples[0]->isa('RDF::Query::Algebra::Clear') or $triples[0]->isa('RDF::Query::Algebra::Create') or $triples[0]->isa('RDF::Query::Algebra::Update') )) { my $ggp = $triples[0]; return $ggp; } else { return RDF::Query::Algebra::GroupGraphPattern->new( @triples ); } } =item C<< is_update >> =cut sub is_update { my $self = shift; my $pat = $self->pattern; return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Clear')); return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Copy')); return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Create')); return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Move')); return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Update')); return 0; } =item C<< as_sparql >> Returns the query as a string in the SPARQL syntax. =cut sub as_sparql { my $self = shift; my $parsed = $self->parsed || {}; my $context = { namespaces => { %{ $parsed->{namespaces} || {} } } }; my $method = $parsed->{method}; if ($method =~ /^(DESCRIBE|ASK)$/i) { $context->{force_ggp_braces} = 1; } my @vars = map { $_->as_sparql( $context, '' ) } @{ $parsed->{ variables } }; my $vars = join(' ', @vars); my $ggp = $self->pattern; if ($method =~ /^(LOAD|CLEAR|CREATE|UPDATE)$/) { return $ggp->as_sparql; } else { { my $pvars = join(' ', sort $ggp->referenced_variables); my $svars = join(' ', sort map { $_->isa('RDF::Query::Node::Resource') ? $_->as_string : $_->name } @{ $parsed->{ variables } }); if ($pvars eq $svars) { $vars = '*'; } } my @ns = map { "PREFIX " . ($_ eq '__DEFAULT__' ? '' : $_) . ": <$parsed->{namespaces}{$_}>" } (sort keys %{ $parsed->{namespaces} }); my @mod; if (my $ob = $parsed->{options}{orderby}) { push(@mod, 'ORDER BY ' . join(' ', map { my ($dir,$v) = @$_; ($dir eq 'ASC') ? $v->as_sparql( $context, '' ) : "${dir}" . $v->as_sparql( $context, '' ); } @$ob)); } if (my $l = $parsed->{options}{limit}) { push(@mod, "LIMIT $l"); } if (my $o = $parsed->{options}{offset}) { push(@mod, "OFFSET $o"); } my $mod = join("\n", @mod); my $methoddata = ''; if ($method eq 'SELECT') { $methoddata = $method; } elsif ($method eq 'ASK') { $methoddata = $method; } elsif ($method eq 'DESCRIBE') { $methoddata = sprintf("%s %s\nWHERE", $method, $vars); } my $ns = scalar(@ns) ? join("\n", @ns, '') : ''; my $sparql; if ($methoddata or $ns) { $sparql = sprintf( "$ns%s %s\n%s", $methoddata, $ggp->as_sparql( $context, '' ), $mod, ); } else { $sparql = sprintf( "%s\n%s", $ggp->as_sparql( $context, '' ), $mod, ); } chomp($sparql); return $sparql; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $pattern = $self->pattern; return $pattern->as_hash; } =item C<< sse >> Returns the query as a string in the SSE syntax. =cut sub sse { my $self = shift; my $parsed = $self->parsed; my $ggp = $self->pattern; my $ns = $parsed->{namespaces}; my $nscount = scalar(@{ [ keys %$ns ] }); my $base_uri = $parsed->{base}; my $indent = ' '; my $context = { namespaces => $ns, indent => $indent }; my $indentcount = 0; $indentcount++ if ($base_uri); $indentcount++ if ($nscount); my $prefix = $indent x $indentcount; my $sse = $ggp->sse( $context, $prefix ); if ($nscount) { $sse = sprintf("(prefix (%s)\n${prefix}%s)", join("\n${indent}" . ' 'x9, map { "(${_}: <$ns->{$_}>)" } (sort keys %$ns)), $sse); } if ($base_uri) { $sse = sprintf("(base <%s>\n${indent}%s)", $base_uri->uri_value, $sse); } chomp($sse); return $sse; } =item C<< dateparser >> Returns the DateTime::Format::W3CDTF object associated with this query object. =cut sub dateparser { my $self = shift; my $parser = ($self->{dateparser} ||= DateTime::Format::W3CDTF->new); return $parser; } =begin private =item C<< supports ( $model, $feature ) >> Returns a boolean value representing the support of $feature for the given model. =end private =cut sub supports { my $self = shift; my $obj = shift; my $model = $self->get_model( $obj ); return $model->supports( @_ ); } =item C<< specifies_update_dataset >> Returns true if the query specifies a custom update dataset via the WITH or USING keywords, false otherwise. =cut sub specifies_update_dataset { my $self = shift; no warnings 'uninitialized'; return $self->{parsed}{custom_update_dataset} ? 1 : 0; } =begin private =item C<< get_model ( $model ) >> Returns a model object for use during execution. If C<< $model >> is a usable model, it is simply returned. Otherwise, a temporary model is constructed and returned. =end private =cut sub get_model { my $self = shift; my $store = shift; my %args = @_; my $parsed = ref($self) ? $self->{parsed} : undef; my $model; if (not $store) { $model = RDF::Trine::Model->temporary_model; } elsif (($store->isa('RDF::Trine::Model'))) { $model = $store; } elsif ($store->isa('RDF::Redland::Model')) { my $s = RDF::Trine::Store->new_with_object( $store ); $model = RDF::Trine::Model->new( $s ); unless (blessed($model)) { Carp::cluck "Failed to construct an RDF::Trine model from $store"; return; } } elsif ($store->isa('RDF::Core::Model')) { Carp::croak "RDF::Core is no longer supported"; } else { Carp::confess "unknown store type: $store"; } return $model; } =begin private =item C<< load_data >> Loads any external data required by this query (FROM and FROM NAMED clauses). =end private =cut sub load_data { my $self = shift; my $parsed = $self->{parsed}; ## LOAD ANY EXTERNAL RDF FILES my $sources = $parsed->{'sources'}; if (ref($sources) and reftype($sources) eq 'ARRAY' and scalar(@$sources)) { my $model = RDF::Trine::Model->temporary_model; $self->model( $model ); foreach my $source (@$sources) { my $named_source = (2 == @{$source} and $source->[1] eq 'NAMED'); my $uri = $source->[0]->uri_value; $self->parse_url( $uri, $named_source ); } $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/post-create-model', $model ); } } =begin private =item C<< var_or_expr_value ( \%bound, $value, $context ) >> Returns an (non-variable) RDF::Query::Node value based on C<< $value >>. If C<< $value >> is a node object, it is simply returned. If it is an RDF::Query::Node::Variable object, the corresponding value in C<< \%bound >> is returned. If it is an RDF::Query::Expression object, the expression is evaluated using C<< \%bound >>, and the resulting value is returned. =end private =cut sub var_or_expr_value { my $self = shift; my $bound = shift; my $v = shift; my $ctx = shift; Carp::confess 'not an object value in var_or_expr_value: ' . Dumper($v) unless (blessed($v)); if ($v->isa('RDF::Query::Expression')) { return $v->evaluate( $self, $bound, $ctx ); } elsif ($v->isa('RDF::Trine::Node::Variable')) { return $bound->{ $v->name }; } elsif ($v->isa('RDF::Query::Node')) { return $v; } else { Carp::cluck "not an expression or node value in var_or_expr_value: " . Dumper($v, $bound); throw RDF::Query::Error -text => 'Not an expression or node value'; } } =item C Associates the custom function C<$function> (a CODE reference) with the specified URI, allowing the function to be called by query FILTERs. =cut sub add_function { my $self = shift; my $uri = shift; my $code = shift; if (ref($self)) { $self->{'functions'}{$uri} = $code; } else { our %functions; $RDF::Query::functions{ $uri } = $code; } } =item C<< supported_extensions >> Returns a list of URLs representing extensions to SPARQL that are supported by the query engine. =cut sub supported_extensions { my $self = shift; return qw( http://kasei.us/2008/04/sparql-extension/service http://kasei.us/2008/04/sparql-extension/service/bloom_filters http://kasei.us/2008/04/sparql-extension/unsaid http://kasei.us/2008/04/sparql-extension/federate_bindings http://kasei.us/2008/04/sparql-extension/select_expression http://kasei.us/2008/04/sparql-extension/aggregate http://kasei.us/2008/04/sparql-extension/aggregate/count http://kasei.us/2008/04/sparql-extension/aggregate/count-distinct http://kasei.us/2008/04/sparql-extension/aggregate/min http://kasei.us/2008/04/sparql-extension/aggregate/max ); } =item C<< supported_functions >> Returns a list URLs that may be used as functions in FILTER clauses (and the SELECT clause if the SPARQL 1.1 parser is used). =cut sub supported_functions { my $self = shift; my @funcs; if (blessed($self)) { push(@funcs, keys %{ $self->{'functions'} }); } push(@funcs, keys %RDF::Query::functions); return grep { not(/^sparql:/) } @funcs; } =begin private =item C If C<$uri> is associated with a query function, returns a CODE reference to the function. Otherwise returns C. =end private =cut sub get_function { my $self = shift; my $uri = shift; my %args = @_; my $l = Log::Log4perl->get_logger("rdf.query"); if (blessed($uri) and $uri->isa('RDF::Query::Node::Resource')) { $uri = $uri->uri_value; } $l->debug("trying to get function from $uri"); if (blessed($uri) and $uri->isa('RDF::Query::Node::Resource')) { $uri = $uri->uri_value; } my $func; if (ref($self)) { $func = $self->{'functions'}{$uri} || $RDF::Query::functions{ $uri }; } else { $func = $RDF::Query::functions{ $uri }; } if ($func) { return $func; } return; } =begin private =item C<< call_function ( $model, $bound, $uri, @args ) >> If C<$uri> is associated with a query function, calls the function with the supplied arguments. =end private =cut sub call_function { my $self = shift; my $model = shift; my $bound = shift; my $uri = shift; my $l = Log::Log4perl->get_logger("rdf.query"); $l->debug("trying to get function from $uri"); my $filter = RDF::Query::Expression::Function->new( $uri, @_ ); return $filter->evaluate( $self, $bound ); } =item C<< add_computed_statement_generator ( $predicate => \&generator ) >> Adds a statement generator for the given C<< $predicate >> to the query object. This statement generator will be called as C<< $generator->( $query, $model, \%bound, $s, $p, $o, $c ) >> and is expected to return an RDF::Trine::Iterator::Graph object containing statements with C<< $predicate >>. =cut sub add_computed_statement_generator { my $self = shift; if (scalar(@_) == 1) { throw RDF::Query::Error::MethodInvocationError -text => 'RDF::Query::add_computed_statement_generator must now take two arguments: ( $predicate, \&generator ).'; } my $pred = shift; my $gen = shift; if (blessed($pred)) { if ($pred->can('uri_value')) { $pred = $pred->uri_value; } else { $pred = "$pred"; } } push( @{ $self->{'computed_statement_generators'}{ $pred } }, $gen ); } =item C<< get_computed_statement_generators ( [ $predicate ] ) >> Returns an ARRAY reference of computed statement generator closures. =cut sub get_computed_statement_generators { my $self = shift; if (@_) { my $pred = shift; if (blessed($pred)) { if ($pred->can('uri_value')) { $pred = $pred->uri_value; } else { $pred = "$pred"; } } return $self->{'computed_statement_generators'}{ $pred } || []; } else { return $self->{'computed_statement_generators'} || {}; } } =item C<< add_hook_once ( $hook_uri, $function, $token ) >> Calls C<< add_hook >> adding the supplied C<< $function >> only once based on the C<< $token >> identifier. This may be useful if the only code that is able to add a hook is called many times (in an extension function, for example). =cut sub add_hook_once { my $self = shift; my $uri = shift; my $code = shift; my $token = shift; unless ($self->{'hooks_once'}{ $token }++) { $self->add_hook( $uri, $code ); } } =item C<< add_hook ( $hook_uri, $function ) >> Associates the custom function C<$function> (a CODE reference) with the RDF::Query code hook specified by C<$uri>. Each function that has been associated with a particular hook will be called (in the order they were registered as hooks) when the hook event occurs. See L for more information. =cut sub add_hook { my $self = shift; my $uri = shift; my $code = shift; if (ref($self)) { push(@{ $self->{'hooks'}{$uri} }, $code); } else { our %hooks; push(@{ $RDF::Query::hooks{ $uri } }, $code); } } =begin private =item C If C<$uri> is associated with any query callback functions ("hooks"), returns an ARRAY reference to the functions. If no hooks are associated with C<$uri>, returns a reference to an empty array. =end private =cut sub get_hooks { my $self = shift; my $uri = shift; my $func = $self->{'hooks'}{ $uri } || $RDF::Query::hooks{ $uri } || []; return $func; } =begin private =item C Calls any query callback functions associated with C<$uri>. Each callback is called with the query object as the first argument, followed by any caller-supplied arguments from C<@args>. =end private =cut sub run_hook { my $self = shift; my $uri = shift; my @args = @_; my $hooks = $self->get_hooks( $uri ); foreach my $hook (@$hooks) { $hook->( $self, @args ); } } =begin private =item C<< parse_url ( $url, $named ) >> Retrieve a remote file by URL, and parse RDF into the RDF store. If $named is TRUE, associate all parsed triples with a named graph. =end private =cut sub parse_url { my $self = shift; my $url = shift; my $named = shift; my $model = $self->model; if ($named) { RDF::Trine::Parser->parse_url_into_model( $url, $model, context => iri($url) ); } else { RDF::Trine::Parser->parse_url_into_model( $url, $model ); } } =begin private =item C Returns a list of the ordered variables the query is selecting. =end private =cut sub variables { my $self = shift; my $parsed = shift || $self->parsed; my @vars = map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') or $_->isa('RDF::Query::Expression::Alias') } @{ $parsed->{'variables'} }; return @vars; } =item C Returns the parse tree. =cut sub parsed { my $self = shift; if (@_) { $self->{parsed} = shift; } return $self->{parsed}; } =item C<< model >> Returns the RDF::Trine::Model object for this query. =cut sub model { my $self = shift; if (@_) { $self->{model} = shift; } my $model = $self->{model}; unless (defined $model) { Carp::confess "query->model shouldn't be calling get_model"; $model = $self->get_model(); } return $model; } =item C<< useragent >> Returns the LWP::UserAgent object used for retrieving web content. =cut sub useragent { my $self = shift; if (my $ua = $self->{useragent}) { return $ua; } else { my $ua = LWP::UserAgent->new( agent => "RDF::Query/${VERSION}" ); $ua->default_headers->push_header( 'Accept' => "application/sparql-results+xml;q=0.9,application/rdf+xml;q=0.5,text/turtle;q=0.7,text/xml" ); $self->{useragent} = $ua; return $ua; } } =item C<< log ( $key [, $value ] ) >> If no logger object is associated with this query object, does nothing. Otherwise, return or set the corresponding value depending on whether a C<< $value >> is specified. =cut sub log { my $self = shift; if (blessed(my $l = $self->{ logger })) { $l->log( @_ ); } } =item C<< logger >> Returns the logger object associated with this query object (if present). =cut sub logger { my $self = shift; return $self->{ logger }; } =item C Returns the last error the parser experienced. =cut sub error { my $self = shift; if (blessed($self)) { return $self->{error}; } else { our $_ERROR; return $_ERROR; } } sub _uniq { my %seen; my @data; foreach (@_) { push(@data, $_) unless ($seen{ $_ }++); } return @data; } =begin private =item C Sets the object's error variable. =end private =cut sub set_error { my $self = shift; my $error = shift; my $e = shift; if (blessed($self)) { $self->{error} = $error; $self->{exception} = $e; } our $_ERROR = $error; our $_EXCEPTION = $e; } =begin private =item C Clears the object's error variable. =end private =cut sub clear_error { my $self = shift; if (blessed($self)) { $self->{error} = undef; $self->{exception} = undef; } our($_ERROR, $_EXCEPTION); undef $_ERROR; undef $_EXCEPTION; } # =begin private # # =item C<_debug_closure ( $code )> # # Debugging function to print out a deparsed (textual) version of a closure. # # =end private # # =cut # # sub _debug_closure { # my $closure = shift; # my $l = Log::Log4perl->get_logger("rdf.query"); # if ($l->is_trace) { # require B::Deparse; # my $deparse = B::Deparse->new("-p", "-sC"); # my $body = $deparse->coderef2text($closure); # $l->trace("--- --- CLOSURE --- ---"); # $l->logcluck($body); # } # } 1; __END__ =back =head1 DEFINED HOOKS The following hook URIs are defined and may be used to extend the query engine functionality using the C<< add_hook >> method: =over 4 =item http://kasei.us/code/rdf-query/hooks/post-create-model Called after loading all external files to a temporary model in queries that use FROM and FROM NAMED. Args: ( $query, $model ) C<$query> is the RDF::Query object. C<$model> is the RDF::Trine::Model object. =item http://kasei.us/code/rdf-query/hooks/post-execute Called immediately before returning a result iterator from the execute method. Args: ( $query, $model, $iterator ) C<$query> is the RDF::Query object. C<$model> is the RDF::Trine::Model object. C<$iterator> is a RDF::Trine::Iterator object. =back =head1 SEE ALSO L =head1 AUTHOR Gregory Todd Williams =head1 LICENSE Copyright (c) 2005-2012 Gregory Todd Williams. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut RDF-Query-2.910/lib/RDF/Query/Algebra/000755 000765 000024 00000000000 12173312223 017151 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Algebra.pm000644 000765 000024 00000020722 12173312155 017516 0ustar00gregstaff000000 000000 # RDF::Query::Algebra # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra - Base class for Algebra expressions =head1 VERSION This document describes RDF::Query::Algebra version 2.910. =head1 METHODS =over 4 =cut package RDF::Query::Algebra; our (@ISA, @EXPORT_OK); BEGIN { our $VERSION = '2.910'; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(triple bgp ggp); } use strict; use warnings; no warnings 'redefine'; use Set::Scalar; use Scalar::Util qw(blessed); use Data::Dumper; use RDF::Query::Expression; use RDF::Query::Expression::Alias; use RDF::Query::Expression::Nary; use RDF::Query::Expression::Binary; use RDF::Query::Expression::Unary; use RDF::Query::Expression::Function; use RDF::Query::Algebra::BasicGraphPattern; use RDF::Query::Algebra::Construct; use RDF::Query::Algebra::Filter; use RDF::Query::Algebra::GroupGraphPattern; use RDF::Query::Algebra::Optional; use RDF::Query::Algebra::Triple; use RDF::Query::Algebra::Quad; use RDF::Query::Algebra::Union; use RDF::Query::Algebra::NamedGraph; use RDF::Query::Algebra::Service; use RDF::Query::Algebra::TimeGraph; use RDF::Query::Algebra::Aggregate; use RDF::Query::Algebra::Sort; use RDF::Query::Algebra::Limit; use RDF::Query::Algebra::Offset; use RDF::Query::Algebra::Distinct; use RDF::Query::Algebra::Path; use RDF::Query::Algebra::Project; use RDF::Query::Algebra::Extend; use RDF::Query::Algebra::SubSelect; use RDF::Query::Algebra::Load; use RDF::Query::Algebra::Clear; use RDF::Query::Algebra::Update; use RDF::Query::Algebra::Minus; use RDF::Query::Algebra::Sequence; use RDF::Query::Algebra::Create; use RDF::Query::Algebra::Copy; use RDF::Query::Algebra::Move; use RDF::Query::Algebra::Table; use constant SSE_TAGS => { 'BGP' => 'RDF::Query::Algebra::BasicGraphPattern', 'constant' => 'RDF::Query::Algebra::Constant', 'construct' => 'RDF::Query::Algebra::Construct', 'distinct' => 'RDF::Query::Algebra::Distinct', 'filter' => 'RDF::Query::Algebra::Filter', 'limit' => 'RDF::Query::Algebra::Limit', 'namedgraph' => 'RDF::Query::Algebra::NamedGraph', 'offset' => 'RDF::Query::Algebra::Offset', 'project' => 'RDF::Query::Algebra::Project', 'quad' => 'RDF::Query::Algebra::Quad', 'service' => 'RDF::Query::Algebra::Service', 'sort' => 'RDF::Query::Algebra::Sort', 'triple' => 'RDF::Query::Algebra::Triple', 'union' => 'RDF::Query::Algebra::Union', 'join' => 'RDF::Query::Algebra::GroupGraphPattern', 'leftjoin' => 'RDF::Query::Algebra::Optional', }; =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->referenced_variables; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; my @list; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { my @blanks = $arg->referenced_blanks; push(@list, @blanks); } } return RDF::Query::_uniq(@list); } =item C<< referenced_functions >> Returns a list of the Function URIs used in this algebra expression. =cut sub referenced_functions { my $self = shift; my @list; foreach my $arg ($self->construct_args) { if (blessed($arg)) { if ($arg->isa('RDF::Query::Expression::Function')) { push(@list, $arg->uri); } elsif ($arg->isa('RDF::Query::Algebra')) { my @funcs = $arg->referenced_functions; push(@list, @funcs); } } } return RDF::Query::_uniq(@list); } =item C<< check_duplicate_blanks >> Returns true if blank nodes respect the SPARQL rule of no blank-label re-use across BGPs, otherwise throws a RDF::Query::Error::QueryPatternError exception. =cut sub check_duplicate_blanks { my $self = shift; my @data; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { $arg->check_duplicate_blanks(); } } return 1; } sub _referenced_blanks { my $self = shift; my @data; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push( @data, $arg->_referenced_blanks ); } } return @data; } =item C<< qualify_uris ( \%namespaces, $base_uri ) >> Returns a new algebra pattern where all referenced Resource nodes representing QNames (ns:local) are qualified using the supplied %namespaces. =cut sub qualify_uris { my $self = shift; my $class = ref($self); my $ns = shift; my $base_uri = shift; my @args; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@args, $arg->qualify_uris( $ns, $base_uri )); } elsif (blessed($arg) and $arg->isa('RDF::Query::Node::Resource')) { my $uri = $arg->uri_value; if (ref($uri)) { $uri = join('', $ns->{ $uri->[0] }, $uri->[1]); $arg = RDF::Query::Node::Resource->new( $uri ); } push(@args, $arg); } else { push(@args, $arg); } } return $class->new( @args ); } =item C<< bind_variables ( \%bound ) >> Returns a new algebra pattern with variables named in %bound replaced by their corresponding bound values. =cut sub bind_variables { my $self = shift; my $class = ref($self); my $bound = shift; my @args; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@args, $arg->bind_variables( $bound )); } elsif (blessed($arg) and $arg->isa('RDF::Trine::Node::Variable') and exists($bound->{ $arg->name })) { push(@args, $bound->{ $arg->name }); } else { push(@args, $arg); } } return $class->new( @args ); } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 0; } =item C<< subpatterns_of_type ( $type [, $block] ) >> Returns a list of Algebra patterns matching C<< $type >> (tested with C<< isa >>). If C<< $block >> is given, then matching stops descending a subtree if the current node is of type C<< $block >>, continuing matching on other subtrees. This list includes the current algebra object if it matches C<< $type >>, and is generated in infix order. =cut sub subpatterns_of_type { my $self = shift; my $type = shift; my $block = shift; return if ($block and $self->isa($block)); my @patterns; push(@patterns, $self) if ($self->isa($type)); foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@patterns, $arg->subpatterns_of_type($type, $block)); } } return @patterns; } =item C<< from_sse ( $sse, \%context ) >> Given an SSE serialization, returns the corresponding algebra expression. =cut sub from_sse { my $class = shift; my $context = $_[1]; if (substr($_[0], 0, 1) eq '(') { for ($_[0]) { if (my ($tag) = m/^[(](\w+)/) { if ($tag eq 'prefix') { s/^[(]prefix\s*[(]\s*//; my $c = { %{ $context || {} } }; while (my ($ns, $iri) = m/^[(](\S+):\s*<([^>]+)>[)]/) { s/^[(](\S+):\s*<([^>]+)>[)]\s*//; $c->{namespaces}{ $ns } = $iri; $context = $c; } s/^[)]\s*//; my $alg = $class->from_sse( $_, $c ); s/^[)]\s*//; return $alg; } if (my $class = SSE_TAGS->{ $tag }) { if ($class->can('_from_sse')) { return $class->_from_sse( $_, $context ); } else { s/^[(](\w+)\s*//; my @nodes; while (my $alg = $class->from_sse( $_, $context )) { push(@nodes, $alg); } return $class->new( @nodes ); } } else { throw RDF::Query::Error -text => "Unknown SSE tag '$tag' in SSE string: >>$_<<"; } } else { throw RDF::Trine::Error -text => "Cannot parse pattern from SSE string: >>$_<<"; } } } else { return; } } =back =head1 FUNCTIONS =over 4 =item C<< triple ( $subj, $pred, $obj ) >> Returns a RDF::Query::Algebra::Triple object with the supplied node objects. =cut sub triple { my @nodes = @_[0..2]; return RDF::Query::Algebra::Triple->new( @nodes ); } =item C<< bgp ( @triples ) >> Returns a RDF::Query::Algebra::BasicGraphPattern object with the supplied triples. =cut sub bgp { my @triples = @_; return RDF::Query::Algebra::BasicGraphPattern->new( @triples ); } =item C<< ggp ( @patterns ) >> Returns a RDF::Query::Algebra::GroupGraphPattern object with the supplied algebra patterns. =cut sub ggp { my @patterns = @_; return RDF::Query::Algebra::GroupGraphPattern->new( @patterns ); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/BGPOptimizer.pm000644 000765 000024 00000007132 12173312155 020474 0ustar00gregstaff000000 000000 # RDF::Query::BGPOptimizer # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::BGPOptimizer - Optimizer for ordering the joins of triple patterns in a BGP =head1 VERSION This document describes RDF::Query::BGPOptimizer version 2.910. =head1 STATUS This module's API and functionality should be considered unstable. In the future, this module may change in backwards-incompatible ways, or be removed entirely. If you need functionality that this module provides, please L. =head1 METHODS =over 4 =cut package RDF::Query::BGPOptimizer; use strict; use warnings; use Data::Dumper; use List::Util qw(reduce); use Scalar::Util qw(blessed reftype refaddr); use RDF::Query::Error qw(:try); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< ordered_triples ( $context, @triples ) >> Returns a list of triples, ordered so as to optimize a left-deep join plan based on the frequency counts provided by the underlying model. =cut sub ordered_triples { my $self = shift; my $context = shift; my @triples = @_; my $model = $context->model; my %vars; my %seen; my @weighted = map { my $triple = RDF::Query::Plan::Triple->new( $_->nodes ); [ $_, $self->_cost( $triple, $context ) ] } @triples; my %triples = map { refaddr($_->[0]) => $_ } @weighted; my @ordered = sort { $a->[1] <=> $b->[1] } @weighted; foreach my $t (@triples) { my @vars = $self->_triple_vars( $t ); foreach my $name (@vars) { push( @{ $vars{ $name } }, $t ) unless ($seen{ $name }{ refaddr($t) }++); } } my %edges; foreach my $name (keys %vars) { my @triples = @{ $vars{ $name } }; foreach my $t (@triples) { my $ta = refaddr($t); foreach my $u (@triples) { my $ua = refaddr($u); next if ($ta == $ua); $edges{ $ta }{ $ua } = $u; } } } my @final; my %used; my $start = shift(@ordered); $used{ refaddr($start) }++; push(@final, $start); my @seen = refaddr($start->[0]); my $count = 0; while (@ordered) { if (++$count > scalar(@triples)) { die "loop in BGPOptimizer (?)"; } my @frontier = grep { not($used{refaddr($_)}) } map { $triples{ $_ } } map { keys(%{ $edges{ $_ } }) } @seen; my @orderedf = sort { $a->[1] <=> $b->[1] } @frontier; if (@orderedf) { my $next = shift(@orderedf); my $addr = refaddr($next); $used{ $addr }++; push(@final, $next); push(@seen, refaddr($next->[0])); @ordered = grep { refaddr($_) != $addr } @ordered; } else { my $next = shift(@ordered); my $addr = refaddr($next); $used{ $addr }++; push(@final, $next); push(@seen, refaddr($next->[0])); } } return map { $_->[0] } @final; } sub _cost { my $self = shift; my $pattern = shift; my $context = shift; my $l = Log::Log4perl->get_logger("rdf.query.bgpoptimizer"); my $bf = $pattern->bf( $context ); my $f = ($bf =~ tr/f//); my $r = $f / 3; $l->debug( "Pattern has bf representation '$bf'" ); $l->debug( "There are $f of 3 free variables" ); $l->debug( 'Estimated cardinality of triple is : ' . $r ); # round the cardinality to an integer return int($r + .5 * ($r <=> 0)); } sub _triple_vars { my $self = shift; my $t = shift; my @nodes = $t->nodes; my (@vars, %seen); foreach my $n (@nodes) { if ($n->isa('RDF::Trine::Node::Variable')) { my $name = $n->name; push(@vars, $name) unless ($seen{ $name }++); } } return @vars; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Compiler/000755 000765 000024 00000000000 12173312223 017366 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Error.pm000644 000765 000024 00000004267 12173312155 017260 0ustar00gregstaff000000 000000 # RDF::Query::Error # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Error - Error classes for RDF::Query. =head1 VERSION This document describes RDF::Query::Error version 2.910. =head1 SYNOPSIS use RDF::Query::Error qw(:try); =head1 DESCRIPTION RDF::Query::Error provides an class hierarchy of errors that other RDF::Query classes may throw using the L API. See L for more information. =head1 REQUIRES L =cut package RDF::Query::Error; use strict; use warnings; no warnings 'redefine'; use Carp qw(carp croak confess); use base qw(Error); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### package RDF::Query::Error::ParseError; use base qw(RDF::Query::Error); package RDF::Query::Error::MethodInvocationError; use base qw(RDF::Query::Error); package RDF::Query::Error::MethodError; use base qw(RDF::Query::Error); package RDF::Query::Error::ModelError; use base qw(RDF::Query::Error); package RDF::Query::Error::QuerySyntaxError; use base qw(RDF::Query::Error); package RDF::Query::Error::QueryPatternError; use base qw(RDF::Query::Error::QuerySyntaxError); package RDF::Query::Error::SimpleQueryPatternError; use base qw(RDF::Query::Error::QueryPatternError); package RDF::Query::Error::CompilationError; use base qw(RDF::Query::Error); package RDF::Query::Error::ComparisonError; use base qw(RDF::Query::Error::CompilationError); package RDF::Query::Error::SerializationError; use base qw(RDF::Query::Error); package RDF::Query::Error::FilterEvaluationError; use base qw(RDF::Query::Error); package RDF::Query::Error::TypeError; use base qw(RDF::Query::Error); package RDF::Query::Error::ExecutionError; use base qw(RDF::Query::Error); package RDF::Query::Error::RequestedInterruptError; use base qw(RDF::Query::Error); package RDF::Query::Error::PermissionError; use base qw(RDF::Query::Error); package RDF::Query::Error::UnimplementedError; use base qw(RDF::Query::Error); 1; __END__ =head1 AUTHOR Gregory Williams =cut RDF-Query-2.910/lib/RDF/Query/ExecutionContext.pm000644 000765 000024 00000006050 12173312155 021467 0ustar00gregstaff000000 000000 # RDF::Query::ExecutionContext # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::ExecutionContext - Query execution context =head1 VERSION This document describes RDF::Query::ExecutionContext version 2.910. =head1 METHODS =over 4 =cut package RDF::Query::ExecutionContext; use strict; use warnings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( model => $model, query => $query, bound => \%bound ) >> =cut sub new { my $class = shift; my %args = @_; my $self = bless( [{ %args }], $class ); return $self; } =item C<< copy ( %new_args ) >> =cut sub copy { my $self = shift; my %args = @_; my $class = ref($self); my @data; foreach my $i (0 .. $#{ $self }) { push(@data, { %{ $self->[$i] } }); } @{ $data[0] }{ keys %args } = values %args; return bless( \@data, $class ); } =item C<< pushstack >> =cut sub pushstack { my $self = shift; unshift( @{ $self }, {} ); } =item C<< popstack >> =cut sub popstack { my $self = shift; shift( @{ $self } ); } =item C<< model >> =cut sub model { my $self = shift; my $model = $self->_get_value( 'model', @_ ); unless ($model) { $model = RDF::Trine::Model->temporary_model; } return $model; } =item C<< query >> =cut sub query { my $self = shift; return $self->_get_value( 'query', @_ ); } =item C<< options >> =cut sub options { my $self = shift; return $self->_get_value( 'options', @_ ); } =item C<< bound >> =cut sub bound { my $self = shift; return $self->_get_value( 'bound', @_ ) || {}; } =item C<< base_uri >> =cut sub base_uri { my $self = shift; return $self->_get_value( 'base_uri', @_ ) || {}; } =item C<< ns >> =cut sub ns { my $self = shift; return $self->_get_value( 'ns', @_ ) || {}; } =item C<< logger >> =cut sub logger { my $self = shift; return $self->_get_value( 'logger', @_ ); } =item C<< costmodel >> =cut sub costmodel { my $self = shift; return $self->_get_value( 'costmodel', @_ ); } =item C<< requested_variables >> =cut sub requested_variables { my $self = shift; return $self->_get_value( 'requested_variables', @_ ); } =item C<< optimize >> =cut sub optimize { my $self = shift; return $self->_get_value( 'optimize', @_ ); } =item C<< strict_errors >> =cut sub strict_errors { my $self = shift; return $self->_get_value( 'strict_errors', @_ ); } =item C<< optimistic_threshold_time >> =cut sub optimistic_threshold_time { my $self = shift; return $self->_get_value( 'optimistic_threshold_time', @_ ); } =item C<< delegate >> =cut sub delegate { my $self = shift; return $self->_get_value( 'delegate', @_ ); } sub _get_value { my $self = shift; my $key = shift; if (@_) { $self->[0]{ $key } = shift; } foreach my $i (0 .. $#{ $self }) { if (exists($self->[ $i ]{ $key })) { return $self->[ $i ]{ $key }; } } return; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Expression/000755 000765 000024 00000000000 12173312223 017753 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Expression.pm000644 000765 000024 00000006336 12173312155 020325 0ustar00gregstaff000000 000000 # RDF::Query::Expression # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression - Class for Expr expressions =head1 VERSION This document describes RDF::Query::Expression version 2.910. =cut package RDF::Query::Expression; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS =over 4 =cut =item C Returns a new Expr structure. =cut sub new { my $class = shift; my $op = shift; my @operands = @_; return bless( [ $op, @operands ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->op, $self->operands); } =item C<< op >> Returns the operator of the expression. =cut sub op { my $self = shift; return $self->[0]; } =item C<< operands >> Returns a list of the operands of the expression. =cut sub operands { my $self = shift; return @{ $self }[ 1 .. $#{ $self } ]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; return sprintf( '(%s %s)', $self->op, join(' ', map { $_->sse( $context ) } $self->operands), ); } =item C<< explain >> Returns a string serialization of the expression appropriate for display on the command line. This method is primarily used by the C<< explain >> method of the subclasses of RDF::Query::Plan. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->op; my $string = "${indent}${type}\n"; foreach my $p ($self->operands) { $string .= $p->explain( $s, $count+1 ); } return $string; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'EXPR'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @ops = $self->operands; my @vars; foreach my $o (@ops) { if ($o->isa('RDF::Query::Node::Variable')) { push(@vars, $o->name); } elsif ($o->isa('RDF::Query::Expression')) { push(@vars, $o->referenced_variables); } } return RDF::Query::_uniq(@vars); } =item C<< nonaggregated_referenced_variables >> Returns a list of the variable names used in this algebra expression except those used as aliases for aggregate operations. =cut sub nonaggregated_referenced_variables { my $self = shift; my @ops = $self->operands; my @vars; foreach my $o (@ops) { if ($o->isa('RDF::Query::Node::Variable::ExpressionProxy')) { } elsif ($o->isa('RDF::Query::Node::Variable')) { push(@vars, $o->name); } elsif ($o->isa('RDF::Query::Expression')) { push(@vars, $o->nonaggregated_referenced_variables); } } return RDF::Query::_uniq(@vars); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Federate/000755 000765 000024 00000000000 12173312223 017333 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Federate.pm000644 000765 000024 00000023230 12173312155 017675 0ustar00gregstaff000000 000000 # RDF::Query::Federate # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Federate - A subclass of RDF::Query for efficient federated query execution. =head1 VERSION This document describes RDF::Query::Federate version 2.910. =head1 STATUS This module's API and functionality should be considered deprecated. If you need functionality that this module provides, please L. =head1 SYNOPSIS my $service = RDF::Query::ServiceDescription->new( $url ); my $query = new RDF::Query::Federate ( $sparql ); $query->add_service( $service ); my $stream = $query->execute(); =head1 DESCRIPTION ... =cut package RDF::Query::Federate; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(blessed); use RDF::Query; use RDF::Query::Federate::Plan; use RDF::Query::ServiceDescription; use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS =over 4 =cut =item C<< new ( $query, \%options ) >> =item C<< new ( $query, $base_uri, $languri, $lang ) >> Returns a new RDF::Query::Federate object for the specified C<$query>. The query language defaults to SPARQLP, but may be set specifically by specifying either C<$languri> or C<$lang>, whose acceptable values are: $lang: 'rdql', 'sparql11', or 'sparql' $languri: 'http://www.w3.org/TR/rdf-sparql-query/', or 'http://jena.hpl.hp.com/2003/07/query/RDQL' =cut sub new { my $class = shift; my $query = shift; my $base_uri = shift; my $languri = shift; my $lang = shift || 'sparql11'; return $class->SUPER::new( $query, $base_uri, $languri, $lang, @_ ); } =item C<< add_service ( $service_description ) >> Adds the service described by C<< $service_description >> to the query's list of data sources. =cut sub add_service { my $self = shift; my $service = shift; push(@{ $self->{ services } }, $service); $self->add_computed_statement_generator( $service->computed_statement_generator ); # and clear out the per-execution query cache, because adding a service might affect the cached values $self->{_query_cache} = {}; return; } =item C<< services >> =cut sub services { my $self = shift; return @{ $self->{ services } || [] }; } =item C<< algebra_fixup ( $algebra, $bridge, $base_uri, $ns ) >> Called in the fixup method of ::Algebra classes, returns either an optimized ::Algebra object ready for execution, or undef (in which case it will be prepared for execution by the ::Algebra::* class itself. =cut sub algebra_fixup { my $self = shift; my $pattern = shift; my $bridge = shift; my $base_uri = shift; my $ns = shift; my $l = Log::Log4perl->get_logger("rdf.query.federate"); $l->trace("RDF::Query::Federate::algebra_fixup called"); if ($self->{force_no_optimization}) { $l->debug("force_no_optimization flag is set, so not performing federation optimization"); return; } # optimize BGPs when we're using service descriptions for pattern matching # (instead of local model-based matching). figure out which triples in the # bgp can be sent to which endpoints, and construct appropriate SERVICE # algebra objects to use instead of the BGP. if ($pattern->isa('RDF::Query::Algebra::BasicGraphPattern')) { if ($self->{ service_predicates }) { my @patterns = $self->_services_for_bgp_triples( $pattern, $bridge, $base_uri, $ns ); my $simple_ggp = RDF::Query::Algebra::GroupGraphPattern->new( @patterns ); my @optimized = $self->_join_services_for_bgp_triples( $pattern, $bridge, $base_uri, $ns, \@patterns ); if ($l->is_debug) { foreach my $i (0 .. $#optimized) { $l->debug("OPTIMIZED $i:\n" . $optimized[$i]->as_sparql({}, '') . "\n---------------\n"); } } my @plan = (@optimized, $simple_ggp); while (scalar(@plan) > 1) { my $rhs = pop(@plan); my $lhs = pop(@plan); my $union = RDF::Query::Algebra::Union->new( $lhs, $rhs ); push(@plan, $union); } my $fixed = do { # turn off optimizations, so we don't end up right back here, trying to optimize this replacement GGP. local($self->{force_no_optimization}) = 1; my $ggp = $plan[0]; $ggp->fixup( $self, $bridge, $base_uri, $ns ); }; my $bgp = "\n" . $pattern->as_sparql({}, ''); $bgp =~ s/\n/\n\t/g; $l->info("replacing BGP =====> {$bgp\n}\n\ WITH =====>\n" . $fixed->as_sparql({}, '')); return $fixed; } } return $bridge->fixup( $pattern, $self, $base_uri, $ns ); } sub _join_services_for_bgp_triples { my $self = shift; my $pattern = shift; my $bridge = shift; my $base_uri = shift; my $ns = shift; my $simplep = shift; my $l = Log::Log4perl->get_logger("rdf.query.federate"); my @join_patterns; # array of tuples, each containing a service pattern and a list of applicable triples foreach my $sd (@{ $self->{services} }) { my $patterns = $sd->patterns; foreach my $bgp (@$patterns) { push( @join_patterns, [ $bgp, $sd, [], [] ] ); } } my @triples = $pattern->triples; foreach my $i (0 .. $#triples) { my $triple = $triples[ $i ]; my $simple = $simplep->[ $i ]; $l->debug("looking at triple: " . $triple->as_sparql({}, '')); my @services; my $pred = $triple->predicate; if ($pred->isa('RDF::Trine::Node::Variable')) { throw RDF::Query::Error::ExecutionError -text => "Cannot use triples with variable predicates with federation endpoints"; } else { foreach my $service_pat (@join_patterns) { my ($bgp, $sd) = @$service_pat; if ($bgp->subsumes( $triple )) { $l->debug("triple {" . $triple->as_sparql({}, "\t") . "} is subsumed by bgp: {" . $bgp->as_sparql({}, "\t") . "}\n"); push( @{ $service_pat->[2] }, $triple ); } else { $l->debug("triple {" . $triple->as_sparql({}, "\t") . "} IS NOT subsumed by bgp: {" . $bgp->as_sparql({}, "\t") . "}\n"); push( @{ $service_pat->[3] }, $simple ); } } } } my @patterns; foreach my $service_pat (@join_patterns) { my (undef, $sd, $join_triples, $simple_triples) = @$service_pat; $l->debug("=====> SERVICE <" . $sd->url . ">\n"); $l->debug(Dumper({ $sd->url => [$join_triples] })); my @triples = @$join_triples; next unless (scalar(@triples) > 1); my $serviceurl = RDF::Query::Node::Resource->new( $sd->url ); my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @triples ); unless ($bgp->connected) { $l->debug("BGP isn't connected. Ignoring."); next; } my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $bgp ); my $service = RDF::Query::Algebra::Service->new( $serviceurl, $ggp ); $l->debug("Triples can be grouped together: \n" . $service->as_sparql( {}, '' )); my $fullggp = RDF::Query::Algebra::GroupGraphPattern->new( $service, @$simple_triples ); push(@patterns, $fullggp); } return @patterns; } sub _services_for_bgp_triples { my $self = shift; my $pattern = shift; my $bridge = shift; my $base_uri = shift; my $ns = shift; my $preds = $self->{ service_predicates }; my %service_triples; # arrays of triples, keyed by service urls my @service_triples; # array of tuples each containing a triple and a list of applicable services foreach my $triple ($pattern->triples) { my @services; my $pred = $triple->predicate; if ($pred->isa('RDF::Trine::Node::Variable')) { throw RDF::Query::Error::ExecutionError -text => "Cannot use triples with variable predicates with federation endpoints"; } else { my $purl = $pred->uri_value; my $services = $preds->{ $purl } || []; if (scalar(@$services) == 0) { throw RDF::Query::Error::ExecutionError -text => "Triple is not described as a capability of any federation endpoint: " . $triple->as_sparql; } else { foreach my $sd (@$services) { push( @{ $service_triples{ $sd->url } }, $triple ); push( @services, $sd->url ); } } } push( @service_triples, [ $triple, \@services ] ); } my @patterns; my %triples_for_single_service; foreach my $data (@service_triples) { my ($triple, $services) = @$data; my @services = @$services; my @spatterns; # if (scalar(@services) == 1) { # push( @{ $triples_for_single_service{ $services[0] } }, $triple ); # } else { foreach my $surl (@services) { my $serviceurl = RDF::Query::Node::Resource->new( $surl ); my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $triple ); my $service = RDF::Query::Algebra::Service->new( $serviceurl, $ggp ); push(@spatterns, $service); } while (@spatterns > 1) { my @patterns = splice( @spatterns, 0, 2, () ); my $union = RDF::Query::Algebra::Union->new( map { RDF::Query::Algebra::GroupGraphPattern->new($_) } @patterns ); push(@spatterns, $union ); } my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @spatterns ); push(@patterns, $ggp); # } } # foreach my $surl (keys %triples_for_single_service) { # my $triples = $triples_for_single_service{ $surl }; # warn "triples for only $surl: " . Dumper($triples) if ($debug); # my $serviceurl = RDF::Query::Node::Resource->new( $surl ); # my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$triples ); # my $service = RDF::Query::Algebra::Service->new( $serviceurl, $ggp ); # unshift(@patterns, $service); # } return @patterns; } =begin private =item C<< plan_class >> Returns the class name for Plan generation. This method should be overloaded by RDF::Query subclasses if the implementation also provides a subclass of RDF::Query::Plan. =end private =cut sub plan_class { return 'RDF::Query::Federate::Plan'; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Functions/000755 000765 000024 00000000000 12173312223 017564 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Functions.pm000644 000765 000024 00000003560 12173312155 020132 0ustar00gregstaff000000 000000 # RDF::Query::Functions # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Functions - Standard Extension Functions =head1 VERSION This document describes RDF::Query::Functions version 2.910. =head1 DESCRIPTION This stub module simply loads all other modules named C<< RDF::Query::Functions::* >>. Each of those modules has an C method that simply adds coderefs to C<< %RDF::Query::functions >>. =head1 METHODS =over 4 =cut package RDF::Query::Functions; use strict; use warnings; no warnings 'redefine'; our $BLOOM_FILTER_LOADED; use Scalar::Util qw(refaddr); use Log::Log4perl; use Module::Pluggable search_path => [ __PACKAGE__ ], require => 1, inner => 1, sub_name => 'function_sets', ; ###################################################################### our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions"); $VERSION = '2.910'; } ###################################################################### =item C<< install_function ( $uri, \&func ) >> =item C<< install_function ( \@uris, \&func ) >> Install the supplied CODE reference as the implementation for the given function URI(s). =cut sub install_function { my $class = shift; while (@_) { my $uris = shift; my $func = shift; $RDF::Query::preferred_function_name{ refaddr($func) } = ref($uris) ? $uris->[0] : $uris; foreach my $uri (ref($uris) ? @$uris : $uris) { $RDF::Query::functions{$uri} = $func; } } } foreach my $function_set (__PACKAGE__->function_sets) { $function_set->install; } 1; __END__ =back =head1 SEE ALSO L, L, L, L, L. =head1 AUTHOR Gregory Williams , Toby Inkster . =cut RDF-Query-2.910/lib/RDF/Query/Node/000755 000765 000024 00000000000 12173312223 016501 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Node.pm000644 000765 000024 00000007717 12173312155 017057 0ustar00gregstaff000000 000000 # RDF::Query::Node # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Node - Base class for RDF Nodes =head1 VERSION This document describes RDF::Query::Node version 2.910. =head1 METHODS =over 4 =cut package RDF::Query::Node; use strict; use warnings; no warnings 'redefine'; use Scalar::Util qw(blessed); use RDF::Query::Node::Blank; use RDF::Query::Node::Literal; use RDF::Query::Node::Resource; use RDF::Query::Node::Variable; our ($VERSION, @ISA, @EXPORT_OK); BEGIN { $VERSION = '2.910'; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(iri blank literal variable); } =item C<< is_variable >> Returns true if this RDF node is a variable, false otherwise. =cut sub is_variable { my $self = shift; return (blessed($self) and $self->isa('RDF::Query::Node::Variable')); } =item C<< compare ( $a, $b ) >> Returns -1, 0, or 1 if $a is less than, equal to, or greater than $b, respectively, according to the SPARQL sorting rules. =cut sub compare { my $a = shift; my $b = shift; warn 'compare'; for ($a, $b) { unless ($_->isa('RDF::Query::Node')) { $_ = RDF::Query::Node->from_trine( $_ ); } } local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; return $a <=> $b; } =item C<< from_trine ( $node ) >> Returns a new RDF::Query::Node object with the same value as $node, a RDF::Trine::Node object. This essentially promotes C<< $node >> to a node object with extra functionality provided by the RDF::Query package (like SPARQL-defined ordering). =cut sub from_trine { my $class = shift; my $n = shift; if ($n->isa('RDF::Trine::Node::Variable')) { return RDF::Query::Node::Variable->new( $n->name ); } elsif ($n->isa('RDF::Trine::Node::Literal')) { return RDF::Query::Node::Literal->new( $n->literal_value, $n->literal_value_language, $n->literal_datatype ); } elsif ($n->isa('RDF::Trine::Node::Resource')) { return RDF::Query::Node::Resource->new( $n->uri_value ); } elsif ($n->isa('RDF::Trine::Node::Blank')) { return RDF::Query::Node::Blank->new( $n->blank_identifier ); } elsif ($n->isa('RDF::Trine::Node::Nil')) { return $n; } else { use Data::Dumper; Carp::confess "from_trine called with unrecognized node type:" . Dumper($n); } } =item C<< explain >> Returns a string serialization of the node appropriate for display on the command line. This method is primarily used by the C<< explain >> method of the subclasses of RDF::Query::Plan. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}" . $self->as_sparql . "\n"; return $string; } =back =head1 FUNCTIONS =over 4 =item C<< compare ( $node_a, $node_b ) >> Returns -1, 0, or 1 if $node_a sorts less than, equal to, or greater than $node_b in the defined SPARQL ordering, respectively. This function may be used as the function argument to C<>. =cut sub compare { my $a = shift; my $b = shift; warn 'compare'; for ($a, $b) { unless ($_->isa('RDF::Query::Node')) { $_ = RDF::Query::Node->from_trine( $_ ); } } local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; return $a <=> $b; } =item C<< iri ( $iri ) >> Returns a RDF::Query::Node::Resource object with the given IRI value. =cut sub iri { my $iri = shift; return RDF::Query::Node::Resource->new( $iri ); } =item C<< blank ( $id ) >> Returns a RDF::Query::Node::Blank object with the given identifier. =cut sub blank { my $id = shift; return RDF::Query::Node::Blank->new( $id ); } =item C<< literal ( $value, $lang, $dt ) >> Returns a RDF::Query::Node::Literal object with the given value and optional language/datatype. =cut sub literal { return RDF::Query::Node::Literal->new( @_ ); } =item C<< variable ( $name ) >> Returns a RDF::Query::Node::Variable object with the given variable name. =cut sub variable { my $name = shift; return RDF::Query::Node::Variable->new( $name ); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Parser/000755 000765 000024 00000000000 12173312223 017050 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Parser.pm000644 000765 000024 00000015736 12173312155 017426 0ustar00gregstaff000000 000000 # RDF::Query::Parser # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Parser - Parser base class =head1 VERSION This document describes RDF::Query::Parser version 2.910. =cut package RDF::Query::Parser; use strict; use warnings; no warnings 'redefine'; use RDF::Query::Node::Resource; use RDF::Query::Node::Literal; use RDF::Query::Node::Blank; use RDF::Query::Node::Variable; use RDF::Query::Algebra; use RDF::Query::Error qw(:try); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS =over 4 =cut =item C Returns a new literal structure. =cut sub new_literal { my $self = shift; my $literal = shift; my $lang = shift; my $dt = shift; return RDF::Query::Node::Literal->new( $literal, $lang, $dt ); } =item C Returns a new variable structure. =cut sub new_variable { my $self = shift; my $name; if (@_) { $name = shift; } else { my $count = $self->{__PRIVATE_VARIABLE_COUNT}++; $name = '_____rdfquery_private_' . $count; } return RDF::Query::Node::Variable->new( $name ); } =item C Returns a new blank node structure. =cut sub new_blank { my $self = shift; my $id; if (@_) { $id = shift; } else { if (not defined($self->{blank_ids})) { $self->{blank_ids} = 1; } $id = 'a' . $self->{blank_ids}++; } return RDF::Query::Node::Blank->new( $id ); } =item C Returns a new variable structure. =cut sub new_uri { my $self = shift; my $uri = shift; return RDF::Query::Node::Resource->new( $uri ); } # =item C # # Returns a new QName URI structure. # # =cut # # sub new_qname { # my $self = shift; # my $prefix = shift; # my $name = shift; # return [ 'URI', [ $prefix, $name ] ]; # } # # =item C # # Returns a new UNION structure. # # =cut # # sub new_union { # my $self = shift; # my @patterns = @_; # return RDF::Query::Algebra::Union->new( @patterns ); # } # # =item C # # Returns a new OPTIONAL structure. # # =cut # # sub new_optional { # my $self = shift; # my $ggp = shift; # my $opt = shift; # return RDF::Query::Algebra::Optional->new( $ggp, $opt ); # } # # =item C # # Returns a new NAMED GRAPH structure. # # =cut # # sub new_named_graph { # my $self = shift; # my $graph = shift; # my $triples = shift; # return RDF::Query::Algebra::NamedGraph->new( $graph, $triples ); # } =item C Returns a new triple structure. =cut sub new_triple { my $self = shift; my ($s,$p,$o) = @_; return RDF::Query::Algebra::Triple->new( $s, $p, $o ); } =item C Returns a new unary expression structure. =cut sub new_unary_expression { my $self = shift; my $op = shift; my $operand = shift; return RDF::Query::Expression::Unary->new( $op, $operand ); } =item C Returns a new binary expression structure. =cut sub new_binary_expression { my $self = shift; my $op = shift; my @operands = @_[0,1]; return RDF::Query::Expression::Binary->new( $op, @operands ); } # =item C # # Returns a new n-ary expression structure. # # =cut # # sub new_nary_expression { # my $self = shift; # my $op = shift; # my @operands = @_; # return RDF::Query::Expression::Binary->new( $op, @operands ); # } # # =item C # # Returns a new logical expression structure. # # =cut # # sub new_logical_expression { # my $self = shift; # my $op = shift; # my @operands = @_; # die $op; # return RDF::Query::Expression->new( $op, @operands ); # } =item C Returns a new function expression structure. =cut sub new_function_expression { my $self = shift; my $function = shift; my @operands = @_; unless (blessed($function)) { $function = RDF::Query::Node::Resource->new( $function ); } return RDF::Query::Expression::Function->new( $function, @operands ); } =item C Returns a new alias expression structure. =cut sub new_alias_expression { my $self = shift; my $var = shift; my $expr = shift; return RDF::Query::Expression::Alias->new( 'alias', $var, $expr ); } =item C Returns a new filter structure. =cut sub new_filter { my $self = shift; my $expr = shift; my $pattern = shift; return RDF::Query::Algebra::Filter->new( $expr, $pattern ); } ###################################################################### =item C Sets the current error to C<$error>. If the parser is in commit mode (by calling C), throws a RDF::Query::Error::ParseError object. Otherwise returns C. =cut sub fail { my $self = shift; my $error = shift; my $l = Log::Log4perl->get_logger("rdf.query.parser"); no warnings 'uninitialized'; my $parsed = substr($self->{input}, 0, $self->{position}); my $line = ($parsed =~ tr/\n//) + 1; my ($lline) = $parsed =~ m/^(.*)\Z/mx; my $col = length($lline); my $rest = substr($self->{remaining}, 0, 10); $self->set_error( "Syntax error; $error at $line:$col (near '$rest')" ); if ($self->{commit}) { throw RDF::Query::Error::ParseError( -text => "$error at $line:$col (near '$rest')" ); } else { return undef; } } ###################################################################### =item C Returns the last error the parser experienced. =cut sub error { my $self = shift; if (defined $self->{error}) { return $self->{error}; } else { return ''; } } =begin private =item C Sets the object's error variable. =end private =cut sub set_error { my $self = shift; my $error = shift; $self->{error} = $error; } =begin private =item C Clears the object's error variable. =end private =cut sub clear_error { my $self = shift; $self->{error} = undef; } # =begin private # # =item C # # Sets the object's commit state. # # =end private # # =cut # # sub set_commit { # my $self = shift; # if (@_) { # $self->{commit} = shift; # } else { # $self->{commit} = 1; # } # } # # =begin private # # =item C # # Clears the object's commit state. # # =end private # # =cut # # sub unset_commit { # my $self = shift; # $self->{commit} = 0; # } # # =begin private # # =item C # # Returns the object's commit state. # # =end private # # =cut # # sub get_commit { # my $self = shift; # return $self->{commit}; # } 1; __END__ =back =head1 AUTHOR Gregory Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/000755 000765 000024 00000000000 12173312223 016506 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Plan.pm000644 000765 000024 00000114225 12173312155 017055 0ustar00gregstaff000000 000000 # RDF::Query::Plan # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan - Executable query plan nodes. =head1 VERSION This document describes RDF::Query::Plan version 2.910. =head1 METHODS =over 4 =cut package RDF::Query::Plan; use strict; use warnings; use Data::Dumper; use List::Util qw(reduce); use Scalar::Util qw(blessed reftype refaddr); use RDF::Query::Error qw(:try); use RDF::Query::BGPOptimizer; use RDF::Query::Plan::Aggregate; use RDF::Query::Plan::BasicGraphPattern; use RDF::Query::Plan::Constant; use RDF::Query::Plan::Construct; use RDF::Query::Plan::Distinct; use RDF::Query::Plan::Filter; use RDF::Query::Plan::Join::NestedLoop; use RDF::Query::Plan::Join::PushDownNestedLoop; use RDF::Query::Plan::Limit; use RDF::Query::Plan::Offset; use RDF::Query::Plan::Project; use RDF::Query::Plan::Extend; use RDF::Query::Plan::Quad; use RDF::Query::Plan::Service; use RDF::Query::Plan::Sort; use RDF::Query::Plan::ComputedStatement; use RDF::Query::Plan::ThresholdUnion; use RDF::Query::Plan::Union; use RDF::Query::Plan::SubSelect; use RDF::Query::Plan::Iterator; use RDF::Query::Plan::Load; use RDF::Query::Plan::Clear; use RDF::Query::Plan::Update; use RDF::Query::Plan::Minus; use RDF::Query::Plan::Sequence; use RDF::Query::Plan::Path; use RDF::Query::Plan::NamedGraph; use RDF::Query::Plan::Copy; use RDF::Query::Plan::Move; use RDF::Trine::Statement; use RDF::Trine::Statement::Quad; use constant READY => 0x01; use constant OPEN => 0x02; use constant CLOSED => 0x04; ###################################################################### our ($VERSION, %PLAN_CLASSES); BEGIN { $VERSION = '2.910'; %PLAN_CLASSES = ( service => 'RDF::Query::Plan::Service', ); } ###################################################################### =item C<< new >> =cut sub new { my $class = shift; my @args = @_; return bless( [ { __state => $class->READY }, @args ], $class ); } =item C<< execute ( $execution_context ) >> =cut sub execute ($); =item C<< next >> =cut sub next; =item C<< get_all >> Returns all remaining rows. =cut sub get_all { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "get_all can't be called on an unopen plan"; } my @rows; while (my $row = $self->next) { push(@rows, $row); } return @rows; } =item C<< close >> =cut sub close { my $self = shift; $self->state( CLOSED ); } =item C<< state ( [ $state ] ) >> Returns the current state of the plan (either READY, OPEN, or CLOSED). If C<< $state >> is provided, updates the plan to a new state. =cut sub state { my $self = shift; if (scalar(@_)) { $self->[0]{__state} = shift; } return $self->[0]{__state}; } =item C<< logging_keys >> =cut sub logging_keys { my $self = shift; return $self->[0]{logging_keys} || {}; } =item C<< explain >> Returns a string serialization of the query plan appropriate for display on the command line. =cut sub explain { my $self = shift; my ($s, $count) = (' ', 0); if (@_) { $s = shift; $count = shift; } my $indent = '' . ($s x $count); my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); foreach my $p ($self->plan_node_data) { if (blessed($p)) { if ($p->isa('RDF::Trine::Statement::Quad')) { $string .= "${indent}${s}" . join(' ', map { ($_->isa('RDF::Trine::Node::Nil')) ? "(nil)" : $_->as_sparql } $p->nodes) . "\n"; } elsif ($p->isa('RDF::Trine::Node::Nil')) { $string .= "${indent}${s}(nil)\n"; } else { $string .= $p->explain( $s, $count+1 ); } } elsif (ref($p)) { $string .= "${indent}${s}" . Dumper($p); Carp::cluck 'unexpected non-blessed ref in RDF::Query::Plan->explain: ' . Dumper($p); } else { no warnings 'uninitialized'; $string .= "${indent}${s}$p\n"; } } return $string; } =item C<< sse >> =cut sub sse { my $self = shift; my $context = shift || {}; my $indent = shift || ''; my $more = ' '; my @proto = $self->plan_prototype; my @data = $self->plan_node_data; my $name = $self->plan_node_name; my @args; my $list = \@data; foreach my $i (0 .. $#proto) { my $p = $proto[ $i ]; push(@args, $self->_sse( $context, $indent, $more, $p, $list )); } return "(${name} " . join(' ', map { defined($_) ? $_ : '()' } @args) . ")"; } sub _sse { my $self = shift; my $context = shift; my $indent = shift; my $more = shift; my $p = shift; my $list = shift; if ($p =~ m/^[PQTNWEJVqibswu]$/) { my $v = shift(@$list); return $self->_sse_atom($context, $indent, $more, $p, $v); } elsif ($p eq 'A') { my $v = shift(@$list); if (blessed($v)) { return $v->sse( $context, $indent ); } else { return '()'; } } elsif (substr($p, 0, 1) eq '\\') { my $rest = substr($p, 1); my $v = shift(@$list); my @args; while (@$v) { push(@args, $self->_sse( $context, $indent, $more, $rest, $v )); } return '(' . join(' ', @args) . ')'; } elsif (substr($p, 0, 1) eq '*') { my $rest = substr($p, 1); my @args; while (@$list) { push(@args, $self->_sse( $context, $indent, $more, $rest, $list )); } no warnings 'uninitialized'; return join("\n${indent}${more}", '', @args); } elsif ($p =~ m/^[PQTNWEJVqibswu\\*]{2,}$/) { my @args; foreach my $p2 (split(//, $p)) { my $v = shift(@$list); push(@args, $self->_sse($context, $indent, $more, $p2, [$v])); } return '(' . join(' ', @args) . ')'; } else { Carp::confess "unrecognized plan node prototype '$p'"; } } sub _sse_atom { my $self = shift; my $context = shift || {}; my $indent = shift; my $more = shift; my $p = shift; my $v = shift; no warnings 'uninitialized'; my $ns = $context->{ namespaces } || {}; my %ns = %$ns; if ($p eq 's') { for ($v) { s/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g; } return qq["$v"]; } elsif ($p eq 'w') { return $v; } elsif ($p eq 'u') { return qq[<$v>]; } elsif ($p eq 'i') { return $v; } elsif ($p eq 'b') { return $v; } elsif ($p eq 'W') { if (blessed($v)) { return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); } else { return $v; } } elsif ($p =~ m/^[PNETV]$/) { if (blessed($v)) { Carp::cluck unless ($v->can('sse')); return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); } else { return '()'; } } elsif ($p eq 'J') { if ($v->isa('RDF::Query::Node::Variable')) { return $v->name; } else { return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); } } } =item C<< serialize >> Return a serialization of the query plan. =cut sub serialize { my $self = shift; } =item C<< delegate >> Returns the delegate object if available. =cut sub delegate { my $self = shift; return $self->[0]{delegate}; } =item C<< referenced_variables >> Returns a list of variable names that are referenced by this plan. =cut sub referenced_variables { my $self = shift; my $refs = $self->[0]{referenced_variables}; return @{ $refs }; } =item C<< as_iterator ( $context ) >> Returns an RDF::Trine::Iterator object for the current (already executed) plan. =cut sub as_iterator { my $self = shift; my $context = shift; my $vars = $context->requested_variables; my $stream = RDF::Trine::Iterator::Bindings->new( sub { $self->next }, $vars, distinct => $self->distinct, sorted_by => $self->ordered ); return $stream; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 0; } =item C<< label ( $label => $value ) >> Sets the named C<< $label >> to C<< $value >> for this plan object. If no C<< $value >> is given, returns the current label value, or undef if none exists. =cut sub label { my $self = shift; my $label = shift; if (@_) { my $value = shift; $self->[0]{labels}{ $label } = $value; } return $self->[0]{labels}{ $label }; } =item C<< graph_labels >> =cut sub graph_labels { my $self = shift; my @labels; foreach my $k (keys %{ $self->[0]{labels} || {} }) { next if ($k eq 'algebra'); my $v = $self->label( $k ); local($Data::Dumper::Indent) = 0; my $l = Data::Dumper->Dump([$v], [$k]); push(@labels, $l); } my $label = join(", ", @labels); return ' ' . $label; } sub DESTROY { my $self = shift; if ($self->state == OPEN) { $self->close; } } ################################################################################ =item C<< generate_plans ( $algebra, $execution_context, %args ) >> Returns a list of equivalent query plan objects for the given algebra object. =cut sub generate_plans { my $self = shift; my $class = ref($self) || $self; my $algebra = shift; my $context = shift; my $config = $context->options || {}; my %args = @_; my $active_graph = $args{ active_graph } || RDF::Trine::Node::Nil->new(); my $l = Log::Log4perl->get_logger("rdf.query.plan"); unless (blessed($algebra) and $algebra->isa('RDF::Query::Algebra')) { throw RDF::Query::Error::MethodInvocationError (-text => "Cannot generate an execution plan with a non-algebra object $algebra"); } $l->trace("generating query plan for " . $algebra->sse({ indent => ' ' }, '')); ############################################################################ ### Optimize simple COUNT(*) aggregates over BGPs if ($algebra->isa('RDF::Query::Algebra::Extend')) { my $agg = $algebra->pattern; if ($agg->isa('RDF::Query::Algebra::Aggregate')) { my @group = $agg->groupby; if (scalar(@group) == 0) { my @ops = $agg->ops; if (scalar(@ops) == 1 and $ops[0][0] eq 'COUNT(*)') { my $ggp = $agg->pattern; if ($ggp->isa('RDF::Query::Algebra::GroupGraphPattern')) { my @bgp = $ggp->patterns; if (scalar(@bgp) == 1 and ($bgp[0]->isa('RDF::Query::Algebra::BasicGraphPattern'))) { my $bgp = $bgp[0]; my @triples = $bgp->triples; if (scalar(@triples) == 1) { $l->debug("Optimizing for COUNT(*) on 1-triple BGP: " . $bgp->sse({ indent => ' ' }, '')); my $vars = $algebra->vars; my $alias = $vars->[0]; my $name = $alias->name; my $done = 0; my $model = $context->model; my $code = sub { return if ($done); $done = 1; my $count = $model->count_statements( $triples[0]->nodes ); my $lit = RDF::Query::Node::Literal->new($count, undef, 'http://www.w3.org/2001/XMLSchema#integer'); my $vb = RDF::Query::VariableBindings->new( { $name => $lit, 'COUNT(*)' => $lit, # this has to be kept around in case a HAVING clause needs it without the alias $name } ); }; my $iter = RDF::Trine::Iterator::Bindings->new( $code, [] ); return RDF::Query::Plan::Iterator->new( $iter ); } } } } } } } ############################################################################ my ($project); my $constant = delete $args{ constants }; if ($algebra->isa('RDF::Query::Algebra::Sort') or not($algebra->is_solution_modifier)) { # projection has to happen *after* sorting, since a sort expr might reference a variable that we project away $project = delete $args{ project }; } my @return_plans; my $aclass = ref($algebra); my ($type) = ($aclass =~ m<::(\w+)$>); if ($type eq 'Aggregate') { my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @groups = $algebra->groupby; my @ops; foreach my $o ($algebra->ops) { my ($alias, $op, $opts, @cols) = @$o; push(@ops, [ $alias, $op, $opts, @cols ]); } my @plans = map { RDF::Query::Plan::Aggregate->new( $_, \@groups, expressions => \@ops ) } @base; push(@return_plans, @plans); } elsif ($type eq 'Construct') { my $triples = $algebra->triples; my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @plans = map { RDF::Query::Plan::Construct->new( $_, $triples ) } @base; push(@return_plans, @plans); } elsif ($type eq 'Distinct') { my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @plans = map { RDF::Query::Plan::Distinct->new( $_ ) } @base; push(@return_plans, @plans); } elsif ($type eq 'Filter') { my @base = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $active_graph ); my $expr = $algebra->expr; my @plans = map { RDF::Query::Plan::Filter->new( $expr, $_, $active_graph ) } @base; push(@return_plans, @plans); } elsif ($type eq 'BasicGraphPattern') { my @triples = map { ($args{ prevent_distinguishing_bnodes }) ? $_ : $_->distinguish_bnode_variables } $algebra->triples; my @normal_triples; my @csg_triples; foreach my $t (@triples) { if (my @csg_plans = $self->_csg_plans( $context, $t )) { push(@csg_triples, $t); } else { my @nodes = $t->nodes; $t = RDF::Query::Algebra::Quad->new( @nodes[ 0..2 ], $active_graph ); # if (my $g = $args{ named_graph }) { # my @nodes = $t->nodes; # $t = RDF::Query::Algebra::Quad->new( @nodes[0..2], $g ); # } push(@normal_triples, $t); } } my @plans; if (scalar(@normal_triples) == 0) { my $v = RDF::Query::VariableBindings->new( {} ); my $plan = RDF::Query::Plan::Constant->new( $v ); push(@plans, $plan); } elsif (scalar(@normal_triples) == 1) { push(@plans, $self->generate_plans( @normal_triples, $context, %args )); } else { my $plan = RDF::Query::Plan::BasicGraphPattern->new( @normal_triples ); push(@plans, $plan); } if (@csg_triples) { my @csg_plans; foreach my $t (@csg_triples) { push(@csg_plans, [ $self->generate_plans( $t, $context, %args ) ]); } my @join_types = RDF::Query::Plan::Join->join_classes( $config ); while (my $cps = shift(@csg_plans)) { my @temp_plans = @plans; @plans = (); foreach my $p (@temp_plans) { foreach my $cp (@$cps) { foreach my $join_type (@join_types) { my $plan = $join_type->new( $p, $cp, 0, {} ); push(@plans, $plan); } } } } push(@return_plans, @plans); } else { push(@return_plans, @plans); } } elsif ($type eq 'GroupGraphPattern') { my @patterns = $algebra->patterns(); my @plans; if (scalar(@patterns) == 0) { my $v = RDF::Query::VariableBindings->new( {} ); my $plan = RDF::Query::Plan::Constant->new( $v ); push(@plans, $plan); } elsif (scalar(@patterns) == 1) { push(@plans, $self->generate_plans( @patterns, $context, %args )); } else { push(@plans, map { $_->[0] } $self->_join_plans( $context, \@patterns, %args, method => 'patterns' )); } push(@return_plans, @plans); } elsif ($type eq 'Limit') { my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @plans = map { RDF::Query::Plan::Limit->new( $algebra->limit, $_ ) } @base; push(@return_plans, @plans); } elsif ($type eq 'NamedGraph') { my @plans; if ($algebra->graph->isa('RDF::Query::Node::Resource')) { @plans = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $algebra->graph ); } else { @plans = map { RDF::Query::Plan::NamedGraph->new( $algebra->graph, $_ ) } $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $algebra->graph ); } push(@return_plans, @plans); } elsif ($type eq 'Offset') { my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @plans = map { RDF::Query::Plan::Offset->new( $algebra->offset, $_ ) } @base; push(@return_plans, @plans); } elsif ($type eq 'Optional') { # just like a BGP or GGP, but we have to pass the optional flag to the join constructor my @patterns = ($algebra->pattern, $algebra->optional); my @base_plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @patterns; my @join_types = RDF::Query::Plan::Join->join_classes( $config ); # XXX this is currently only considering left-deep trees. maybe it should produce all trees? my @plans; my $base_a = shift(@base_plans); my $base_b = shift(@base_plans); foreach my $i (0 .. $#{ $base_a }) { foreach my $j (0 .. $#{ $base_b }) { my $a = $base_a->[ $i ]; my $b = $base_b->[ $j ]; foreach my $join_type (@join_types) { try { my $plan = $join_type->new( $a, $b, 1, {} ); push( @plans, $plan ); } catch RDF::Query::Error::MethodInvocationError with { # my $e = shift; # warn "caught MethodInvocationError: " . Dumper($e); }; } } } push(@return_plans, @plans); } elsif ($type eq 'Minus') { my @patterns = ($algebra->pattern, $algebra->minus); my @base_plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @patterns; my @plans; my $base_a = shift(@base_plans); my $base_b = shift(@base_plans); foreach my $i (0 .. $#{ $base_a }) { foreach my $j (0 .. $#{ $base_b }) { my $a = $base_a->[ $i ]; my $b = $base_b->[ $j ]; my $plan = RDF::Query::Plan::Minus->new( $a, $b ); push( @plans, $plan ); } } push(@return_plans, @plans); } elsif ($type eq 'Project') { my $pattern = $algebra->pattern; my $vars = $algebra->vars; my @base = $self->generate_plans( $pattern, $context, %args ); if ($constant) { # if there's constant data to be joined, we better do it now in case # the project gets rid of variables needed for the join my @plans = splice( @base ); @base = $self->_add_constant_join( $context, $constant, @plans ); $constant = undef; } my @plans; foreach my $plan (@base) { push(@return_plans, RDF::Query::Plan::Project->new( $plan, $vars )); } push(@return_plans, @plans); } elsif ($type eq 'Extend') { my $pattern = $algebra->pattern; my $vars = $algebra->vars; my @base = $self->generate_plans( $pattern, $context, %args ); my @plans; foreach my $plan (@base) { push(@plans, RDF::Query::Plan::Extend->new( $plan, $vars )); } push(@return_plans, @plans); } elsif ($type eq 'Service') { my $pattern = $algebra->pattern; my @base = $self->generate_plans( $pattern, $context, %args ); my @plans; foreach my $plan (@base) { my $sparqlcb = sub { my $row = shift; my $p = $pattern; if ($row) { $p = $p->bind_variables( $row ); } my $ns = $context->ns; my $pstr = $p->as_sparql({namespaces => $ns}, ''); unless (substr($pstr, 0, 1) eq '{') { $pstr = "{ $pstr }"; } my $sparql = join("\n", (map { sprintf("PREFIX %s: <%s>", ($_ eq '__DEFAULT__' ? '' : $_), $ns->{$_}) } (keys %$ns)), sprintf("SELECT * WHERE %s", $pstr) ); return $sparql; }; # unless ($algebra->endpoint->can('uri_value')) { # throw RDF::Query::Error::UnimplementedError (-text => "Support for variable-endpoint SERVICE blocks is not implemented"); # } if (my $ggp = $algebra->lhs) { my @lhs_base = $self->generate_plans( $ggp, $context, %args ); foreach my $lhs_plan (@lhs_base) { my $splan = RDF::Query::Plan::Service->new( $algebra->endpoint, $plan, $algebra->silent, $sparqlcb, $lhs_plan ); push(@plans, $splan); } } else { push(@plans, $PLAN_CLASSES{'service'}->new( $algebra->endpoint, $plan, $algebra->silent, $sparqlcb )); } } push(@return_plans, @plans); } elsif ($type eq 'SubSelect') { my $query = $algebra->query; my $model = $context->model; my %pargs = %args; my $ag = $args{ active_graph }; if (blessed($ag) and $ag->isa('RDF::Query::Node::Variable')) { my %vars = map { $_ => 1 } $query->pattern->referenced_variables; if ($vars{ $ag->name }) { my $new_ag = RDF::Query::Node::Variable->new(); my ($pattern) = $query->pattern; my $new_pattern = $pattern->bind_variables( { $ag->name => $new_ag } ); my $apattern = RDF::Query::Algebra::Extend->new( $new_pattern, [ RDF::Query::Expression::Alias->new( 'alias', $ag, $new_ag ) ] ); $query->{parsed}{triples} = [$apattern]; } my ($plan) = $self->generate_plans( $query->pattern, $context, %args ); push(@return_plans, RDF::Query::Plan::SubSelect->new( $query, $plan )); } else { my ($plan) = $query->prepare( $context->model, planner_args => \%pargs ); push(@return_plans, RDF::Query::Plan::SubSelect->new( $query, $plan )); } } elsif ($type eq 'Sort') { my @base = $self->generate_plans( $algebra->pattern, $context, %args ); my @order = $algebra->orderby; my @neworder; foreach my $o (@order) { my ($dirname, $expr) = @$o; my $dir = ($dirname eq 'ASC') ? 0 : 1; push(@neworder, [$expr, $dir]); } my @plans = map { RDF::Query::Plan::Sort->new( $_, @neworder ) } @base; push(@return_plans, @plans); } elsif ($type eq 'Triple' or $type eq 'Quad') { my $st; if ($args{ prevent_distinguishing_bnodes }) { $st = $algebra; } else { $st = $algebra->distinguish_bnode_variables; } my $pred = $st->predicate; my @nodes = $st->nodes; if (my @csg_plans = $self->_csg_plans( $context, $st )) { push(@return_plans, @csg_plans); } elsif ($type eq 'Triple') { my $plan = RDF::Query::Plan::Quad->new( @nodes[0..2], $active_graph, { sparql => $algebra->as_sparql, bf => $algebra->bf } ); push(@return_plans, $plan); } else { my $plan = (scalar(@nodes) == 4) ? RDF::Query::Plan::Quad->new( @nodes, { sparql => $algebra->as_sparql } ) : RDF::Query::Plan::Quad->new( @nodes, RDF::Trine::Node::Nil->new(), { sparql => $algebra->as_sparql, bf => $algebra->bf } ); push(@return_plans, $plan); } } elsif ($type eq 'Path') { my @plans = $self->_path_plans( $algebra, $context, %args ); push(@return_plans, @plans); } elsif ($type eq 'Union') { my @plans = map { [ $self->generate_plans( $_, $context, %args ) ] } $algebra->patterns; my $plan = RDF::Query::Plan::Union->new( map { $_->[0] } @plans ); push(@return_plans, $plan); } elsif ($type eq 'Sequence') { my @pat = $algebra->patterns; if (@pat) { my @plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @pat; my $plan = RDF::Query::Plan::Sequence->new( map { $_->[0] } @plans ); push(@return_plans, $plan); } else { my $stream = RDF::Trine::Iterator::Bindings->new( sub {} ); push(@return_plans, $stream); } } elsif ($type eq 'Load') { push(@return_plans, RDF::Query::Plan::Load->new( $algebra->url, $algebra->graph )); } elsif ($type eq 'Update') { my $ds = $algebra->dataset || {}; my $default = $ds->{'default'} || []; my $named = $ds->{'named'} || {}; my $dcount = scalar(@$default); my $ncount = scalar(@{[ keys %$named ]}); # warn 'Update dataset: ' . Dumper($algebra->dataset); my @plans; my @dataset = ($ds); if ($dcount == 1 and $ncount == 0) { # if it's just a single named graph to be used as the default graph, # then rewrite the pattern to use the named graph (and check to make # sure there aren't any GRAPH blocks) @dataset = (); @plans = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $default->[0] ); } elsif ($dcount == 0 and $ncount == 0) { @dataset = (); @plans = $self->generate_plans( $algebra->pattern, $context, %args ); } else { @plans = $self->generate_plans( $algebra->pattern, $context, %args ); } foreach my $p (@plans) { push(@return_plans, RDF::Query::Plan::Update->new( $algebra->delete_template, $algebra->insert_template, $p, @dataset )); } } elsif ($type eq 'Clear') { push(@return_plans, RDF::Query::Plan::Clear->new( $algebra->graph )); } elsif ($type eq 'Create') { my $plan = RDF::Query::Plan::Constant->new(); push(@return_plans, $plan); } elsif ($type eq 'Copy') { my $plan = RDF::Query::Plan::Copy->new( $algebra->from, $algebra->to, $algebra->silent ); push(@return_plans, $plan); } elsif ($type eq 'Move') { my $plan = RDF::Query::Plan::Move->new( $algebra->from, $algebra->to, $algebra->silent ); push(@return_plans, $plan); } elsif ($type eq 'Table') { my $plan = RDF::Query::Plan::Constant->new( $algebra->rows ); push(@return_plans, $plan); } else { throw RDF::Query::Error::MethodInvocationError (-text => "Cannot generate an execution plan for unknown algebra class $aclass"); } if ($constant and scalar(@$constant)) { my @plans = splice( @return_plans ); @return_plans = $self->_add_constant_join( $context, $constant, @plans ); } foreach my $p (@return_plans) { Carp::confess 'not a plan: ' . Dumper($p) unless ($p->isa('RDF::Query::Plan')); $p->label( algebra => $algebra ); } return @return_plans; } sub _csg_plans { my $self = shift; my $context = shift; my $st = shift; my $pred = $st->predicate; return unless (blessed($context)); my $query = $context->query; my @return_plans; if (blessed($query) and $pred->isa('RDF::Trine::Node::Resource') and scalar(@{ $query->get_computed_statement_generators( $st->predicate->uri_value ) })) { my $csg = $query->get_computed_statement_generators( $pred->uri_value ); my @nodes = $st->nodes; my $quad = (scalar(@nodes) == 4) ? 1 : 0; my $mp = RDF::Query::Plan::ComputedStatement->new( @nodes[0..3], $quad ); push(@return_plans, $mp); } return @return_plans; } sub _join_plans { my $self = shift; my $context = shift; my $triples = shift; my %args = @_; my $config = $context->options || {}; my $method = $args{ method }; my @join_types = RDF::Query::Plan::Join->join_classes( $config ); my @plans; my $opt = $context->optimize; my @slice = ($opt) ? (0 .. $#{ $triples }) : (0); foreach my $i (@slice) { my @triples = @$triples; # pick a triple to use as the LHS my ($t) = splice( @triples, $i, 1 ); my @_lhs = $self->generate_plans( $t, $context, %args ); my @lhs_plans = map { [ $_, [$t] ] } @_lhs; if (@triples) { my @rhs_plans = $self->_join_plans( $context, \@triples, %args ); foreach my $i (0 .. $#lhs_plans) { foreach my $j (0 .. $#rhs_plans) { my $a = $lhs_plans[ $i ][0]; my $b = $rhs_plans[ $j ][0]; my $algebra_a = $lhs_plans[ $i ][1]; my $algebra_b = $rhs_plans[ $j ][1]; Carp::confess 'no lhs for join: ' . Dumper(\@lhs_plans) unless (blessed($a)); Carp::confess 'no rhs for join: ' . Dumper(\@triples, \@rhs_plans) unless (blessed($b)); foreach ($algebra_a, $algebra_b) { unless (ref($_) and reftype($_) eq 'ARRAY') { Carp::cluck Dumper($_) } } foreach my $join_type (@join_types) { next if ($join_type eq 'RDF::Query::Plan::Join::PushDownNestedLoop' and $b->subplans_of_type('RDF::Query::Plan::Service')); try { my @algebras; foreach ($algebra_a, $algebra_b) { if (reftype($_) eq 'ARRAY') { push(@algebras, @$_); } } my %logging_keys; if ($method eq 'triples') { my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @algebras ); my $sparql = $bgp->as_sparql; my $bf = $bgp->bf; $logging_keys{ bf } = $bf; $logging_keys{ sparql } = $sparql; } else { my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @algebras ); my $sparql = $ggp->as_sparql; $logging_keys{ sparql } = $sparql; } my $plan = $join_type->new( $b, $a, 0, \%logging_keys ); push( @plans, [ $plan, [ @algebras ] ] ); } catch RDF::Query::Error::MethodInvocationError with { # warn "caught MethodInvocationError."; }; } } } } else { @plans = @lhs_plans; } } if ($opt) { return @plans; } else { if (@plans) { return $plans[0]; # XXX need to figure out what's the 'best' plan here } else { return; } } } sub _add_constant_join { my $self = shift; my $context = shift; my $constant = shift; my @return_plans = @_; my $config = $context->options || {}; my @join_types = RDF::Query::Plan::Join->join_classes( $config ); while (my $const = shift(@$constant)) { my @plans = splice(@return_plans); foreach my $p (@plans) { foreach my $join_type (@join_types) { try { my $plan = $join_type->new( $p, $const ); push( @return_plans, $plan ); } catch RDF::Query::Error::MethodInvocationError with { # warn "caught MethodInvocationError."; }; } } } return @return_plans; } sub _path_plans { my $self = shift; my $algebra = shift; my $context = shift; my %args = @_; my $path = $algebra->path; my $start = $algebra->start; my $end = $algebra->end; for ($start, $end) { if ($_->isa('RDF::Query::Node::Blank')) { $_ = $_->make_distinguished_variable; } } my $npath = $self->_normalize_path( $path ); return $self->__path_plan( $start, $npath, $end, $args{ active_graph }, $context, %args ); } sub _normalize_path { my $self = shift; my $path = shift; if (blessed($path)) { return $path; } my $op = $path->[0]; my @nodes = map { $self->_normalize_path($_) } @{ $path }[ 1 .. $#{ $path } ]; if ($op eq '0-') { $op = '*'; } elsif ($op eq '1-') { $op = '+'; } elsif ($op eq '0-1') { $op = '?'; } elsif ($op =~ /^-\d+$/) { $op = "0$op"; } if ($op eq '!') { # re-order the nodes so that forward predicates come first, followed by backwards predicates # !(:fwd1|:fwd2|:fwd3|^:bkw1|^:bkw2|^:bkw3) @nodes = sort { blessed($a) ? -1 : (($a->[0] eq '^') ? 1 : -1) } @nodes; } return [$op, @nodes]; } sub __path_plan { my $self = shift; my $start = shift; my $path = shift; my $end = shift; my $graph = shift; my $context = shift; my %args = @_; my $distinct = 1; #$args{distinct} ? 1 : 0; my $config = $context->options || {}; my $l = Log::Log4perl->get_logger("rdf.query.plan.path"); # _simple_path will return an algebra object if the path can be expanded # into a simple basic graph pattern (for fixed-length paths) if (my $a = $self->_simple_path( $start, $path, $end, $graph )) { my ($plan) = $self->generate_plans( $a, $context, %args, prevent_distinguishing_bnodes => 1 ); $l->trace('expanded path to pattern: ' . $plan->sse); return $plan; } if (blessed($path)) { ### X iri Y # $path is a resource object: this is a triple (a path of length 1) my $s = $start; my $e = $end; my $algebra = $graph ? RDF::Query::Algebra::Quad->new( $s, $path, $e, $graph ) : RDF::Query::Algebra::Triple->new( $s, $path, $e ); my ($plan) = $self->generate_plans( $algebra, $context, %args, prevent_distinguishing_bnodes => 1 ); $l->trace('expanded path to pattern: ' . $plan->sse); return $plan; } my ($op, @nodes) = @$path; # if ($op eq 'DISTINCT') { # return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args, distinct => 1 ); # } if ($op eq '!') { my $total = scalar(@nodes); my $neg = scalar(@{ [ grep { not(blessed($_)) and $_->[0] eq '^' } @nodes ] }); my $pos = $total - $neg; if ($pos == $total) { ### X !(:iri1|...|:irin) Y return RDF::Query::Plan::Path->new( 'NegatedPropertySet', $start, [@nodes], $end, $graph, $distinct, %args ); } elsif ($neg == $total) { ### X !(^:iri1|...|^:irin)Y my @preds = map { $_->[1] } @nodes; return $self->__path_plan($start, ['^', ['!', @preds]], $end, $graph, $context, %args); } else { ### X !(:iri1|...|:irii|^:irii+1|...|^:irim) Y my @fwd = grep { blessed($_) } @nodes; my @bwd = grep { not(blessed($_)) } @nodes; my $fplan = $self->__path_plan($start, ['!', @fwd], $end, $graph, $context, %args); my $bplan = $self->__path_plan($start, ['!', @bwd], $end, $graph, $context, %args); return RDF::Query::Plan::Union->new($fplan, $bplan); } } elsif ($op eq '^') { ### X ^path Y return $self->__path_plan( $end, $nodes[0], $start, $graph, $context, %args); } elsif ($op eq '/') { my $count = scalar(@nodes); if ($count == 1) { return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args ); } else { my $joinvar = RDF::Query::Node::Variable->new(); my @plans = $self->__path_plan( $start, $nodes[0], $joinvar, $graph, $context, %args ); foreach my $i (2 .. $count) { my $endvar = ($i == $count) ? $end : RDF::Query::Node::Variable->new(); my ($rhs) = $self->__path_plan( $joinvar, $nodes[$i-1], $endvar, $graph, $context, %args ); push(@plans, $rhs); $joinvar = $endvar; } my @join_types = RDF::Query::Plan::Join->join_classes( $config ); my @jplans; foreach my $jclass (@join_types) { push(@jplans, $jclass->new( @plans[0,1], 0 )); } $l->trace("expanded /-path to: " . $jplans[0]->sse); return $jplans[0]; } } elsif ($op eq '|') { ### X path1 | path2 Y my @plans = map { $self->__path_plan($start, $_, $end, $graph, $context, %args) } @nodes; return RDF::Query::Plan::Union->new(@plans); } elsif ($op eq '?') { ### X path? Y my $upath = $nodes[0]; my $zplan = $self->__path_plan($start, ['0', $upath], $end, $graph, $context, %args ); my $oplan = $self->__path_plan($start, $upath, $end, $graph, $context, %args); # project away any non-distinguished variables introduced by plan-to-bgp expansion my @vars = grep { blessed($_) and $_->isa('RDF::Query::Node::Variable') } ($start, $end); my $odplan = RDF::Query::Plan::Project->new( $oplan, \@vars ); my $pplan = RDF::Query::Plan::Union->new($zplan, $odplan); # distinct the results my $plan = RDF::Query::Plan::Distinct->new( $pplan ); return $plan; } elsif ($op eq '*') { ### X path* Y return RDF::Query::Plan::Path->new( 'ZeroOrMorePath', $start, $nodes[0], $end, $graph, $distinct, %args ); } elsif ($op eq '+') { ### X path+ Y return RDF::Query::Plan::Path->new( 'OneOrMorePath', $start, $nodes[0], $end, $graph, $distinct, %args ); } elsif ($op eq '0') { ### X path{0} Y return RDF::Query::Plan::Path->new( 'ZeroLengthPath', $start, $nodes[0], $end, $graph, $distinct, %args ); } elsif ($op =~ /^\d+$/) { ### X path{n} Y where n > 0 my $count = $op; if ($count == 1) { return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args ); } else { my $joinvar = RDF::Query::Node::Variable->new(); my @plans = $self->__path_plan( $start, $nodes[0], $joinvar, $graph, $context, %args ); foreach my $i (2 .. $count) { my $endvar = ($i == $count) ? $end : RDF::Query::Node::Variable->new(); my ($rhs) = $self->__path_plan( $joinvar, $nodes[0], $endvar, $graph, $context, %args ); push(@plans, $rhs); $joinvar = $endvar; } my @join_types = RDF::Query::Plan::Join->join_classes( $config ); my @jplans; my @plan = shift(@plans); while (@plans) { my $q = shift(@plans); my @p; foreach my $p (@plan) { foreach my $jclass (@join_types) { push(@p, $jclass->new( $p, $q, 0 )); } } @plan = @p; } return $plan[0]; } } elsif ($op =~ /^(\d+)-(\d+)$/) { ### X path{n,m} Y my ($n,$m) = split('-', $op, 2); # warn "$1- to $2-length path"; my @range = sort { $a <=> $b } ($n, $m); my $from = $range[0]; my $to = $range[1]; my @plans; foreach my $i ($from .. $to) { if ($i == 0) { push(@plans, $self->__path_plan($start, ['0', []], $end, $graph, $context, %args )); } else { push(@plans, $self->__path_plan( $start, [$i, $nodes[0]], $end, $graph, $context, %args )); } } while (scalar(@plans) > 1) { my $lhs = shift(@plans); my $rhs = shift(@plans); unshift(@plans, RDF::Query::Plan::Union->new( $lhs, $rhs )); } return $plans[0]; } elsif ($op =~ /^(\d+)-$/) { ### X path{n,} Y where n > 0 my ($min) = split('-', $op); # expand :p{n,} into :p{n}/:p* my $path = [ '/', [ $1, @nodes ], [ '*', @nodes ] ]; my $plan = $self->__path_plan( $start, $path, $end, $graph, $context, %args ); return $plan; } else { throw RDF::Query::Error -text => "Cannot generate plan for unknown path type $op"; } } sub _simple_path { my $self = shift; my $start = shift; my $path = shift; my $end = shift; my $graph = shift; if (blessed($path)) { return ($graph) ? RDF::Query::Algebra::Quad->new( $start, $path, $end, $graph ) : RDF::Query::Algebra::Triple->new( $start, $path, $end ); } return unless (reftype($path) eq 'ARRAY'); my $op = $path->[0]; if ($op eq '/') { my @patterns; my @jvars = map { RDF::Query::Node::Variable->new() } (2 .. $#{ $path }); foreach my $i (1 .. $#{ $path }) { my $s = ($i == 1) ? $start : $jvars[ $i-2 ]; my $e = ($i == $#{ $path }) ? $end : $jvars[ $i-1 ]; my $triple = $self->_simple_path( $s, $path->[ $i ], $e, $graph ); return unless ($triple); push(@patterns, $triple); } my @triples = map { $_->isa('RDF::Query::Algebra::BasicGraphPattern') ? $_->triples : $_ } @patterns; return RDF::Query::Algebra::BasicGraphPattern->new( @triples ); } elsif ($op eq '^' and scalar(@$path) == 2 and blessed($path->[1])) { return ($graph) ? RDF::Query::Algebra::Quad->new( $end, $path->[1], $start, $graph ) : RDF::Query::Algebra::Triple->new( $end, $path->[1], $start ); } elsif ($op =~ /^\d+$/ and $op == 1) { return $self->_simple_path( $start, $path->[1], $end, $graph ); } return; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name; =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. These identifiers are recognized: * 'A' - An RDF::Query::Algebra object * 'b' - A boolean integer value (0 or 1) * 'E' - An expression (either an RDF::Query::Expression object or an RDF node) * 'i' - An integer * 'J' - A valid Project node (an RDF::Query::Expression object or an Variable node) * 'N' - An RDF node * 'P' - A RDF::Query::Plan object * 'q' - A RDF::Query object * 'Q' - An RDF::Trine::Statement::Quad object * 's' - A string * 'T' - An RDF::Trine::Statement object * 'u' - A valid URI string * 'V' - A variable binding set (an object of type RDF::Query::VariableBindings) * 'w' - A bareword string * 'W' - An RDF node or wildcard ('*') * '*X' - A list of X nodes (where X is another identifier scalar) * '\X' - An array reference of X nodes (where X is another identifier scalar) =cut sub plan_prototype; =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data; =item C<< subplans_of_type ( $type [, $block] ) >> Returns a list of Plan objects matching C<< $type >> (tested with C<< isa >>). If C<< $block >> is given, then matching stops descending a subtree if the current node is of type C<< $block >>, continuing matching on other subtrees. This list includes the current plan object if it matches C<< $type >>, and is generated in infix order. =cut sub subplans_of_type { my $self = shift; my $type = shift; my $block = shift; return if ($block and $self->isa($block)); my @patterns; push(@patterns, $self) if ($self->isa($type)); foreach my $p ($self->plan_node_data) { if (blessed($p) and $p->isa('RDF::Query::Plan')) { push(@patterns, $p->subplans_of_type($type, $block)); } } return @patterns; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/ServiceDescription.pm000644 000765 000024 00000033531 12173312155 021767 0ustar00gregstaff000000 000000 # RDF::Query::ServiceDescription # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::ServiceDescription - Class for describing federated query data sources. =head1 VERSION This document describes RDF::Query::ServiceDescription version 2.910. =head1 METHODS =over 4 =cut package RDF::Query::ServiceDescription; our ($VERSION); BEGIN { $VERSION = '2.910'; } use strict; use warnings; no warnings 'redefine'; use URI::file; use RDF::Trine::Iterator qw(smap swatch); use Scalar::Util qw(blessed); use LWP::UserAgent; use Data::Dumper; =item C<< new ( $service_uri, %data ) >> =cut sub new { my $class = shift; my $uri = shift; my %data = @_; unless ($data{capabilities}) { $data{capabilities} = [ { type => RDF::Query::Node::Resource->new('http://kasei.us/2008/04/sparql#any_triple') } ]; } my $data = { url => $uri, label => "SPARQL Endpoint $uri", definitive => 0, %data, }; my $self = bless( $data, $class ); return $self; } =item C<< new_from_uri ( $url ) >> Creates a new service description object using the DARQ-style service description data located at C<< $url >>. =cut sub new_from_uri { my $class = shift; my $uri = shift; my $ua = LWP::UserAgent->new( agent => "RDF::Query/$RDF::Query::VERSION" ); $ua->default_headers->push_header( 'Accept' => "application/rdf+xml;q=0.5,text/turtle;q=0.7,text/xml" ); my $resp = $ua->get( $uri ); unless ($resp->is_success) { warn "No content available from $uri: " . $resp->status_line; return; } my $content = $resp->content; my $store = RDF::Trine::Store::DBI->temporary_store(); my $model = RDF::Trine::Model->new( $store ); my $parser = RDF::Trine::Parser->new('turtle'); $parser->parse_into_model( $uri, $content, $model ); return $class->new_with_model( $model ); } =item C<< new_with_model ( $model ) >> Creates a new service description object using the DARQ-style service description data loaded in the supplied C<< $model >> object. =cut sub new_with_model { my $class = shift; my $model = shift; my ($label, $url, $triples, $definitive, @capabilities, @patterns); my $l = Log::Log4perl->get_logger("rdf.query.servicedescription"); my $infoquery = RDF::Query->new( <<"END" ); PREFIX rdf: PREFIX rdfs: PREFIX foaf: PREFIX xsd: PREFIX sd: SELECT ?label ?url ?size ?def WHERE { ?s a sd:Service ; rdfs:label ?label ; sd:url ?url . OPTIONAL { ?s sd:totalTriples ?size . FILTER( ISLITERAL(?size) ) } OPTIONAL { ?s sd:isDefinitive ?def . FILTER( ISLITERAL(?def) ) } FILTER( ISLITERAL(?label) && ISURI(?url) ). } LIMIT 1 END ($label, $url, $triples, my $def) = $infoquery->get( $model ); return undef unless (defined $label); $definitive = (defined($def) ? ($def->literal_value eq 'true' ? 1 : 0) : 0); { my $capquery = RDF::Query->new( <<"END" ); PREFIX rdf: PREFIX rdfs: PREFIX foaf: PREFIX xsd: PREFIX sd: PREFIX sparql: SELECT DISTINCT ?pred ?type ?sofilter ?ssel ?osel ?triples WHERE { [] a sd:Service ; sd:capability ?cap . { ?cap sd:predicate ?pred . } UNION { ?cap a ?type . FILTER(?type = sparql:any_triple) } OPTIONAL { ?cap sd:sofilter ?sofilter } OPTIONAL { ?cap sd:objectSelectivity ?osel } OPTIONAL { ?cap sd:subjectSelectivity ?ssel } OPTIONAL { ?cap sd:triples ?triples } } END my $iter = $capquery->execute( $model ); while (my $row = $iter->next) { my ($p, $type, $f, $ss, $os, $t) = @{ $row }{ qw(pred type sofilter ssel osel triples) }; my $data = ($p) ? { pred => $p } : { type => $type }; $data->{ object_selectivity } = $os if (defined $os); $data->{ subject_selectivity } = $ss if (defined $ss); $data->{ size } = $t if (defined $t); if (defined $f) { my $base; my $parser = RDF::Query::Parser::SPARQL->new(); my $expr = $parser->parse_expr( $f->literal_value, $base, {} ); $data->{ sofilter } = $expr; } push(@capabilities, $data); } } { my $var_id = 1; my @statements; my %patterns; my %bnode_map; my $patterns = $model->get_statements( undef, RDF::Trine::Node::Resource->new('http://kasei.us/2008/04/sparql#pattern'), undef ); while (my $st = $patterns->next) { my $pattern = $st->object; my @queue = ($pattern); while (my $subj = shift(@queue)) { my $stream = $model->get_statements( $subj, undef, undef ); while (my $st = $stream->next) { push(@queue, $st->object); my @nodes = ($subj, $st->predicate, $st->object); foreach my $i (0 .. $#nodes) { if ($nodes[$i]->isa('RDF::Query::Node::Blank')) { if (exists($bnode_map{ $nodes[$i]->as_string })) { $nodes[$i] = $bnode_map{ $nodes[$i]->as_string }; } else { $nodes[$i] = $bnode_map{ $nodes[$i]->as_string } = RDF::Query::Node::Variable->new('p' . $var_id++); } } } my $st = RDF::Query::Algebra::Triple->new( @nodes ); push(@{ $patterns{ $pattern->as_string } }, $st ); } } } foreach my $k (keys %patterns) { my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @{ $patterns{ $k } } ); $l->debug("SERVICE BGP: " . $bgp->as_sparql({}, '')); push( @patterns, $bgp ); } } my %data = ( label => (ref($label) ? $label->literal_value : ''), size => (ref($triples) ? $triples->literal_value : ''), definitive => $definitive, capabilities => \@capabilities, patterns => \@patterns, ); return $class->new( $url->uri_value, %data ); } =item C<< url >> Returns the endpoint URL of the service. =cut sub url { my $self = shift; return $self->{url}; } =item C<< size >> Returns the number of triples the service claims to have. =cut sub size { my $self = shift; return $self->{size}; } =item C<< label >> Returns the label of the service. =cut sub label { my $self = shift; return $self->{label}; } =item C<< definitive >> Returns true if the endpoint claims to have definitive information. =cut sub definitive { my $self = shift; return $self->{definitive}; } =item C<< capabilities >> Returns an ARRAY reference of capabilities (as HASH references) of the service. Each capability will contain information on size, selectivity, any subject-object filter, and required predicate, with the following classes: $capability->{object_selectivity} # RDF::Trine::Node::Literal xsd:double $capability->{sofilter} # RDF::Query::Expression $capability->{size} # RDF::Trine::Node::Literal xsd:integer $capability->{pred} # RDF::Trine::Node::Resource =cut sub capabilities { my $self = shift; return $self->{capabilities}; } =item C<< patterns >> Returns an ARRAY reference of RDF::Query::Algebra::BasicGraphPattern objects representing common patterns used by the endpoint. =cut sub patterns { my $self = shift; return $self->{patterns}; } =item C<< computed_statement_generator >> Returns a closure appropriate for passing to C<< RDF::Query->add_computed_statement_generator >> to generate statement iterators for the remote service. This closure takes C<< ($query, $bridge, \%bound, $subj, $pred, $obj [, $context ] ) >> as arguments and returns either C<< undef >> if no statements can be generated given the arguments, or a C<< RDF::Trine::Iterator::Graph >> iterator containing statements matching C<< $subj, $pred, $obj [, $context ] >>. =cut sub computed_statement_generator { my $self = shift; my $caps = $self->capabilities; my %preds = map { $_->{pred}->uri_value => $_ } grep { exists $_->{pred} } @$caps; my $l = Log::Log4perl->get_logger("rdf.query.servicedescription"); return sub { my $query = shift; my $bridge = shift; my $bound = shift; my $_cast = $bridge->can('_cast_to_local'); my $cast = sub { my $n = shift; return unless $n; $n->isa('RDF::Query::Node') ? $n : $_cast->( $n ) }; my $s = $cast->( shift ); my $p = $cast->( shift ); my $o = $cast->( shift ); my $c = $cast->( shift ); return undef if ($c); # named statements can't be retrieved from another endpoint. return undef unless ($p); # we need a predicate for matching against service capabilities. my $puri = $p->uri_value; my $cap = $preds{ $puri }; if ($self->definitive) { return unless ($cap); # no capability matches this predicate. } else { $cap ||= {}; } my $ok = 1; my $sofilter = $cap->{ sofilter }; if ($sofilter) { my %vars = map { $_ => 1 } $sofilter->referenced_variables; my $runnable = 1; if ($vars{ subject }) { unless ($bound->{subject}) { $runnable = 0; $l->debug( "statement generator isn't runnable: subject is not bound" ); } } if ($vars{ object }) { unless ($bound->{object}) { $runnable = 0; $l->debug( "statement generator isn't runnable: object is not bound" ); } } if ($runnable) { my $bound = { subject => $s, object => $o }; my $bool = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $filter = RDF::Query::Expression::Function->new( $bool, $sofilter ); my $value = $filter->evaluate( $query, $bound ); my $nok = ($value->literal_value eq 'false'); if ($nok) { $ok = 0; $l->debug( "statement generator didn't pass sofilter: " . $sofilter->sse({}, '') ); } } } if ($ok) { my @triple = ($s,$p,$o); foreach my $i (0 .. $#triple) { my $node = $triple[$i]; if ($node->isa('RDF::Query::Node::Blank')) { # blank nodes need special handling, since we can't directly reference # blank nodes on a remote endpoint. # for now, we'll just assume that this is a losing battle, and no # results are possible when trying to use a blank node to identify # remote nodes (which is an acceptable reading of the spec). so return # an empty iterator without actually making the remote call. return RDF::Trine::Iterator::Bindings->new(); } } my $st = RDF::Query::Algebra::Triple->new( @triple ); $l->debug( "running statement generator for " . $st->sse({}, '') ); $l->debug( "running statement generator with pre-bound data: " . Dumper($bound) ); my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $st ); my $service = RDF::Query::Algebra::Service->new( RDF::Query::Node::Resource->new( $self->url ), $ggp ); my $context = RDF::Query::ExecutionContext->new( bound => {}, ); my ($plan) = RDF::Query::Plan->generate_plans( $service, $context ); $plan->execute( $context ); my $bindings = RDF::Trine::Iterator::Bindings->new( sub { my $vb = $plan->next; return $vb; } ); my $iter = smap { my $bound = shift; my $triple = $st->bind_variables( $bound ); $triple->label( origin => $self->url ); $triple; } $bindings; return $iter; } else { return undef; } }; } =item C<< answers_triple_pattern ( $triple ) >> Returns true if the service described by this object can answer queries comprised of the supplied triple pattern. =cut sub answers_triple_pattern { my $self = shift; my $triple = shift; my $l = Log::Log4perl->get_logger("rdf.query.servicedescription"); $l->debug( 'checking triple for service compatability: ' . $triple->sse ); my $caps = $self->capabilities; my @wildcards = grep { exists $_->{type} and $_->{type}->uri_value eq 'http://kasei.us/2008/04/sparql#any_triple' } @$caps; if (@wildcards) { # service can answer any triple pattern, so return true. return 1; } my @pred_caps = grep { exists $_->{pred} } @$caps; my $p = $triple->predicate; if (not($p->isa('RDF::Trine::Node::Variable'))) { # if predicate is bound (not a variable) my $puri = $p->uri_value; $l->trace(" service compatability based on predicate: $puri"); my %preds = map { $_->{pred}->uri_value => $_ } @pred_caps; $l->trace(" service supports predicates: " . join(', ', keys %preds)); my $cap = $preds{ $puri }; unless ($cap) { # no capability matches this predicate. $l->debug("*** service doesn't support the predicate $puri"); return 0; } my $ok = 1; my $sofilter = $cap->{ sofilter }; if ($sofilter) { my %vars = map { $_ => 1 } $sofilter->referenced_variables; my $runnable = 1; if ($vars{ subject }) { unless ($triple->subject) { $l->debug( "triple pattern doesn't match the subject filter" ); $runnable = 0; } } if ($vars{ object }) { unless ($triple->object) { $l->debug( "triple pattern doesn't match the object filter" ); $runnable = 0; } } if ($runnable) { my $bridge = RDF::Query->new_bridge; my $bound = { subject => $triple->subject, object => $triple->object }; my $bool = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $filter = RDF::Query::Expression::Function->new( $bool, $sofilter ); # XXX "ASK {}" is just a simple stand-in query allowing us to have a valid query # XXX object to pass to $filter->evaluate below. evaluating a filter really # XXX shouldn't require a query object in this case, since it's not going # XXX to even touch a datastore, but the code needs to be changed to allow for that. my $query = RDF::Query->new("ASK {}"); my $value = $filter->evaluate( $query, $bound ); my $nok = ($value->literal_value eq 'false'); if ($nok) { $l->debug( "triple pattern doesn't match the sofilter" ); $ok = 0; } } } return $ok; } else { # predicate is a variable in the triple pattern. can we matchit based on sparql:pattern? $l->trace("service doesn't handle triple patterns with unbound predicates"); return 0; } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Temporal.pm000644 000765 000024 00000004043 12173312155 017742 0ustar00gregstaff000000 000000 # RDF::Query::Temporal # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Temporal - tSPARQL temporal extensions to the RDF::Query engine. =head1 VERSION This document describes RDF::Query::Temporal version 2.910. =cut package RDF::Query::Temporal; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query); use Scalar::Util qw(blessed); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =begin private =item C \%bound, triples => \@triples )> Called by C to handle TIME graph query patterns. =end private =cut sub query_more_time { my $self = shift; my %args = @_; if ($args{quad}) { throw RDF::Query::Error::QueryPatternError ( -text => "Can't use nested temporal queries" ); } my $triples = delete($args{triples}); my @triples = @{$triples}; my $triple = shift(@triples); return $self->query_more( triples => [ $triple->pattern ], %args, quad => $triple->interval ); } =begin private =item C Called by fixup() with individual graph patterns. Returns a list of sub-patterns that may need fixing up. =end private =cut sub fixup_pattern { my $self = shift; my $triple = shift; my $bridge = $self->{bridge}; if ($triple->isa('RDF::Query::Algebra::TimeGraph')) { my @triples; push(@triples, $triple->pattern); push(@triples, $triple->time_triples); use Data::Dumper; warn Dumper(\@triples); if ($triple->interval->isa('RDF::Query::Node::Resource')) { $triple->interval( $bridge->new_resource( $triple->interval->uri_value ) ); } elsif ($triple->interval->isa('RDF::Query::Node::Variable')) { my $var = $triple->interval->name; $self->{ known_variables_hash }{ $var }++ } return @triples; } else { return $self->SUPER::fixup_pattern( $triple ); } } 1; __END__ =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Util.pm000644 000765 000024 00000026270 12173312155 017102 0ustar00gregstaff000000 000000 # RDF::Query::Util # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Util - Miscellaneous utility functions to support work with RDF::Query. =head1 VERSION This document describes RDF::Query::Util version 2.910. =head1 SYNOPSIS use RDF::Query::Util; my $query = &RDF::Query::Util::cli_make_query; my $model = &RDF::Query::Util::cli_make_model; $query->execute( $model ); ... =head1 FUNCTIONS =over 4 =cut package RDF::Query::Util; use strict; use warnings; no warnings 'redefine'; use Carp qw(carp croak confess); use URI::file; use RDF::Query; use LWP::Simple; use File::Spec; use JSON; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### our $PREFIXES = <<"END"; PREFIX rdf: PREFIX rdfs: PREFIX owl: PREFIX xsd: PREFIX air: PREFIX bibtex: PREFIX bio: PREFIX book: PREFIX contact: PREFIX cyc: PREFIX dc: PREFIX dcterms: PREFIX foaf: PREFIX geo: PREFIX ical: PREFIX lang: PREFIX likes: PREFIX quaff: PREFIX rel: PREFIX trust: PREFIX visit: PREFIX whois: PREFIX wn: PREFIX wot: END { my $file = File::Spec->catfile($ENV{HOME}, '.prefix-cmd', 'prefixes.json'); if (-r $file) { my $json = do { local($/) = undef; open( my $fh, '<', $file ) or next; <$fh> }; my $prefixes = from_json($json); $PREFIXES = join("\n", map { "PREFIX $_: <" . $prefixes->{$_} . ">" } (keys %$prefixes)); } } =item C<< cli_make_query_and_model >> Returns a query object, model, and args HASHref based on the arguments in @ARGV. These arguments are parsed using C<< cli_make_query >> and C<< make_model >>. =cut sub cli_make_query_and_model { my ($query, $args) = cli_make_query(); my $model = make_model( $args, @ARGV ); return ($query, $model, $args); } =item C<< cli_make_query >> Returns a RDF::Query object based on the arguments in @ARGV. These arguments are parsed using C<< &cli_parse_args >>. If the -e flag is not present, the query will be loaded from a file named by the argument in @ARGV immediately following the final argument parsed by C<< &cli_parse_args >>. =cut sub cli_make_query { my %args = cli_parse_args(@_); my $class = delete $args{ class } || 'RDF::Query'; my $sparql = delete $args{ query }; my $l = Log::Log4perl->get_logger("rdf.query.util"); $l->debug("creating sparql query with class $class"); my $query = $class->new( $sparql, \%args ); if (wantarray) { return ($query, \%args); } else { return $query; } } =item C<< cli_make_model >> Calls C<< make_model >> with arguments from C<< @ARGV >>, returning the constructed model object. C<< cli_make_model >> will usually be called after cli_make_query, allowing a typical CLI invocation to look like `prog.pl [flags] [query file] [data files]`. =cut sub cli_make_model { my %args; my @files; while (scalar(@ARGV) and $ARGV[0] ne '--') { while (scalar(@ARGV) and $ARGV[0] =~ /^-(\w)$/) { my $opt = shift(@ARGV); if ($opt eq '-s') { my $server = shift(@ARGV); if ($server eq 'mysql') { $args{ dsn } = "DBI:mysql:database="; } elsif ($server eq 'sqlite') { $args{ dsn } = "DBI:SQLite:dbname="; } elsif ($server eq 'pg') { $args{ dsn } = "DBI:Pg:dbname="; } } elsif ($opt eq '-d') { $args{ dbname } = shift(@ARGV); } elsif ($opt eq '-u') { $args{ user } = shift(@ARGV); } elsif ($opt eq '-p') { $args{ pass } = shift(@ARGV); } elsif ($opt eq '-m') { $args{ model } = shift(@ARGV); } elsif ($opt eq '--') { last; } } if (@ARGV) { my $file = shift(@ARGV); push(@files, $file); } } if (scalar(@ARGV) and $ARGV[0] eq '--') { shift(@ARGV); } return make_model( \%args, @files ); } =item C<< make_model ( @files ) >> Returns a model object suitable for use in a call to C<< $query->execute >>, loaded with RDF from files and/or URLs listed in @files. This model may be any of the supported models, but as currently implemented will be a RDF::Trine::Model object. =cut sub make_model { my $args = shift; my %args = %$args; my $l = Log::Log4perl->get_logger("rdf.query.util"); while (scalar(@_) and $_[0] =~ m/^-(.)/) { shift; } if ($args{ dsn } and $args{ user } and $args{ pass } and $args{ model } and $args{ dbname }) { $args{ dsn } .= $args{ dbname }; my $store = RDF::Trine::Store::DBI->new($args{ model }, $args{ dsn }, $args{ user }, $args{ pass }); my $model = RDF::Trine::Model->new($store); return $model; } else { # create a temporary triplestore, and wrap it into a model my $model = RDF::Trine::Model->temporary_model; # read in the list of files with RDF/XML content for querying my @files = @_; # loop over all the files foreach my $i (0 .. $#files) { my $file = $files[ $i ]; my $pclass = RDF::Trine::Parser->guess_parser_by_filename( $file ) || 'RDF::Trine::Parser::RDFXML'; my $parser = $pclass->new(); if ($file =~ m<^https?:\/\/>) { $l->debug("fetching RDF from $file ..."); $parser->parse_url_into_model( $file, $model ); } else { $file = File::Spec->rel2abs( $file ); # $uri is the URI object used as the base uri for parsing my $uri = URI::file->new_abs( $file ); my $content = do { open( my $fh, '<', $file ); local($/) = undef; <$fh> }; $parser->parse_into_model( $uri, $content, $model ); } } return $model; } } =item C<< cli_parse_args >> Parses CLI arguments from @ARGV and returns a HASH with the recognized key/values. The allowable arguments are listed below. =cut sub cli_parse_args { my %args = @_; $args{ class } = 'RDF::Query'; my @service_descriptions; while (scalar(@ARGV) and $ARGV[0] =~ /^-(\w+)/) { my $opt = shift(@ARGV); if ($opt eq '-e') { $args{ query } = shift(@ARGV); } elsif ($opt eq '-l') { $args{ lang } = shift(@ARGV); } elsif ($opt eq '-O') { $args{ optimize } = 1; } elsif ($opt eq '-o') { $args{ force_no_optimization } = 1; } elsif ($opt eq '-k') { $args{ canonicalize } = 1; } elsif ($opt eq '-C') { my $k = shift(@ARGV); my $v = shift(@ARGV); $args{ $k } = $v; } elsif ($opt eq '-c') { my $class = shift(@ARGV); eval "require $class"; $args{ class } = $class; } elsif ($opt eq '-f') { require RDF::Query::Federate; $args{ class } = 'RDF::Query::Federate'; } elsif ($opt eq '-F') { require RDF::Query::Federate; require RDF::Query::ServiceDescription; $args{ class } = 'RDF::Query::Federate'; my $url_string = shift(@ARGV); my $uri; if ($url_string =~ m<^https?:\/\/>) { $uri = URI->new( $url_string ); } else { $uri = URI::file->new_abs( $url_string ); } my $sd = RDF::Query::ServiceDescription->new_from_uri( $uri ); push(@service_descriptions, $sd); } elsif ($opt eq '-E') { require RDF::Query::Federate; require RDF::Query::ServiceDescription; $args{ class } = 'RDF::Query::Federate'; my $service_url = shift(@ARGV); my $sd = RDF::Query::ServiceDescription->new( $service_url ); push(@service_descriptions, $sd); } elsif ($opt =~ /^-D([^=]+)(=(.+))?/) { $args{ defines }{ $1 } = (defined($2) ? $3 : 1); } elsif ($opt eq '-N') { $args{ declare_namespaces } = 1; } elsif ($opt eq '-s') { my $server = shift(@ARGV); if ($server eq 'mysql') { $args{ dsn } = "DBI:mysql:database="; } elsif ($server eq 'sqlite') { $args{ dsn } = "DBI:SQLite:dbname="; } elsif ($server eq 'pg') { $args{ dsn } = "DBI:Pg:dbname="; } } elsif ($opt eq '-d') { $args{ dbname } = shift(@ARGV); } elsif ($opt eq '-u') { $args{ user } = shift(@ARGV); } elsif ($opt eq '-p') { $args{ pass } = shift(@ARGV); } elsif ($opt eq '-m') { $args{ model } = shift(@ARGV); } elsif ($opt eq '-w') { if (exists($args{update}) and not($args{update})) { warn "Model requested to be both read-only and read-write.\n"; } else { $args{ update } = 1; } } elsif ($opt eq '-r') { if (exists($args{update}) and $args{update}) { warn "Model requested to be both read-only and read-write.\n"; } else { $args{ update } = 0; } } elsif ($opt eq '--') { last; } } if (@service_descriptions) { $args{ service_descriptions } = \@service_descriptions; } unless (defined($args{query})) { if (@ARGV) { my $file = shift(@ARGV); my $sparql = ($file eq '-') ? do { local($/) = undef; <> } : do { local($/) = undef; open(my $fh, '<', $file) || croak $!; binmode($fh, ':utf8'); <$fh> }; $args{ query } = $sparql; } } if ($args{ query }) { if (delete $args{ declare_namespaces }) { $args{ query } = join('', $PREFIXES, $args{ query } ); } } return %args; } =item C<< start_endpoint ( $model, $port ) >> Starts an SPARQL endpoint HTTP server on port $port. If called in list context, returns the PID and the actual port the server bound to. If called in scalar context, returns only the port. =cut sub start_endpoint { my $model = shift; my $port = shift; my $path = shift; require CGI; require RDF::Endpoint::Server; local($ENV{TMPDIR}) = '/tmp'; my $cgi = CGI->new; my $s = RDF::Endpoint::Server->new_with_model( $model, Port => $port, Prefix => '', CGI => $cgi, IncludePath => $path, ); my $pid = $s->background(); # warn "Endpoint started as [$pid]\n"; if (wantarray) { return ($pid, $port); } else { return $port; } } 1; __END__ =back =head1 COMMAND LINE ARGUMENTS =over 4 =item -e I Specifies the query string I. =item -l I Specifies the query language I used. This should be one of: B, B, or B. =item -O Turns on optimization. =item -o Turns off optimization. =item -c I Specifies the perl I used to construct the query object. Defaults to C<< RDF::Query >>. =item -f Implies -c B. =item -F I Specifies the URL or path to a file I which contains an RDF service description. The described service is used as an underlying triplestore for query answering. Implies -f. =item -E I Specifies the URL of a remove SPARQL endpoint to be used as a data source. The endpoint is used as an underlying triplestore for query answering. Implies -f. =item -s I Specifies the database type to use for the underlying data model. =item -u I =item -p I =item -m I =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/VariableBindings.pm000644 000765 000024 00000003736 12173312155 021372 0ustar00gregstaff000000 000000 # RDF::Query::VariableBindings # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::VariableBindings - Variable bindings =head1 VERSION This document describes RDF::Query::VariableBindings version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::VariableBindings; use strict; use warnings; use base qw(RDF::Trine::VariableBindings); use overload '""' => sub { $_[0]->as_string }, 'bool' => sub { return 1 }; use Scalar::Util qw(blessed refaddr); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( \%bindings ) >> =cut sub new { my $class = shift; my $bindings = shift || {}; my $data = { %$bindings }; foreach my $k (keys %$data) { my $node = $data->{$k}; if (ref($node) and not($node->isa('RDF::Query::Node'))) { $data->{$k} = RDF::Query::Node->from_trine( $node ); } } my $self = $class->SUPER::new( $data ); return $self; } =item C<< sse ( \%context, $indent ) >> =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $more = ' '; my @keys = sort keys %$self; return sprintf('(row %s)', CORE::join(' ', map { '[' . CORE::join(' ', '?' . $_, ($self->{$_}) ? $self->{$_}->as_string : ()) . ']' } (@keys))); } =item C<< explain >> Returns a string serialization of the variable bindings appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}Variable Bindings\n"; my @keys = sort keys %$self; foreach my $k (@keys) { $string .= "${indent}${s}$k: " . $self->{$k}->as_string . "\n"; } return $string; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Aggregate.pm000644 000765 000024 00000042762 12173312155 020751 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Aggregate # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Aggregate - Executable query plan for Aggregates. =head1 VERSION This document describes RDF::Query::Plan::Aggregate version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Aggregate; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed); use RDF::Query::Error qw(:try); use RDF::Query::Node qw(literal); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $pattern, \@group_by, expressions => [ [ $alias, $op, \%options, @attributes ], ... ] ) >> =cut sub new { my $class = shift; my $plan = shift; my $groupby = shift; my %args = @_; my @ops = @{ $args{ 'expressions' } || [] }; my $self = $class->SUPER::new( $plan, $groupby, \@ops ); $self->[0]{referenced_variables} = [ RDF::Query::_uniq( $plan->referenced_variables, map { ($_->isa('RDF::Query::Node::Variable')) ? $_->name : $_->isa('RDF::Query::Node') ? () : $_->referenced_variables } @$groupby) ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "AGGREGATE plan can't be executed while already open"; } my $plan = $self->[1]; $plan->execute( $context ); my $l = Log::Log4perl->get_logger("rdf.query.plan.aggregate"); if ($plan->state == $self->OPEN) { my $query = $context->query; my $bridge = $context->model; my %seen; my %groups; my %group_data; my @groupby = $self->groupby; my @ops = @{ $self->[3] }; local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; ROW: while (my $row = $plan->next) { $l->debug("aggregate on $row"); my @group; foreach my $g (@groupby) { my $v = $query->var_or_expr_value( $row, $g, $context ); if ($g->isa('RDF::Query::Expression::Alias')) { $row->{ $g->name } = $v; } push(@group, $v); } # my @group = map { $query->var_or_expr_value( $row, $_ ) } @groupby; my $group = join('<<<', map { blessed($_) ? $_->as_string : '' } map { blessed($_) ? $query->var_or_expr_value( $row, $_, $context ) : '' } @group); push( @{ $group_data{ 'rows' }{ $group } }, $row ); $group_data{ 'groups' }{ $group } = \@group; foreach my $i (0 .. $#groupby) { my $g = $groupby[$i]; $group_data{ 'groupby_sample' }{ $group } = $row; } } my @groups = values %{ $group_data{'groups'} }; if (scalar(@groups) == 0) { $group_data{'rows'}{''} = []; $group_data{'groups'}{''} = []; } my @rows; GROUP: foreach my $group (keys %{ $group_data{ 'rows' } }) { $l->debug( "group: $group" ); my %options; my %aggregates; my %passthrough_data; my @group = @{ $group_data{ 'groups' }{ $group } }; my $row_sample = $group_data{ 'groupby_sample' }{ $group }; foreach my $g (@groupby) { if ($g->isa('RDF::Query::Expression::Alias') or $g->isa('RDF::Query::Node::Variable')) { my $name = $g->name; $passthrough_data{ $name } = $row_sample->{ $name }; } elsif ($g->isa('RDF::Query::Expression')) { my @names = $g->referenced_variables; foreach my $name (@names) { $passthrough_data{ $name } = $row_sample->{ $name }; } } else { my $name = $g->sse; $passthrough_data{ $name } = $row_sample->{ $name }; } } my @operation_data = (map { [ @{ $_ }, \%aggregates ] } @ops); foreach my $data (@operation_data) { my $aggregate_data = pop(@$data); my ($alias, $op, $opts, @cols) = @$data; $options{ $alias } = $opts; my $distinct = ($op =~ /^(.*)-DISTINCT$/); $op =~ s/-DISTINCT$//; my $col = $cols[0]; my %agg_group_seen; try { foreach my $row (@{ $group_data{ 'rows' }{ $group } }) { my @proj_rows = map { (blessed($col)) ? $query->var_or_expr_value( $row, $col, $context ) : '*' } @cols; if ($distinct) { next if ($agg_group_seen{ join('<<<', @proj_rows) }++); } $l->debug( "- row: $row" ); # $groups{ $group } ||= { map { $_ => $row->{ $_ } } @groupby }; if ($op eq 'COUNT') { $l->debug("- aggregate op: COUNT"); my $should_inc = 0; if (not(blessed($col)) and $col eq '*') { $should_inc = 1; } else { my $value = $query->var_or_expr_value( $row, $col, $context ); $should_inc = (defined $value) ? 1 : 0; } $aggregate_data->{ $alias }{ $group }[0] = $op; $aggregate_data->{ $alias }{ $group }[1] += $should_inc; } elsif ($op eq 'SUM') { $l->debug("- aggregate op: SUM"); my $value = $query->var_or_expr_value( $row, $col, $context ); my $type = _node_type( $value ); $aggregate_data->{ $alias }{ $group }[0] = $op; my $strict = 1; # if ($value->isa('RDF::Query::Node::Literal')) { my $v = $value->numeric_value; if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) { if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) { if ($context->strict_errors) { delete $aggregate_data->{ $alias }{ $group }; throw RDF::Query::Error::ComparisonError -text => "Cannot compute SUM aggregate over nodes of multiple, non-numeric types"; } else { $strict = 0; } } $aggregate_data->{ $alias }{ $group }[1] += $v; $aggregate_data->{ $alias }{ $group }[2] = RDF::Query::Expression::Binary->promote_type('+', $type, $aggregate_data->{ $alias }{ $group }[2]); } else { $aggregate_data->{ $alias }{ $group }[1] = $v; $aggregate_data->{ $alias }{ $group }[2] = $type; } # } } elsif ($op eq 'MAX') { $l->debug("- aggregate op: MAX"); my $value = $query->var_or_expr_value( $row, $col, $context ); my $type = _node_type( $value ); $aggregate_data->{ $alias }{ $group }[0] = $op; my $strict = 1; if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) { if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) { if ($context->strict_errors) { delete $aggregate_data->{ $alias }{ $group }; throw RDF::Query::Error::ComparisonError -text => "Cannot compute MAX aggregate over nodes of multiple, non-numeric types"; } else { $strict = 0; } } if ($strict) { if ($value > $aggregate_data->{ $alias }{ $group }[1]) { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } else { if ("$value" gt "$aggregate_data->{ $alias }{ $group }[1]") { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } } else { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } elsif ($op eq 'MIN') { $l->debug("- aggregate op: MIN"); my $value = $query->var_or_expr_value( $row, $col, $context ); my $type = _node_type( $value ); $aggregate_data->{ $alias }{ $group }[0] = $op; my $strict = 1; if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) { if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) { if ($context->strict_errors) { delete $aggregate_data->{ $alias }{ $group }; throw RDF::Query::Error::ComparisonError -text => "Cannot compute MIN aggregate over nodes of multiple, non-numeric types"; } else { $strict = 0; } } if ($strict) { if ($value < $aggregate_data->{ $alias }{ $group }[1]) { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } else { if ("$value" lt "$aggregate_data->{ $alias }{ $group }[1]") { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } } else { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } elsif ($op eq 'SAMPLE') { ### this is just the MIN code from above, without the strict comparison checking $l->debug("- aggregate op: SAMPLE"); my $value = $query->var_or_expr_value( $row, $col, $context ); my $type = _node_type( $value ); $aggregate_data->{ $alias }{ $group }[0] = $op; if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) { if ("$value" lt "$aggregate_data->{ $alias }{ $group }[1]") { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } else { $aggregate_data->{ $alias }{ $group }[1] = $value; $aggregate_data->{ $alias }{ $group }[2] = $type; } } elsif ($op eq 'AVG') { $l->debug("- aggregate op: AVG"); my $value = $query->var_or_expr_value( $row, $col, $context ); my $type = _node_type( $value ); # warn "AVG\t$group\t" . $value->as_string . "\n"; $aggregate_data->{ $alias }{ $group }[0] = $op; unless (blessed($value) and $value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type) { delete $aggregate_data->{ $alias }{ $group }; throw RDF::Query::Error::ComparisonError -text => "Cannot compute AVG aggregate over non-numeric nodes"; } if (blessed($value) and $value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type) { $aggregate_data->{ $alias }{ $group }[1]++; $aggregate_data->{ $alias }{ $group }[2] += $value->numeric_value; if ($aggregate_data->{ $alias }{ $group }[3]) { $aggregate_data->{ $alias }{ $group }[3] = RDF::Query::Expression::Binary->promote_type('+', $type, $aggregate_data->{ $alias }{ $group }[3]); } else { $aggregate_data->{ $alias }{ $group }[3] = $type; } } } elsif ($op eq 'GROUP_CONCAT') { $l->debug("- aggregate op: GROUP_CONCAT"); $aggregate_data->{ $alias }{ $group }[0] = $op; my $str = RDF::Query::Node::Resource->new('sparql:str'); my @values = map { my $expr = RDF::Query::Expression::Function->new( $str, $query->var_or_expr_value( $row, $_, $context ) ); my $val = $expr->evaluate( $context->query, $row ); blessed($val) ? $val->literal_value : ''; } @cols; # warn "adding '$string' to group_concat aggregate"; push( @{ $aggregate_data->{ $alias }{ $group }[1] }, @values ); } else { throw RDF::Query::Error -text => "Unknown aggregate operator $op"; } } } catch RDF::Query::Error::ComparisonError with { delete $aggregate_data->{ $alias }{ $group }; } } my %row = %passthrough_data; foreach my $agg (keys %aggregates) { if (defined($aggregates{$agg}{$group})) { my $op = $aggregates{ $agg }{ $group }[0]; if ($op eq 'AVG') { my $value = ($aggregates{ $agg }{ $group }[2] / $aggregates{ $agg }{ $group }[1]); my $type = $aggregates{ $agg }{ $group }[3]; if ($type eq 'http://www.w3.org/2001/XMLSchema#integer') { $type = 'http://www.w3.org/2001/XMLSchema#decimal'; } $row{ $agg } = (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, $type, 1 ); } elsif ($op eq 'GROUP_CONCAT') { my $j = (exists $options{$agg}{seperator}) ? $options{$agg}{seperator} : ' '; $row{ $agg } = RDF::Query::Node::Literal->new( join($j, @{ $aggregates{ $agg }{ $group }[1] }) ); } elsif ($op =~ /COUNT/) { my $value = $aggregates{ $agg }{ $group }[1]; $row{ $agg } = (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#integer', 1 ); } else { if (defined($aggregates{$agg}{$group})) { my $value = $aggregates{ $agg }{ $group }[1]; $row{ $agg } = (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, $aggregates{ $agg }{ $group }[2], 1 ); } } } } my $vars = RDF::Query::VariableBindings->new( \%row ); $l->debug("aggregate row: $vars"); push(@rows, $vars); } $self->[0]{rows} = \@rows; $self->state( $self->OPEN ); } else { warn "could not execute plan in distinct"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open AGGREGATE"; } my $bindings = shift(@{ $self->[0]{rows} }); if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open AGGREGATE"; } delete $self->[0]{rows}; if (defined($self->[1])) { $self->[1]->close(); } $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the aggregated data. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< groupby >> Returns the grouping arguments that will be used to produce the aggregated data. =cut sub groupby { my $self = shift; return @{ $self->[2] || [] }; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'aggregate'; } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->pattern, $self->groupby); } =item C<< sse ( $context, $indent ) >> =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $more = ' '; my $psse = $self->pattern->sse( $context, "${indent}${more}" ); my @group = map { $_->sse($context, "${indent}${more}") } $self->groupby; my $gsse = join(' ', @group); my @ops; foreach my $p (@{ $self->[3] }) { my ($alias, $op, $options, @cols) = @$p; my $cols = '(' . join(' ', map { ref($_) ? $_->sse($context, "${indent}${more}") : '*' } @cols) . ')'; my @opts_keys = keys %$options; if (@opts_keys) { my $opt_string = '(' . join(' ', map { $_, qq["$options->{$_}"] } @opts_keys) . ')'; push(@ops, qq[("$alias" "$op" $cols $opt_string)]); } else { push(@ops, qq[("$alias" "$op" $cols)]); } } my $osse = join(' ', @ops); return sprintf( "(aggregate\n${indent}${more}%s\n${indent}${more}(%s)\n${indent}${more}(%s))", $psse, $gsse, $osse, ); } # =item C<< plan_prototype >> # # Returns a list of scalar identifiers for the type of the content (children) # nodes of this plan node. See L for a list of the allowable # identifiers. # # =cut # # sub plan_prototype { # my $self = shift; # return qw(P \E *\ssW); # } # # =item C<< plan_node_data >> # # Returns the data for this plan node that corresponds to the values described by # the signature returned by C<< plan_prototype >>. # # =cut # # sub plan_node_data { # my $self = shift; # my @group = $self->groupby; # my @ops = @{ $self->[3] }; # return ($self->pattern, \@group, map { [@$_] } @ops); # } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; my $sort = [ $self->groupby ]; return []; # XXX aggregates are actually sorted, so figure out what should go here... } sub _node_type { my $node = shift; if (blessed($node)) { if ($node->isa('RDF::Query::Node::Literal')) { if (my $type = $node->literal_datatype) { return $type; } else { return 'literal'; } } elsif ($node->isa('RDF::Query::Node::Resource')) { return 'resource'; } elsif ($node->isa('RDF::Query::Node::Blank')) { return 'blank'; } else { return ''; } } else { return ''; } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/BasicGraphPattern.pm000644 000765 000024 00000012243 12173312155 022413 0ustar00gregstaff000000 000000 # RDF::Query::Plan::BasicGraphPattern # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::BasicGraphPattern - Executable query plan for BasicGraphPatterns. =head1 VERSION This document describes RDF::Query::Plan::BasicGraphPattern version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::BasicGraphPattern; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed); use RDF::Trine::Statement; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @triples ) >> =cut sub new { my $class = shift; my @triples = map { my @nodes = $_->nodes; $nodes[3] ||= RDF::Trine::Node::Nil->new(); (scalar(@nodes) == 4) ? RDF::Trine::Statement::Quad->new( @nodes ) : RDF::Trine::Statement->new( @nodes ) } @_; my @vars = map { $_->name } grep { $_->isa('RDF::Trine::Node::Variable') } map { $_->nodes } @triples; my @uvars = keys %{ { map { $_ => 1 } @vars } }; my $self = $class->SUPER::new( \@triples ); $self->[0]{referenced_variables} = \@uvars; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "BGP plan can't be executed twice"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.basicgraphpattern"); $l->trace( "executing RDF::Query::Plan::BasicGraphPattern" ); my @bound_triples; my $bound = $context->bound; if (%$bound) { $self->[0]{bound} = $bound; my @triples = @{ $self->[1] }; foreach my $j (0 .. $#triples) { my @nodes = $triples[$j]->nodes; foreach my $i (0 .. $#nodes) { next unless ($nodes[$i]->isa('RDF::Trine::Node::Variable')); next unless (blessed($bound->{ $nodes[$i]->name })); # warn "pre-bound variable found: " . $nodes[$i]->name; $nodes[$i] = $bound->{ $nodes[$i]->name }; } my $triple = (scalar(@nodes) == 4) ? RDF::Trine::Statement::Quad->new( @nodes ) : RDF::Trine::Statement->new( @nodes ); push(@bound_triples, $triple); } } else { @bound_triples = @{ $self->[1] }; } my @tmp = grep { $_->isa('RDF::Trine::Statement::Quad') and $_->context->isa('RDF::Trine::Node::Variable') } @bound_triples; my $quad = scalar(@tmp) ? $tmp[0]->context : undef; my $model = $context->model; my $pattern = RDF::Trine::Pattern->new( @bound_triples ); $l->trace( "BGP: " . $pattern->sse ); my $iter = $model->get_pattern( $pattern ); if (blessed($iter)) { $self->[0]{iter} = $iter; $self->[0]{quad} = $quad; $self->[0]{nil} = RDF::Trine::Node::Nil->new(); $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open BGP"; } my $q = $self->[0]{quad}; my $iter = $self->[0]{iter}; return undef unless ($iter); while (ref(my $row = $iter->next)) { if (ref(my $bound = $self->[0]{bound})) { @{ $row }{ keys %$bound } = values %$bound; } if (blessed($q)) { # skip results when we were matching over variable named graphs (GRAPH ?g {...}) # and where the graph variable is bound to the nil node # (the nil node is used to represent the default graph, which should never match inside a GRAPH block). my $node = $row->{ $q->name }; if (blessed($node)) { next if ($node->isa('RDF::Trine::Node::Nil')); } } my $result = RDF::Query::VariableBindings->new( $row ); if (my $d = $self->delegate) { $d->log_result( $self, $result ); } return $result; } return; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open BGP"; } delete $self->[0]{iter}; $self->SUPER::close(); } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'bgp'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(*T); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my @triples = @{ $self->[1] }; return @triples; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Clear.pm000644 000765 000024 00000011544 12173312155 020103 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Clear # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Clear - Executable query plan for CLEAR operations. =head1 VERSION This document describes RDF::Query::Plan::Clear version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Clear; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $graph ) >> =cut sub new { my $class = shift; my $graph = shift; my $self = $class->SUPER::new( $graph ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "CLEAR plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.clear"); $l->trace( "executing RDF::Query::Plan::Clear" ); my %args = ($self->namedgraph) ? (context => $self->namedgraph) : (); my $graph = $self->namedgraph; unless ($graph) { $graph = RDF::Trine::Node::Nil->new; } # warn "clearing graph " . $graph->as_string; my $ok = 0; try { if ($graph->is_nil) { $context->model->remove_statements( undef, undef, undef, $graph ); } else { my $uri = $graph->uri_value; if ($uri eq 'tag:gwilliams@cpan.org,2010-01-01:RT:ALL') { $context->model->remove_statements( undef, undef, undef, undef ); } elsif ($uri eq 'tag:gwilliams@cpan.org,2010-01-01:RT:NAMED') { my $citer = $context->model->get_graphs; while (my $graph = $citer->next) { $context->model->remove_statements( undef, undef, undef, $graph ); } } else { $context->model->remove_statements( undef, undef, undef, $graph ); } } $ok = 1; } catch RDF::Trine::Error with {}; $self->[0]{ok} = $ok; $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open CLEAR"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.clear"); $self->close(); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{ok} ); } return $self->[0]{ok}; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open CLEAR"; } delete $self->[0]{ok}; $self->SUPER::close(); } =item C<< namedgraph >> Returns the graph node which is to be cleared. =cut sub namedgraph { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'clear'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; my $g = $self->namedgraph; if ($g->isa('RDF::Query::Node::Resource') and $g->uri_value =~ m'^tag:gwilliams@cpan[.]org,2010-01-01:RT:(NAMED|ALL)$') { return qw(w); } else { return qw(N); } } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $g = $self->namedgraph; if ($g->isa('RDF::Query::Node::Resource') and $g->uri_value =~ m'^tag:gwilliams@cpan[.]org,2010-01-01:RT:(NAMED|ALL)$') { return $1; } else { return ($self->namedgraph); } } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; my $url = $self->namedgraph->uri_value; $g->add_node( "$self", label => "Clear" . $self->graph_labels ); $g->add_node( "${self}$url", label => $url ); $g->add_edge( "$self" => "${self}$url", label => 'url' ); return "$self"; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/ComputedStatement.pm000644 000765 000024 00000017106 12173312155 022522 0ustar00gregstaff000000 000000 # RDF::Query::Plan::ComputedStatement # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::ComputedStatement - Executable query plan for computed triples. =head1 VERSION This document describes RDF::Query::Plan::ComputedStatement version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::ComputedStatement; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @triple ) >> =cut sub new { my $class = shift; my @nodes = splice(@_, 0, 4); my $quad = shift; my $keys = shift || {}; my $self = $class->SUPER::new( \@nodes, $quad ); $self->[0]{logging_keys} = $keys; my %var_to_position; my @methodmap = qw(subject predicate object); my %counts; my $dup_var; foreach my $idx (0 .. 3) { my $node = $nodes[ $idx ]; if (blessed($node) and $node->isa('RDF::Trine::Node::Variable')) { my $name = $node->name; $var_to_position{ $name } = $methodmap[ $idx ]; $counts{ $name }++; if ($counts{ $name } >= 2) { $dup_var = $name; } } } $self->[0]{referenced_variables} = [ keys %counts ]; my @positions; if (defined($dup_var)) { foreach my $idx (0 .. 2) { my $var = $nodes[ $idx ]; if (blessed($var) and $var->isa('RDF::Trine::Node::Variable')) { my $name = $var->name; if ($name eq $dup_var) { push(@positions, $methodmap[ $idx ]); } } } } $self->[0]{mappings} = \%var_to_position; if (@positions) { $self->[0]{dups} = \@positions; } return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "COMPUTEDSTATEMENT plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.computedstatement"); $l->trace( "executing RDF::Query::Plan::ComputedStatement" ); $self->[0]{start_time} = [gettimeofday]; my @nodes = @{ $self->[1] }; unless ($self->[2]) { pop(@nodes); } my $bound = $context->bound; if (%$bound) { foreach my $i (0 .. $#nodes) { next unless ($nodes[$i]->isa('RDF::Trine::Node::Variable')); next unless (blessed($bound->{ $nodes[$i]->name })); $nodes[ $i ] = $bound->{ $nodes[$i]->name }; } } $l->trace( "computed statement pattern after pre-binding: " . join(' ', map { $_->as_string } @nodes)); my $query = $context->query; my $csg = $query->get_computed_statement_generators( $nodes[1]->uri_value ); unless (scalar(@$csg)) { throw RDF::Query::Error::ExecutionError -text => "No computed statement generator found for predicate " . $nodes[1]->uri_value; } my $iter; { local($query->{model}) = $context->model; $iter = $csg->[0]->( $query, $bound, @nodes ); } if (blessed($iter)) { $self->[0]{iter} = $iter; $self->[0]{bound} = $bound; $self->[0]{logger} = $context->logger; $self->[0]{count} = 0; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open COMPUTEDSTATEMENT"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.computedstatement"); my $iter = $self->[0]{iter}; LOOP: while (my $row = $iter->next) { if ($l->is_trace) { $l->trace( "- got triple from model: " . $row->as_string ); } if (my $pos = $self->[0]{dups}) { $l->trace( "- checking for duplicate variables in triple" ); my @pos = @$pos; my $first_method = shift(@pos); my $first = $row->$first_method(); foreach my $p (@pos) { unless ($first->equal( $row->$p() )) { next LOOP; } } } my $binding = {}; foreach my $key (keys %{ $self->[0]{mappings} }) { my $method = $self->[0]{mappings}{ $key }; $binding->{ $key } = $row->$method(); } my $pre_bound = $self->[0]{bound}; my $bindings = RDF::Query::VariableBindings->new( $binding ); if ($row->can('label')) { if (my $o = $row->label('origin')) { $bindings->label( origin => [ $o ] ); } } @{ $bindings }{ keys %$pre_bound } = values %$pre_bound; $self->[0]{count}++; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } return; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open TRIPLE"; } # my $l = Log::Log4perl->get_logger("rdf.query.plan.computedstatement"); my $t0 = delete $self->[0]{start_time}; my $count = delete $self->[0]{count}; delete $self->[0]{iter}; $self->SUPER::close(); } =item C<< nodes >> Returns a list of the three node objects that comprise the triple pattern this plan will return. =cut sub nodes { my $self = shift; if ($self->[2]) { return @{ $self->[1] }[0..3]; } else { return @{ $self->[1] }[0..2]; } } =item C<< triple >> Returns a RDF::Trine::Statement object representing the triple pattern this plan will return. =cut sub triple { my $self = shift; my @nodes = $self->nodes; if ($self->[2]) { return RDF::Trine::Statement::Quad->new( @nodes ); } else { return RDF::Trine::Statement->new( @nodes ); } } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $context = shift; my $bf = ''; my $bound = $context->bound; foreach my $n (@{ $self->[1] }[0..3]) { if ($n->isa('RDF::Trine::Node::Variable')) { if (my $b = $bound->{ $n->name }) { $bf .= 'b'; } else { $bf .= 'f'; } } else { $bf .= 'b'; } } return $bf; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'computedstatement'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N N N i); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->nodes); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; $g->add_node( "$self", label => "Computed Statement" . $self->graph_labels ); my @names = qw(subject predicate object graph); foreach my $i (0 .. 3) { my $n = $self->[ $i + 1 ]; my $rel = $names[ $i ]; my $str = $n->sse( {}, '' ); $g->add_node( "${self}$n", label => $str ); $g->add_edge( "$self" => "${self}$n", label => $names[ $i ] ); } return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Constant.pm000644 000765 000024 00000007244 12173312155 020650 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Constant # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Constant - Executable query plan for Constants. =head1 VERSION This document describes RDF::Query::Plan::Constant version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Constant; use strict; use warnings; use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @variable_bindings ) >> =cut sub new { my $class = shift; my @binds = @_; my $self = $class->SUPER::new( \@binds ); $self->[0]{referenced_variables} = [ keys %{ $binds[0] } ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "CONSTANT plan can't be executed while already open"; } $self->[0]{'index'} = 0; $self->state( $self->OPEN ); $self; } =item C<< bindings >> Returns a list of the variable bindings for this constant result set. =cut sub bindings { my $self = shift; my $binds = $self->[1] || []; return @$binds; } =item C<< is_unit >> Returns true if this constant result set is comprised of a single, empty variable binding. =cut sub is_unit { my $self = shift; my @binds = $self->bindings; return 0 unless scalar(@binds) == 1; my @keys = keys %{ $binds[0] }; return not(scalar(@keys)); } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open CONSTANT"; } my $binds = $self->[1]; if ($self->[0]{'index'} > $#{ $binds }) { return; } my $row = $binds->[ $self->[0]{'index'}++ ]; if ($row) { my $bindings = RDF::Query::VariableBindings->new( $row ); if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } else { return; } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open CONSTANT"; } delete $self->[0]{'index'}; $self->SUPER::close(); } =item C<< size >> =cut sub size { my $self = shift; return scalar( @{ $self->[1] } ); } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { # XXX could check constant data to determine if it's unique return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { # XXX could check constant data to determine if it's ordered return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'table'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(*V); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $binds = $self->[1]; return @$binds; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Construct.pm000644 000765 000024 00000014312 12173312155 021035 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Construct # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Construct - Executable query plan for constructing a graph from a set of variable bindings. =head1 VERSION This document describes RDF::Query::Plan::Construct version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Construct; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed refaddr); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, \@triples ) >> =cut sub new { my $class = shift; my $plan = shift; my $triples = shift; unless (@$triples) { throw RDF::Query::Error::MethodInvocationError -text => "No triples passed to ::Plan::Construct constructor"; } my $self = $class->SUPER::new( $plan, $triples ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "CONSTRUCT plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.construct"); $l->trace( "executing RDF::Query::Plan::Construct" ); my $plan = $self->pattern; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->[0]{triples} = []; $self->[0]{blank_map} = {}; $self->state( $self->OPEN ); } else { warn "could not execute plan in construct"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open CONSTRUCT"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.triple"); my $plan = $self->[1]; while (1) { while (scalar(@{ $self->[0]{triples} })) { my $t = shift(@{ $self->[0]{triples} }); if (my $d = $self->delegate) { $d->log_result( $self, $t ); } return $t; } my $row = $plan->next; return undef unless ($row); $self->[0]{blank_map} = {}; if ($l->is_debug) { $l->debug( "- got construct bindings from pattern: " . $row->as_string ); } my $triples = $self->triples; foreach my $t (@$triples) { if ($l->is_debug) { $l->debug( "- filling-in construct triple pattern: " . $t->as_string ); } my @triple = $t->nodes; for my $i (0 .. 2) { if ($triple[$i]->isa('RDF::Trine::Node::Variable')) { my $name = $triple[$i]->name; $triple[$i] = $row->{ $name }; } elsif ($triple[$i]->isa('RDF::Trine::Node::Blank')) { my $id = $triple[$i]->blank_identifier; unless (exists($self->[0]{blank_map}{ $id })) { $self->[0]{blank_map}{ $id } = RDF::Trine::Node::Blank->new(); } $triple[$i] = $self->[0]{blank_map}{ $id }; } } my $ok = 1; foreach (@triple) { if (not blessed($_)) { $ok = 0; } } next unless ($ok); my $st = RDF::Trine::Statement->new( @triple ); unless ($self->[0]{seen}{ $st->as_string }++) { push(@{ $self->[0]{triples} }, $st); } } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open CONSTRUCT"; } delete $self->[0]{blank_map}; delete $self->[0]{triples}; if ($self->[1] and $self->[1]->state == $self->OPEN) { $self->[1]->close(); } $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the variable bindings for constructing the new graph. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< triples >> Returns the triples that are to be used in constructing the new graph for each variable binding. =cut sub triples { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'construct'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P \T); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->pattern, [ @{ $self->triples } ]); } =item C<< explain >> Returns a string serialization of the query plan appropriate for display on the command line. =cut sub explain { my $self = shift; my ($s, $count) = (' ', 0); if (@_) { $s = shift; $count = shift; } my $indent = '' . ($s x $count); my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); $string .= $self->pattern->explain( $s, $count+1 ); $string .= "${indent}${s}pattern\n"; foreach my $t (@{ $self->triples }) { $string .= "${indent}${s}${s}" . $t->as_sparql . "\n"; } return $string; } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); $g->add_node( "$self", label => "Construct" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } =item C<< as_iterator ( $context ) >> Returns an RDF::Trine::Iterator object for the current (already executed) plan. =cut sub as_iterator { my $self = shift; my $context = shift; my $stream = RDF::Trine::Iterator::Graph->new( sub { $self->next } ); return $stream; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Copy.pm000644 000765 000024 00000011063 12173312155 017763 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Copy # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Copy - Executable query plan for COPY operations. =head1 VERSION This document describes RDF::Query::Plan::Copy version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Copy; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $from, $to, $silent ) >> =cut sub new { my $class = shift; my $from = shift; my $to = shift; my $silent = shift; my $self = $class->SUPER::new( $from, $to, $silent ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "COPY plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.copy"); $l->trace( "executing RDF::Query::Plan::Copy" ); my $from = $self->from; my $to = $self->to; # warn "Copying graph " . $from->as_string; my $ok = 0; try { if ($from->equal( $to )) { # no-op } else { my $model = $context->model; $model->begin_bulk_ops(); $model->remove_statements( undef, undef, undef, $to ); my $iter = $model->get_statements( undef, undef, undef, $from ); while (my $st = $iter->next) { $model->add_statement( $st, $to ); } $model->end_bulk_ops(); } $ok = 1; } catch RDF::Trine::Error with {}; $self->[0]{ok} = $ok; $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open COPY"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.copy"); $self->close(); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{ok} ); } return $self->[0]{ok}; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open COPY"; } delete $self->[0]{ok}; $self->SUPER::close(); } =item C<< from >> Returns the graph node which is to be copied. =cut sub from { my $self = shift; return $self->[1]; } =item C<< to >> Returns the graph node to which data is copied. =cut sub to { my $self = shift; return $self->[2]; } =item C<< silent >> Returns the silent flag. =cut sub silent { my $self = shift; return $self->[3]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'copy'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->from, $self->to); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; my $furl = $self->from->uri_value; my $turl = $self->to->uri_value; $g->add_node( "$self", label => "Copy" . $self->graph_labels ); $g->add_node( "${self}$furl", label => $furl ); $g->add_node( "${self}$turl", label => $turl ); $g->add_edge( "$self" => "${self}$furl", label => 'from' ); $g->add_edge( "$self" => "${self}$turl", label => 'to' ); return "$self"; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Distinct.pm000644 000765 000024 00000006770 12173312155 020643 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Distinct # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Distinct - Executable query plan for Distincts. =head1 VERSION This document describes RDF::Query::Plan::Distinct version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Distinct; use strict; use warnings; use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan ) >> =cut sub new { my $class = shift; my $plan = shift; my $self = $class->SUPER::new( $plan ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "DISTINCT plan can't be executed while already open"; } my $plan = $self->[1]; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->[0]{seen} = {}; $self->state( $self->OPEN ); } else { warn "could not execute plan in distinct"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open DISTINCT"; } my $plan = $self->[1]; while (1) { my $row = $plan->next; return undef unless ($row); if (not $self->[0]{seen}{ $row->as_string }++) { if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open DISTINCT"; } delete $self->[0]{seen}; $self->[1]->close(); $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be made distinct. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'distinct'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->pattern); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); $g->add_node( "$self", label => "Distinct" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Extend.pm000644 000765 000024 00000014007 12173312155 020301 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Extend # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Extend - Executable query plan for Extends. =head1 VERSION This document describes RDF::Query::Plan::Extend version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Extend; use strict; use warnings; use base qw(RDF::Query::Plan); use RDF::Query::Error qw(:try); use Scalar::Util qw(blessed refaddr); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, \@keys ) >> =cut sub new { my $class = shift; my $plan = shift; my $keys = shift; my (@vars, @exprs); foreach my $k (@$keys) { push(@exprs, $k) if ($k->isa('RDF::Query::Expression')); push(@vars, $k->name) if ($k->isa('RDF::Query::Node::Variable')); push(@vars, $k) if (not(ref($k))); } my $self = $class->SUPER::new( $plan, \@vars, \@exprs ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; my $l = Log::Log4perl->get_logger("rdf.query.plan.extend"); $l->trace( "executing extend plan: " . $self->sse ); if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "EXTEND plan can't be executed while already open"; } my $plan = $self->[1]; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->[0]{context} = $context; $self->state( $self->OPEN ); } else { warn "could not execute plan in PROJECT"; } $self; } =item C<< next >> =cut sub next { my $self = shift; my $ctx = $self->[0]{context}; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PROJECT"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.extend"); my $plan = $self->[1]; while (1) { my $result = $plan->next; unless (defined($result)) { $l->trace("no remaining rows in extend"); if ($self->[1]->state == $self->[1]->OPEN) { $self->[1]->close(); } return; } my $row = RDF::Query::VariableBindings->new( { %$result } ); if ($l->is_trace) { $l->trace( "extend on row $row" ); } my $keys = $self->[2]; my $exprs = $self->[3]; my $query = $self->[0]{context}->query; local($query->{_query_row_cache}) = {}; # my $proj = $row->project( @{ $keys } ); my $ok = 1; foreach my $e (@$exprs) { my $name = $e->name; my $var_or_expr = $e->expression; if ($l->is_trace) { $l->trace( "- extend alias " . $var_or_expr->sse . " -> $name" ); } try { my $value = $query->var_or_expr_value( $row, $var_or_expr, $ctx ); if ($l->is_trace) { $l->trace( "- extend value $name -> $value" ); } $row->{ $name } = $value; } catch RDF::Query::Error with { $l->trace( "- evaluating extend expression resulted in an error; dropping the variable binding" ); } otherwise { my $e = shift; warn 'exception caught in Extend(): ' . Dumper($e); }; } next unless ($ok); $l->trace( "Extended result: $row" ); if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PROJECT"; } delete $self->[0]{context}; if (blessed($self->[1]) and $self->[1]->state == $self->OPEN) { $self->[1]->close(); } $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be extended. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'extend'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(\J P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my @vars = map { RDF::Query::Node::Variable->new( $_ ) } @{$self->[2]}; my @exprs = @{$self->[3]}; return ([ @vars, @exprs ], $self->pattern); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); my $expr = join(' ', @{$self->[2]}, map { blessed($_) ? $_->sse( {}, "" ) : $_ } @{$self->[3]}); $g->add_node( "$self", label => "Extend ($expr)" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } =item C<< explain >> Returns a string serialization of the plan appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); $string .= "${indent}${s}vars:\n"; my @vars = map { RDF::Query::Node::Variable->new( $_ ) } @{$self->[2]}; my @exprs = @{$self->[3]}; foreach my $e (@vars, @exprs) { $string .= $e->explain($s, $count+2); } $string .= $self->pattern->explain( $s, $count+1 ); return $string; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Filter.pm000644 000765 000024 00000012067 12173312155 020303 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Filter # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Filter - Executable query plan for Filters. =head1 VERSION This document describes RDF::Query::Plan::Filter version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Filter; use strict; use warnings; use base qw(RDF::Query::Plan); use RDF::Query::Error qw(:try); use Scalar::Util qw(blessed); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, $expr, $active_graph ) >> =cut sub new { my $class = shift; my $expr = shift; my $plan = shift; my $graph = shift; my $self = $class->SUPER::new( $expr, $plan, $graph ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "FILTER plan can't be executed while already open"; } my $plan = $self->[2]; $plan->execute( $context ); my $l = Log::Log4perl->get_logger("rdf.query.plan.filter"); if ($plan->state == $self->OPEN) { $self->state( $self->OPEN ); my $expr = $self->[1]; my $bool = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $filter = RDF::Query::Expression::Function->new( $bool, $expr ); if ($l->is_trace) { $l->trace("filter constructed for " . $expr->sse({}, '')); } my $query = $context->query; my $bridge = $context->model; $self->[0]{filter} = sub { my $row = shift; my $bool = 0; try { my $qok = ref($query); local($query->{_query_row_cache}) = {}; unless ($qok) { # $query may not be defined, but the local() call will autovivify it into a HASH. # later on, if it's a ref, somebody's going to try to call a method on it, so # undef it if it wasn't defined before the local() call. $query = undef; } my $value = $filter->evaluate( $query, $row, $context, $self->active_graph ); $bool = ($value->literal_value eq 'true') ? 1 : 0; } catch RDF::Query::Error with { my $e = shift; no warnings 'uninitialized'; $l->debug( 'exception thrown during filter evaluation: ' . $e->text ); } otherwise { $l->debug( 'error during filter evaluation: ' . $@); }; return $bool; }; } else { warn "could not execute plan in filter"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open FILTER"; } my $plan = $self->[2]; my $filter = $self->[0]{filter}; my $l = Log::Log4perl->get_logger("rdf.query.plan.filter"); while (1) { my $row = $plan->next; unless (defined($row)) { $l->debug("no remaining rows in filter"); return; } if ($l->is_trace) { $l->trace("filter processing bindings $row"); } if ($filter->( $row )) { $l->trace( "- filter returned true on row" ); if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } else { $l->trace( "- filter returned false on row" ); } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open FILTER"; } delete $self->[0]{filter}; if (blessed($self->pattern)) { $self->pattern->close(); } $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be filtered. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< active_graph >> Returns the active graph. =cut sub active_graph { my $self = shift; return $self->[3]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'filter'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(E P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $expr = $self->[1]; return ($expr, $self->pattern); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Iterator.pm000644 000765 000024 00000006263 12173312155 020650 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Iterator # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Iterator - Executable query plan for result-generating iterators. =head1 VERSION This document describes RDF::Query::Plan::Iterator version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Iterator; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed reftype); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $iter, \&execute_cb ) >> =item C<< new ( \&create_iterator_cb ) >> =cut sub new { my $class = shift; my $iter = shift; my $cb = shift; my $self = $class->SUPER::new( $iter, $cb ); $self->[0]{referenced_variables} = []; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "ITERATOR plan can't be executed while already open"; } if (ref($self->[2])) { $self->[2]->( $context ); } # if we don't have an actual iterator, but only a promise of one, construct it now if (reftype($self->[1]) eq 'CODE' and not(blessed($self->[1]) and $self->[1]->isa('RDF::Trine::Iterator'))) { $self->[1] = $self->[1]->( $context ); } $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open ITERATOR"; } my $iter = $self->[1]; my $bindings = $iter->next; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open ITERATOR"; } my $iter = $self->[1]; $iter->close; $self->SUPER::close(); } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'optimized-iterator'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Join/000755 000765 000024 00000000000 12173312223 017405 5ustar00gregstaff000000 000000 RDF-Query-2.910/lib/RDF/Query/Plan/Join.pm000644 000765 000024 00000006776 12173312155 017767 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Join # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Join - Join query plan base class. =head1 VERSION This document describes RDF::Query::Plan::Join version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Join; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed); use RDF::Query::ExecutionContext; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $lhs, $rhs, $optional ) >> =cut sub new { my $class = shift; my $lhs = shift; my $rhs = shift; my $opt = shift; my $self = $class->SUPER::new( $lhs, $rhs, $opt, @_ ); my %vars; my @lhs_rv = $lhs->referenced_variables; my @rhs_rv = $rhs->referenced_variables; foreach my $v (@lhs_rv, @rhs_rv) { $vars{ $v }++; } $self->[0]{referenced_variables} = [ keys %vars ]; return $self; } =item C<< lhs >> Returns the left-hand-side plan to the join. =cut sub lhs { my $self = shift; return $self->[1]; } =item C<< rhs >> Returns the right-hand-side plan to the join. =cut sub rhs { my $self = shift; return $self->[2]; } =item C<< optional >> =cut sub optional { my $self = shift; return $self->[3]; } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $context = shift; my @bf; my %var_to_num; my %use_count; my $counter = 1; foreach my $t ($self->lhs, $self->rhs) { unless ($t->can('bf')) { throw RDF::Query::Error::ExecutionError -text => "Cannot compute bf for $t during join"; } my $bf = $t->bf( $context ); if ($bf =~ /f/) { $bf = ''; foreach my $n ($t->nodes) { if ($n->isa('RDF::Trine::Node::Variable')) { my $name = $n->name; my $num = ($var_to_num{ $name } ||= $counter++); $use_count{ $name }++; $bf .= "{${num}}"; } else { $bf .= 'b'; } } } push(@bf, $bf); } if ($counter <= 10) { for (@bf) { s/[{}]//g; } } my $bf = join(',',@bf); return wantarray ? @bf : $bf; } =item C<< join_classes >> Returns the class names of all available join algorithms. =cut sub join_classes { my $class = shift; my $config = shift || {}; our %JOIN_CLASSES; my @classes = reverse sort keys %JOIN_CLASSES; my @ok = grep { my $name = lc($_); $name =~ s/::/./g; (exists $config->{ $name } and not($config->{ $name })) ? 0 : 1 } @classes; return @ok; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return 0; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $expr = $self->[2]; return ($self->lhs, $self->rhs); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Limit.pm000644 000765 000024 00000007272 12173312155 020136 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Limit # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Limit - Executable query plan for Limits. =head1 VERSION This document describes RDF::Query::Plan::Limit version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Limit; use strict; use warnings; use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, $limit ) >> =cut sub new { my $class = shift; my $limit = shift; my $plan = shift; my $self = $class->SUPER::new( $limit, $plan ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "LIMIT plan can't be executed while already open"; } my $plan = $self->[2]; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->state( $self->OPEN ); $self->[0]{count} = 0; } else { warn "could not execute plan in LIMIT"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open LIMIT"; } return undef if ($self->[0]{count} >= $self->limit); my $plan = $self->[2]; my $row = $plan->next; return undef unless ($row); $self->[0]{count}++; if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open LIMIT"; } delete $self->[0]{count}; $self->[2]->close(); $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be filtered. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< limit >> Returns the limit size. =cut sub limit { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'limit'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(i P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->limit, $self->pattern); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); my $limit = $self->limit; $g->add_node( "$self", label => "Limit ($limit)" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Load.pm000644 000765 000024 00000010023 12173312155 017723 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Load # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Load - Executable query plan for LOAD operations. =head1 VERSION This document describes RDF::Query::Plan::Load version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Load; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $url, $graph ) >> =cut sub new { my $class = shift; my $url = shift; my $graph = shift; my $self = $class->SUPER::new( $url, $graph ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "LOAD plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.load"); $l->trace( "executing RDF::Query::Plan::Load" ); my %args = ($self->namedgraph) ? (context => $self->namedgraph) : (); my $ok = 0; try { RDF::Trine::Parser->parse_url_into_model( $self->url->uri_value, $context->model, %args ); $ok = 1; } catch RDF::Trine::Error with {}; $self->[0]{ok} = $ok; $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open LOAD"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.load"); $self->close(); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{ok} ); } return $self->[0]{ok}; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open LOAD"; } delete $self->[0]{ok}; $self->SUPER::close(); } =item C<< url >> Returns the URL to load data from. =cut sub url { my $self = shift; return $self->[1]; } =item C<< namedgraph >> Returns the optional graph name to load the data with. =cut sub namedgraph { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'load'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->url, $self->namedgraph); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; my $url = $self->url->uri_value; $g->add_node( "$self", label => "Load" . $self->graph_labels ); $g->add_node( "${self}$url", label => $url ); $g->add_edge( "$self" => "${self}$url", label => 'url' ); return "$self"; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Minus.pm000644 000765 000024 00000015303 12173312155 020145 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Minus # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Minus - Executable query plan for minus operations. =head1 VERSION This document describes RDF::Query::Plan::Minus version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Minus; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $lhs, $rhs ) >> =cut sub new { my $class = shift; my $lhs = shift; my $rhs = shift; my $self = $class->SUPER::new( $lhs, $rhs, @_ ); my %vars; my @lhs_rv = $lhs->referenced_variables; my @rhs_rv = $rhs->referenced_variables; foreach my $v (@lhs_rv, @rhs_rv) { $vars{ $v }++; } $self->[0]{referenced_variables} = [ keys %vars ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "Minus plan can't be executed while already open"; } $self->[0]{start_time} = [gettimeofday]; my @inner; $self->rhs->execute( $context ); while (my $row = $self->rhs->next) { # warn "*** loading inner row cache with: " . Dumper($row); push(@inner, $row); } $self->lhs->execute( $context ); if ($self->lhs->state == $self->OPEN) { $self->[0]{inner} = \@inner; $self->[0]{outer} = $self->lhs; $self->[0]{inner_index} = 0; $self->[0]{needs_new_outer} = 1; $self->[0]{inner_count} = 0; $self->[0]{count} = 0; $self->[0]{logger} = $context->logger; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } # warn '########################################'; $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open Minus"; } my $outer = $self->[0]{outer}; my $inner = $self->[0]{inner}; my $l = Log::Log4perl->get_logger("rdf.query.plan.minus"); while (1) { if ($self->[0]{needs_new_outer}) { $self->[0]{outer_row} = $outer->next; if (ref($self->[0]{outer_row})) { $self->[0]{outer_row_vars} = { map { $_ => 1 } $self->[0]{outer_row}->variables }; $self->[0]{needs_new_outer} = 0; $self->[0]{inner_index} = 0; $self->[0]{inner_count} = 0; # warn "got new outer row: " . Dumper($self->[0]{outer_row}); } else { # we've exhausted the outer iterator. we're now done. # warn "exhausted"; return undef; } } my $ok = 1; while ($self->[0]{inner_index} < scalar(@$inner)) { my $inner_row = $inner->[ $self->[0]{inner_index}++ ]; # warn "using inner row: " . Dumper($inner_row); my @shared = grep { exists $self->[0]{outer_row_vars}{ $_ } } $inner_row->variables; if (scalar(@shared) == 0) { if ($l->is_trace) { $l->trace("no intersection of domains in minus: $inner_row ⋈ $self->[0]{outer_row}"); } } elsif (my $joined = $inner_row->join( $self->[0]{outer_row} )) { if ($l->is_trace) { $l->trace("joined bindings in minus: $inner_row ⋈ $self->[0]{outer_row}"); } # warn "-> joined\n"; $self->[0]{inner_count}++; $self->[0]{count}++; $ok = 0; last; } else { if ($l->is_trace) { $l->trace("failed to join bindings in minus: $inner_row ⋈ $self->[0]{outer_row}"); } } } $self->[0]{needs_new_outer} = 1; if ($ok) { my $bindings = $self->[0]{outer_row}; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open Minus"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.minus"); my $t0 = delete $self->[0]{start_time}; my $count = delete $self->[0]{count}; if (my $log = delete $self->[0]{logger}) { $l->debug("logging minus execution statistics"); my $elapsed = tv_interval ( $t0 ); if (my $sparql = $self->logging_keys->{sparql}) { if ($l->is_trace) { $l->trace("- SPARQL: $sparql"); $l->trace("- elapsed: $elapsed"); $l->trace("- count: $count"); } $log->push_key_value( 'execute_time-minus', $sparql, $elapsed ); $log->push_key_value( 'cardinality-minus', $sparql, $count ); } if (my $bf = $self->logging_keys->{bf}) { if ($l->is_trace) { $l->trace("- bf: $bf"); } $log->push_key_value( 'cardinality-bf-minus', $bf, $count ); } } delete $self->[0]{inner}; delete $self->[0]{outer}; delete $self->[0]{inner_index}; delete $self->[0]{needs_new_outer}; delete $self->[0]{inner_count}; $self->lhs->close(); $self->rhs->close(); $self->SUPER::close(); } =item C<< lhs >> Returns the left-hand-side plan to the join. =cut sub lhs { my $self = shift; return $self->[1]; } =item C<< rhs >> Returns the right-hand-side plan to the join. =cut sub rhs { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return 0; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { my $self = shift; return 'minus'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $expr = $self->[2]; return ($self->lhs, $self->rhs); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs); $g->add_node( "$self", label => "Minus" . $self->graph_labels ); $g->add_edge( "$self", $l ); $g->add_edge( "$self", $r ); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Move.pm000644 000765 000024 00000011167 12173312155 017764 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Move # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Move - Executable query plan for MOVE operations. =head1 VERSION This document describes RDF::Query::Plan::Move version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Move; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $from, $to, $silent ) >> =cut sub new { my $class = shift; my $from = shift; my $to = shift; my $silent = shift; my $self = $class->SUPER::new( $from, $to, $silent ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "MOVE plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.move"); $l->trace( "executing RDF::Query::Plan::Move" ); my $from = $self->from; my $to = $self->to; # warn "Moving graph " . $from->as_string; my $ok = 0; try { if ($from->equal( $to )) { # no-op } else { my $model = $context->model; $model->begin_bulk_ops(); $model->remove_statements( undef, undef, undef, $to ); my $iter = $model->get_statements( undef, undef, undef, $from ); while (my $st = $iter->next) { $model->add_statement( $st, $to ); } $context->model->remove_statements( undef, undef, undef, $from ); $model->end_bulk_ops(); } $ok = 1; } catch RDF::Trine::Error with {}; $self->[0]{ok} = $ok; $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open MOVE"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.move"); $self->close(); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{ok} ); } return $self->[0]{ok}; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open MOVE"; } delete $self->[0]{ok}; $self->SUPER::close(); } =item C<< from >> Returns the graph node which is to be copied. =cut sub from { my $self = shift; return $self->[1]; } =item C<< to >> Returns the graph node to which data is copied. =cut sub to { my $self = shift; return $self->[2]; } =item C<< silent >> Returns the silent flag. =cut sub silent { my $self = shift; return $self->[3]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'move'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->from, $self->to); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; my $furl = $self->from->uri_value; my $turl = $self->to->uri_value; $g->add_node( "$self", label => "Move" . $self->graph_labels ); $g->add_node( "${self}$furl", label => $furl ); $g->add_node( "${self}$turl", label => $turl ); $g->add_edge( "$self" => "${self}$furl", label => 'from' ); $g->add_edge( "$self" => "${self}$turl", label => 'to' ); return "$self"; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/NamedGraph.pm000644 000765 000024 00000011504 12173312155 021057 0ustar00gregstaff000000 000000 # RDF::Query::Plan::NamedGraph # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::NamedGraph - Executable query plan for named graphs. =head1 VERSION This document describes RDF::Query::Plan::NamedGraph version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::NamedGraph; use strict; use warnings; use Scalar::Util qw(blessed); use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $graph, $plan ) >> =cut sub new { my $class = shift; my $graph = shift; my $plan = shift; my $self = $class->SUPER::new( $graph, $plan ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; my $l = Log::Log4perl->get_logger("rdf.query.plan.namedgraph"); $l->trace('constructing named graph plan...'); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "NamedGraph plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.namedgraph"); $l->trace('executing named graph plan'); my $model = $context->model; my $graphs = $model->get_graphs; $self->[0]{graphs} = $graphs; $self->[0]{bound} = $context->bound || {}; $self->[0]{context} = $context; if (my $g = $self->[0]{graphs}->next) { my %bound = %{ $self->[0]{bound} }; $bound{ $self->graph->name } = $g; my $ctx = $context->copy( bound => \%bound ); my $plan = $self->pattern; $l->trace("Executing named graph pattern with graph " . $g->as_string . ": " . $plan->sse); $plan->execute( $ctx ); $self->[0]{current_graph} = $g; } $self->state( $self->OPEN ); $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open NAMED GRAPH"; } my $context = $self->[0]{context}; my $l = Log::Log4perl->get_logger("rdf.query.plan.namedgraph"); while (1) { unless ($self->[0]{current_graph}) { return; } my $row = $self->pattern->next; if ($row) { my $g = $self->[0]{current_graph}; if (my $rg = $row->{ $self->graph->name }) { unless ($rg->equal( $g )) { next; } } $row->{ $self->graph->name } = $g; if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } else { my $g = $self->[0]{graphs}->next; unless (blessed($g)) { return; } my %bound = %{ $self->[0]{bound} }; $bound{ $self->graph->name } = $g; my $ctx = $self->[0]{context}->copy( bound => \%bound ); my $plan = $self->pattern; if ($plan->state == $plan->OPEN) { $plan->close(); } $l->trace("Executing named graph pattern with graph " . $g->as_string); $plan->execute( $ctx ); $self->[0]{current_graph} = $g; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open NAMED GRAPH"; } delete $self->[0]{current_graph}; my $plan = $self->pattern; if ($plan->state == $plan->OPEN) { $plan->close(); } $self->SUPER::close(); } =item C<< graph >> Returns the graph variable. =cut sub graph { my $self = shift; return $self->[1]; } =item C<< pattern >> Returns the query plan that will be used with each named graph in the model. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'named-graph'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->graph, $self->pattern); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Offset.pm000644 000765 000024 00000007535 12173312155 020310 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Offset # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Offset - Executable query plan for Offsets. =head1 VERSION This document describes RDF::Query::Plan::Offset version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Offset; use strict; use warnings; use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, $offset ) >> =cut sub new { my $class = shift; my $offset = shift; my $plan = shift; my $self = $class->SUPER::new( $offset, $plan ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "OFFSET plan can't be executed while already open"; } my $plan = $self->[2]; $self->[0]{exhausted} = 0; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->state( $self->OPEN ); for (my $i = 0; $i < $self->offset; $i++) { my $row = $plan->next; if(not(defined($row))) { $self->[0]{exhausted} = 1; last; } } } else { warn "could not execute plan in OFFSET"; } $self; } =item C<< next >> =cut sub next { my $self = shift; if ($self->[0]{exhausted}) { return undef; } unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open OFFSET"; } my $plan = $self->[2]; my $row = $plan->next; unless ($row) { $self->[0]{exhausted} = 1; return undef; } if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open OFFSET"; } $self->[2]->close(); $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be offset. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< offset >> Returns the number of results that are discarded as offset. =cut sub offset { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'offset'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(i P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->offset, $self->pattern); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); $g->add_node( "$self", label => "Offset ($self->[1])" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Path.pm000644 000765 000024 00000032347 12173312155 017755 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Path # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Path - Executable query plan for Paths. =head1 VERSION This document describes RDF::Query::Plan::Path version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Path; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $path_operator, $path, $start, $end, $graph, $distinct, %args ) >> =cut sub new { my $class = shift; my $op = shift; my $start = shift; my $path = shift; my $end = shift; my $graph = shift; my $distinct = shift; my %args = @_; my $self = $class->SUPER::new( $op, $path, $start, $end, $graph, $distinct, \%args ); my %vars; for ($start, $end) { $vars{ $_->name }++ if ($_->isa('RDF::Query::Node::Variable')); } $self->[0]{referenced_variables} = [keys %vars]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "PATH plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.path"); $l->trace( "executing RDF::Query::Plan::Path " . $self->sse ); my $start = $self->start; my $end = $self->end; my $graph = $self->graph; my $bound = $context->bound; if (%$bound) { for ($start, $end, $graph) { next unless (blessed($_)); next unless ($_->isa('RDF::Trine::Node::Variable')); next unless (blessed($bound->{ $_->name })); $_ = $bound->{ $_->name }; } } $self->[0]{results} = []; my @vars = grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); my $model = $context->model; $self->[0]{bound} = $bound; $self->[0]{graph} = $graph; $self->[0]{count} = 0; $self->[0]{context} = $context; $self->state( $self->OPEN ); my $op = $self->path_operator; if ($op eq 'NegatedPropertySet') { $self->_run_nps(); } elsif ($op eq 'ZeroOrMorePath') { $self->_run_zeroormore(); } elsif ($op eq 'OneOrMorePath') { $self->_run_oneormore(); } elsif ($op eq 'ZeroLengthPath') { $self->_run_zerolength(); } $self; } sub _run_nps { my $self = shift; my $context = $self->[0]{context}; my $graph = $self->[0]{graph}; $graph = RDF::Trine::Node::Nil->new() unless (defined($graph)); my $model = $context->model; my $path = $self->path; my $var = RDF::Query::Node::Variable->new(); my $st = RDF::Query::Algebra::Quad->new( $self->start, $var, $self->end, $graph ); my @nodes = $st->nodes; my $plan = RDF::Query::Plan::Quad->new( @nodes[0..2], $graph ); my %not; foreach my $n (@$path) { $not{ $n->uri_value }++; } $plan->execute( $context ); while (my $row = $plan->next) { if (my $p = $row->{ $var->name }) { next if (exists $not{ $p->uri_value }); } push(@{ $self->[0]{results} }, $row); } } sub _run_zeroormore { my $self = shift; my $context = $self->[0]{context}; my $graph = $self->[0]{graph}; $graph = RDF::Trine::Node::Nil->new() unless (defined($graph)); my $model = $context->model; my $path = $self->path; my @vars = grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); if (scalar(@vars) == 2) { # var path+ var my %nodes; foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { $nodes{ $n->as_string } = $n; } my $end = $self->end; my $path = $self->path; my @names = map { $_->name } @vars; foreach my $start (values %nodes) { # warn "starting var path* var path at $start"; my $r = []; $self->_alp( $start, $path, $r, {} ); foreach my $term (@$r) { my %data = ($names[0] => $start, $names[1] => $term); my $vb = RDF::Query::VariableBindings->new(\%data); push(@{ $self->[0]{results} }, $vb); } } } elsif (scalar(@vars) == 1) { my $start = $self->start; my $end = $self->end; my $path = $self->path; if ($start->isa('RDF::Trine::Node::Variable')) { # var path+ term ($start, $end) = ($end, $start); $path = ['^', $path]; } # term path+ var my $r = []; $self->_alp( $start, $path, $r, {} ); my $name = $vars[0]->name; foreach my $term (@$r) { my $vb = RDF::Query::VariableBindings->new({ $name => $term }); push(@{ $self->[0]{results} }, $vb); } } else { # term path+ term my $var = RDF::Trine::Node::Variable->new(); my $start = $self->start; my $end = $self->end; my $path = $self->path; my $r = []; $self->_alp( $start, $path, $r, {} ); foreach my $term (@$r) { if ($term->equal( $end )) { my $vb = RDF::Query::VariableBindings->new({}); push(@{ $self->[0]{results} }, $vb); return; } } } } sub _run_oneormore { my $self = shift; my $context = $self->[0]{context}; my $graph = $self->[0]{graph}; $graph = RDF::Trine::Node::Nil->new() unless (defined($graph)); my $model = $context->model; my $path = $self->path; my @vars = grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); if (scalar(@vars) == 2) { # var path+ var my %nodes; foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { $nodes{ $n->as_string } = $n; } my $end = $self->end; my $path = $self->path; my @names = map { $_->name } @vars; foreach my $start (values %nodes) { # warn "starting var path+ var path at $start"; my $x = $self->_path_eval($start, $path); my $r = []; while (my $n = $x->next) { $self->_alp( $n, $path, $r, {} ); } foreach my $term (@$r) { my %data = ($names[0] => $start, $names[1] => $term); my $vb = RDF::Query::VariableBindings->new(\%data); push(@{ $self->[0]{results} }, $vb); } } } elsif (scalar(@vars) == 1) { my $start = $self->start; my $end = $self->end; my $path = $self->path; if ($start->isa('RDF::Trine::Node::Variable')) { # var path+ term ($start, $end) = ($end, $start); $path = ['^', $path]; } # term path+ var my $x = $self->_path_eval($start, $path); my $r = []; my $V = {}; while (my $n = $x->next) { $self->_alp( $n, $path, $r, $V ); } my $name = $vars[0]->name; foreach my $term (@$r) { my $vb = RDF::Query::VariableBindings->new({ $name => $term }); push(@{ $self->[0]{results} }, $vb); } } else { # term path+ term my $var = RDF::Trine::Node::Variable->new(); my $start = $self->start; my $end = $self->end; my $path = $self->path; my $x = $self->_path_eval($start, $path); my $V = {}; while (my $n = $x->next) { my $r = []; $self->_alp( $n, $path, $r, $V ); foreach my $term (@$r) { if ($term->equal( $end )) { my $vb = RDF::Query::VariableBindings->new({}); push(@{ $self->[0]{results} }, $vb); return; } } } } } # returns an iterator of terms sub _path_eval { my $self = shift; my $start = shift; my $path = shift; my $context = $self->[0]{context}; my $graph = $self->[0]{graph}; $graph = RDF::Trine::Node::Nil->new() unless (defined($graph)); my $var = RDF::Query::Node::Variable->new(); my $plan = RDF::Query::Plan->__path_plan( $start, $path, $var, $graph, $context, prevent_distinguishing_bnodes => 1, distinct => $self->distinct ); $plan->execute( $context ); my $iter = RDF::Trine::Iterator->new( sub { my $r = $plan->next; return unless ($r); my $t = $r->{ $var->name }; return $t; } ); } sub _alp { my $self = shift; my $term = shift; my $path = shift; my $r = shift; my $v = shift; return if (exists($v->{ $term->as_string })); $v->{ $term->as_string } = $term; push(@$r, $term); my $x = $self->_path_eval($term, $path); while (my $n = $x->next) { $self->_alp( $n, $path, $r, $v ); } unless ($self->distinct) { delete $v->{ $term->as_string }; } } sub _run_zerolength { my $self = shift; my $context = $self->[0]{context}; my $graph = $self->[0]{graph}; $graph = RDF::Trine::Node::Nil->new() unless (defined($graph)); my $model = $context->model; my $path = $self->path; my @vars = grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); if (scalar(@vars) == 2) { # -- bind VAR(s) to subjects and objects in the current active graph my @names = map { $_->name } @vars; my %nodes; foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { $nodes{ $n->as_string } = $n; } foreach my $n (values %nodes) { my %data; @data{ @names } = ($n) x scalar(@names); my $vb = RDF::Query::VariableBindings->new(\%data); push(@{ $self->[0]{results} }, $vb); } } elsif (scalar(@vars) == 1) { my ($term) = grep { blessed($_) and not($_->isa('RDF::Trine::Node::Variable')) } ($self->start, $self->end); my $name = $vars[0]->name; my $vb = RDF::Query::VariableBindings->new({ $name => $term }); push(@{ $self->[0]{results} }, $vb); } else { if ($self->start->equal( $self->end )) { my $vb = RDF::Query::VariableBindings->new({}); push(@{ $self->[0]{results} }, $vb); } } } =item C<< next >> =cut sub next { my $self = shift; my $l = Log::Log4perl->get_logger("rdf.query.plan.path"); unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PATH"; } if (scalar(@{ $self->[0]{results} })) { my $result = shift(@{ $self->[0]{results} }); $l->trace( 'returning path result: ' . $result ) if (defined($result)); if (my $d = $self->delegate) { $d->log_result( $self, $result ); } return $result; } return; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PATH"; } delete $self->[0]{iter}; $self->SUPER::close(); } =item C<< path_operator >> Returns the path operation. =cut sub path_operator { my $self = shift; return $self->[1]; } =item C<< path >> Returns the path expression. =cut sub path { my $self = shift; return $self->[2]; } =item C<< start >> Returns the path start node. =cut sub start { my $self = shift; return $self->[3]; } =item C<< end >> Returns the path end node. =cut sub end { my $self = shift; return $self->[4]; } =item C<< graph >> Returns the named graph. =cut sub graph { my $self = shift; return $self->[5]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->[6]; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { my $self = shift; return $self->path_operator; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(s N N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $path = $self->path; if (blessed($path)) { return ($path->sse, $self->start, $self->end, $self->graph); } else { return ('(undefined path)', $self->start, $self->end, $self->graph); } } =item C<< explain >> Returns a string serialization of the plan appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); $string .= $self->start->explain($s, $count+1); my $path = $self->path; if ($type eq 'NegatedPropertySet') { $string .= "${indent}${s}(\n"; foreach my $iri (@$path) { $string .= "${indent}${s}${s}" . $iri->as_string . "\n"; } $string .= "${indent}${s})\n"; } elsif ($type =~ /^ZeroOrMorePath|OneOrMorePath|ZeroLengthPath$/) { $string .= "${indent}${s}${s}" . $self->_path_as_string($path) . "\n"; } else { throw RDF::Query::Error; } $string .= $self->end->explain($s, $count+1); # $string .= $self->pattern->explain( $s, $count+1 ); return $string; } sub _path_as_string { my $self = shift; my $path = shift; if (blessed($path)) { return $path->as_string; } my ($op, @nodes) = @$path; if ($op eq '/') { return join('/', map { $self->_path_as_string($_) } @nodes); } elsif ($op =~ /^[?+*]$/) { return '(' . $self->_path_as_string($nodes[0]) . ')' . $op; } else { throw RDF::Query::Error -text => "Can't serialize path '$op' in plan explanation"; } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Project.pm000644 000765 000024 00000012552 12173312155 020463 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Project # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Project - Executable query plan for Projects. =head1 VERSION This document describes RDF::Query::Plan::Project version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Project; use strict; use warnings; use Scalar::Util qw(blessed refaddr); use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $plan, \@keys ) >> =cut sub new { my $class = shift; my $plan = shift; my $keys = shift; my (@vars, @exprs); foreach my $k (@$keys) { push(@exprs, $k) if ($k->isa('RDF::Query::Expression')); push(@vars, $k->name) if ($k->isa('RDF::Query::Node::Variable')); push(@vars, $k) if (not(ref($k))); } my $self = $class->SUPER::new( $plan, \@vars, \@exprs ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "PROJECT plan can't be executed while already open"; } my $plan = $self->[1]; $plan->execute( $context ); if ($plan->state == $self->OPEN) { $self->[0]{context} = $context; $self->state( $self->OPEN ); } else { warn "could not execute plan in PROJECT"; } $self; } =item C<< next >> =cut sub next { my $self = shift; my $ctx = $self->[0]{context}; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PROJECT"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.project"); my $plan = $self->[1]; my $row = $plan->next; unless (defined($row)) { $l->trace("no remaining rows in project"); # if ($self->[1]->state == $self->[1]->OPEN) { # $self->[1]->close(); # } return; } if ($l->is_trace) { $l->trace( "project on row $row" ); } my $keys = $self->[2]; my $exprs = $self->[3]; my $query = $self->[0]{context}->query; my $bridge = $self->[0]{context}->model; my $proj = $row->project( @{ $keys } ); foreach my $e (@$exprs) { my $name = $e->sse; my $var_or_expr = $e; my $value = $query->var_or_expr_value( $bridge, $row, $var_or_expr, $ctx ); if ($l->is_trace) { $l->trace( "- project value $name -> $value" ); } $proj->{ $name } = $value; } $l->trace( "- projected row: $proj" ); if (my $d = $self->delegate) { $d->log_result( $self, $proj ); } return $proj; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PROJECT"; } delete $self->[0]{context}; if (blessed($self->[1]) and $self->[1]->state == $self->OPEN) { $self->[1]->close(); } $self->SUPER::close(); } =item C<< pattern >> Returns the query plan that will be used to produce the data to be projected. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; return $self->pattern->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'project'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(\J P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my @vars = map { RDF::Query::Node::Variable->new( $_ ) } @{$self->[2]}; my @exprs = @{$self->[3]}; return ([ @vars, @exprs ], $self->pattern); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); my $expr = join(' ', @{$self->[2]}, map { blessed($_) ? $_->sse( {}, "" ) : $_ } @{$self->[3]}); $g->add_node( "$self", label => "Project ($expr)" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } =item C<< explain >> Returns a string serialization of the plan appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift || ' '; my $count = shift || 0; my $indent = $s x $count; my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); my @vars = map { RDF::Query::Node::Variable->new( $_ ) } @{$self->[2]}; my @exprs = @{$self->[3]}; $string .= "${indent}${s}" . join(' ', @vars, @exprs) . "\n"; $string .= $self->pattern->explain( $s, $count+1 ); return $string; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Quad.pm000644 000765 000024 00000017333 12173312155 017751 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Quad # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Quad - Executable query plan for Quads. =head1 VERSION This document describes RDF::Query::Plan::Quad version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Quad; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed refaddr); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @quad ) >> =cut sub new { my $class = shift; my @quad = @_; my $self = $class->SUPER::new( @quad ); ### the next two loops look for repeated variables because some backends ### can't distinguish a pattern like { ?a ?a ?b } ### from { ?a ?b ?c }. if we find repeated variables (there can be at most ### two since there are only four nodes in a quad), we save the positions ### in the quad that hold the variable(s), and the code in next() will filter ### out any results that don't have the same value in those positions. ### ### in the first pass, we also set up the mapping that will let us pull out ### values from the result quads to construct result bindings. my %var_to_position; my @methodmap = qw(subject predicate object context); my %counts; my @dup_vars; foreach my $idx (0 .. 3) { my $node = $quad[ $idx ]; if (blessed($node) and $node->isa('RDF::Trine::Node::Variable')) { my $name = $node->name; $var_to_position{ $name } = $methodmap[ $idx ]; $counts{ $name }++; if ($counts{ $name } >= 2) { push(@dup_vars, $name); } } } $self->[0]{referenced_variables} = [ keys %counts ]; my %positions; if (@dup_vars) { foreach my $dup_var (@dup_vars) { foreach my $idx (0 .. 3) { my $var = $quad[ $idx ]; if (blessed($var) and ($var->isa('RDF::Trine::Node::Variable') or $var->isa('RDF::Trine::Node::Blank'))) { my $name = ($var->isa('RDF::Trine::Node::Blank')) ? '__' . $var->blank_identifier : $var->name; if ($name eq $dup_var) { push(@{ $positions{ $dup_var } }, $methodmap[ $idx ]); } } } } } $self->[0]{mappings} = \%var_to_position; if (%positions) { $self->[0]{dups} = \%positions; } return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "QUAD plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.quad"); $l->trace( "executing RDF::Query::Plan::Quad:" ); my @quad = @{ $self }[ 1..4 ]; my $bound = $context->bound; if (%$bound) { foreach my $i (0 .. $#quad) { next unless ($quad[$i]->isa('RDF::Trine::Node::Variable')); next unless (blessed($bound->{ $quad[$i]->name })); $quad[ $i ] = $bound->{ $quad[$i]->name }; } } my $model = $context->model; my @names = qw(subject predicate object context); foreach my $i (0 .. 3) { $l->trace( sprintf("- quad %10s: %s", $names[$i], $quad[$i]) ); } my $iter = $model->get_statements( @quad[0..3] ); if (blessed($iter)) { $l->trace("got quad iterator"); $self->[0]{iter} = $iter; $self->[0]{bound} = $bound; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open QUAD"; } my $iter = $self->[0]{iter}; my $l = Log::Log4perl->get_logger("rdf.query.plan.quad"); $l->trace("next() called on Quad plan"); LOOP: while (my $row = $iter->next) { $l->trace("got quad: " . $row->as_string); if (my $data = $self->[0]{dups}) { foreach my $pos (values %$data) { my @pos = @$pos; my $first_method = shift(@pos); my $first = $row->$first_method(); foreach my $p (@pos) { unless ($first->equal( $row->$p() )) { use Data::Dumper; $l->trace("Quad $first_method and $p didn't match: " . Dumper($first, $row->$p())); next LOOP; } } } } # if ($row->context->isa('RDF::Trine::Node::Nil')) { # next; # } my $binding = {}; foreach my $key (keys %{ $self->[0]{mappings} }) { my $method = $self->[0]{mappings}{ $key }; $binding->{ $key } = $row->$method(); } my $pre_bound = $self->[0]{bound}; my $bindings = RDF::Query::VariableBindings->new( $binding ); if ($row->can('label')) { if (my $o = $row->label('origin')) { $bindings->label( origin => [ $o ] ); } } @{ $bindings }{ keys %$pre_bound } = values %$pre_bound; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } $l->trace("No more quads"); return; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { Carp::cluck; throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open QUAD"; } delete $self->[0]{iter}; delete $self->[0]{bound}; $self->SUPER::close(); } =item C<< nodes () >> =cut sub nodes { my $self = shift; return @{ $self }[1,2,3,4]; } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $context = shift; my $bf = ''; my $bound = $context->bound; foreach my $n (@{ $self }[1,2,3,4]) { if ($n->isa('RDF::Trine::Node::Variable')) { if (my $b = $bound->{ $n->name }) { $bf .= 'b'; } else { $bf .= 'f'; } } else { $bf .= 'b'; } } return $bf; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'quad'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->nodes); } =item C<< explain >> Returns a string serialization of the query plan appropriate for display on the command line. =cut sub explain { my $self = shift; my ($s, $count) = (' ', 0); if (@_) { $s = shift; $count = shift; } my $indent = '' . ($s x $count); my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)) . "${indent}${s}" . join(' ', map { ($_->isa('RDF::Trine::Node::Nil')) ? "(nil)" : $_->as_sparql } $self->plan_node_data) . "\n"; return $string; } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; $g->add_node( "$self", label => "Quad" . $self->graph_labels ); my @names = qw(subject predicate object context); foreach my $i (0 .. 3) { my $n = $self->[ $i + 1 ]; my $rel = $names[ $i ]; my $str = $n->sse( {}, '' ); $g->add_node( "${self}$n", label => $str ); $g->add_edge( "$self" => "${self}$n", label => $names[ $i ] ); } return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Sequence.pm000644 000765 000024 00000006514 12173312155 020626 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Sequence # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Sequence - Executable query plan for a sequence of operations. =head1 VERSION This document describes RDF::Query::Plan::Sequence version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Sequence; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed); use RDF::Trine::Statement; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @plans ) >> =cut sub new { my $class = shift; my @plans = @_; my $self = $class->SUPER::new( \@plans ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "SEQUENCE plan can't be executed twice"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.sequence"); $l->trace( "executing RDF::Query::Plan::Sequence" ); my @plans = @{ $self->[1] }; while (scalar(@plans) > 1) { my $p = shift(@plans); $p->execute( $context ); 1 while ($p->next); } my $iter = $plans[0]->execute( $context ); if (blessed($iter)) { $self->[0]{iter} = $iter; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open SEQUENCE"; } my $iter = $self->[0]{iter}; my $row = $iter->next; if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open SEQUENCE"; } delete $self->[0]{iter}; $self->SUPER::close(); } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; my @plans = @{ $self->[1] }; return $plans[ $#plans ]->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; my @plans = @{ $self->[1] }; return $plans[ $#plans ]->ordered; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'sequence'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(*P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my @triples = @{ $self->[1] }; return @triples; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Service.pm000644 000765 000024 00000032676 12173312155 020466 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Service # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Service - Executable query plan for remote SPARQL queries. =head1 VERSION This document describes RDF::Query::Plan::Service version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Service; use strict; use warnings; use base qw(RDF::Query::Plan); use Data::Dumper; use Scalar::Util qw(blessed refaddr); use Storable qw(store_fd fd_retrieve); use URI::Escape; use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $endpoint, $plan, $silent, $sparql, [ \%logging_keys ] ) >> Returns a new SERVICE (remote endpoint call) query plan object. C<<$endpoint>> is the URL of the endpoint (as a node object). C<<$plan>> is the query plan representing the query to be sent to the remote endpoint (needed for cost estimates). C<<$sparql>> is the serialized SPARQL query to be sent to the remote endpoint. Finally, if present, C<<%logging_keys>> is a HASH containing the keys to use in logging the execution of this plan. Valid HASH keys are: * bf - The bound/free string representing C<<$plan>> =cut sub new { my $class = shift; my $endpoint = shift; my $plan = shift; my $silent = shift; my $sparql = shift; unless ($sparql) { throw RDF::Query::Error::MethodInvocationError -text => "SERVICE plan constructor requires a serialized SPARQL query argument"; } my $keys = {}; my $self; if ($endpoint->isa('RDF::Query::Node::Variable')) { my $lhs = shift; Carp::confess "no lhs to binary SERVICE plan" unless ($lhs); $self = $class->SUPER::new( $endpoint, $plan, $silent, $sparql, $lhs ); } else { $self = $class->SUPER::new( $endpoint, $plan, $silent, $sparql ); } $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; $self->[0]{logging_keys} = $keys; # if (@_) { # # extra args (like the bound/free stuff for logging # my %args = @_; # @{ $self->[0] }{ keys %args } = values %args; # } return $self; } =item C<< new_from_plan ( $endpoint, $plan, $context ) >> Returns a new SERVICE query plan object. C<<$endpoint>> is the URL of the endpoint (as a string). C<<$plan>> is the query plan representing the query to be sent to the remote endpoint. The exact SPARQL serialization that will be used is obtained by getting the originating RDF::Query::Algebra object from C<<$plan>>, and serializing it (with the aid of the RDF::Query::ExecutionContext object C<<$context>>). =cut sub new_from_plan { my $class = shift; my $url = shift; my $plan = shift; my $context = shift; my $pattern = $plan->label( 'algebra' ); unless ($pattern->isa('RDF::Query::Algebra::GroupGraphPattern')) { $pattern = RDF::Query::Algebra::GroupGraphPattern->new( $pattern ); } my $ns = $context->ns; my $sparql = join("\n", (map { sprintf("PREFIX %s: <%s>", ($_ eq '__DEFAULT__' ? '' : $_), $ns->{$_}) } (keys %$ns)), sprintf("SELECT * WHERE %s", $pattern->as_sparql({namespaces => $ns}, '')) ); my $service = $class->new( $url, $plan, $sparql, @_ ); return $service; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "SERVICE plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.service"); my $endpoint = $self->endpoint; my $bound = $context->bound || {}; my $sparql = $self->sparql( $bound ); my $query = $context->query; if ($self->lhs) { $self->lhs->execute( $context ); $self->[0]{context} = $context; $self->[0]{'open'} = 1; $self->[0]{'count'} = 0; $self->[0]{logger} = $context->logger; $self->state( $self->OPEN ); } else { { $l->debug('SERVICE execute'); my $printable = $sparql; $l->debug("SERVICE <$endpoint> pattern: $printable"); } my $iter = $self->_get_iterator( $context, $endpoint->uri_value, $sparql ); if ($iter) { # $self->[0]{args} = $args; # $self->[0]{fh} = $fh; # $self->[0]{'write'} = $write; $self->[0]{iter} = $iter; $self->[0]{'open'} = 1; $self->[0]{'count'} = 0; $self->[0]{logger} = $context->logger; if (my $log = $self->[0]{logger}) { $log->push_value( service_endpoints => $endpoint ); } $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open SERVICE"; } return undef unless ($self->[0]{'open'}); if ($self->lhs) { while (1) { if ($self->[0]{iter_stash}) { while (my $row = $self->[0]{iter_stash}->next) { # warn "got service RHS row: $row\n"; my $lhs = $self->[0]{lhs_row}; # warn "joining:\n\t$row\n\t$lhs\n"; if (my $j = $row->join( $lhs )) { return $j; } } delete $self->[0]{iter_stash}; } my $lrow = $self->lhs->next; unless ($lrow) { # warn "reached end of service LHS iterator\n"; delete $self->[0]{lhs_row}; return undef; } # warn "got service LHS row: $lrow\n"; $self->[0]{lhs_row} = $lrow; my $endpoint = $lrow->{ $self->endpoint->name }; unless (blessed($endpoint) and $endpoint->isa('RDF::Query::Node::Resource')) { throw RDF::Query::Error::ExecutionError -text => "Endpoint variable bound to non-IRI"; } my $context = $self->[0]{context}; my $bound = $context->bound || {}; my $sparql = $self->sparql( $bound ); my $plan = $RDF::Query::Plan::PLAN_CLASSES{'service'}->new( $endpoint, $self->pattern, $self->silent, $sparql ); # warn $plan; my $iter = $plan->execute( $context ); return undef unless ($iter); $self->[0]{iter_stash} = $iter; } } else { my $iter = $self->[0]{iter}; my $result = $iter->next; return undef unless $result; $self->[0]{'count'}++; my $row = RDF::Query::VariableBindings->new( $result ); $row->label( origin => [ $self->endpoint ] ); if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open SERVICE"; } delete $self->[0]{args}; if (my $log = delete $self->[0]{logger}) { my $endpoint = $self->endpoint; my $sparql = $self->sparql; my $count = $self->[0]{count}; $log->push_key_value( 'cardinality-service-' . $endpoint, $sparql, $count ); if (my $bf = $self->logging_keys->{ 'bf' }) { $log->push_key_value( 'cardinality-bf-service-' . $endpoint, $bf, $count ); } } delete $self->[0]{count}; my $fh = delete $self->[0]{fh}; # 1 while (<$fh>); # delete $self->[0]{'write'}; # delete $self->[0]{'open'}; $self->SUPER::close(); } sub _get_iterator { my $self = shift; my $context = shift; my $endpoint = shift; my $sparql = shift; my $url = $endpoint . '?query=' . uri_escape($sparql); my $l = Log::Log4perl->get_logger("rdf.query.plan.service"); $l->trace( 'SERVICE URL: ' . $url ); my $query = $context->query; my $handler = RDF::Trine::Iterator::SAXHandler->new(); my $p = XML::SAX::ParserFactory->parser(Handler => $handler); my $ua = ($query) ? $query->useragent : do { my $u = LWP::UserAgent->new( agent => "RDF::Query/${RDF::Query::VERSION}" ); $u->default_headers->push_header( 'Accept' => "application/sparql-results+xml;q=0.9,application/rdf+xml;q=0.5,text/turtle;q=0.7,text/xml" ); $u; }; my $req = HTTP::Request->new('GET', $url ); my $response = $self->_request( $ua, $req ); if (blessed($response) and $response->is_success) { $p->parse_string( $response->content ); return $handler->iterator; } elsif ($self->silent) { my $v = RDF::Query::VariableBindings->new( {} ); my $iter = RDF::Trine::Iterator::Bindings->new( [ $v ] ); return $iter; } else { my $status = $response->status_line; my $sparql = $self->sparql; # warn "url: $url\n"; unless ($self->silent) { throw RDF::Query::Error::ExecutionError -text => "*** error making remote SPARQL call to endpoint $endpoint ($status) while making service call for query: $sparql"; } } } sub _request { my $self = shift; my $ua = shift; my $req = shift; my $l = Log::Log4perl->get_logger("rdf.query.plan.service"); if ($ENV{RDFQUERY_THROW_ON_SERVICE}) { if ($self->silent) { return; } else { throw RDF::Query::Error::RequestedInterruptError -text => "Won't execute SERVICE block. Unset RDFQUERY_THROW_ON_SERVICE to continue."; } } my $response = $ua->request( $req ); return $response; } # sub _get_and_parse_url { # my $self = shift; # my $context = shift; # my $url = shift; # my $fh = shift; # my $pid = shift; # my $query = $context->query; # # eval " # require XML::SAX::Expat; # require XML::SAX::Expat::Incremental; # "; # if ($@) { # die $@; # } # local($XML::SAX::ParserPackage) = 'XML::SAX::Expat::Incremental'; # my $handler = RDF::Trine::Iterator::SAXHandler->new(); # my $p = XML::SAX::Expat::Incremental->new( Handler => $handler ); # $p->parse_start; # # my $has_head = 0; # my $callback = sub { # my $content = shift; # my $resp = shift; # my $proto = shift; # unless ($resp->is_success) { # throw RDF::Query::Error -text => "SERVICE query couldn't get remote content: " . $resp->status_line; # } # $p->parse_more( $content ); # # if (not($has_head) and $handler->has_head) { # my @args = $handler->iterator_args; # if (exists( $args[2]{Handler} )) { # delete $args[2]{Handler}; # } # $has_head = 1; # store_fd \@args, $fh or die "PID $pid can't store!\n"; # } # # while (my $data = $handler->pull_result) { # store_fd $data, $fh or die "PID $pid can't store!\n"; # } # }; # my $ua = ($query) # ? $query->useragent # : do { # my $u = LWP::UserAgent->new( agent => "RDF::Query/${RDF::Query::VERSION}" ); # $u->default_headers->push_header( 'Accept' => "application/sparql-results+xml;q=0.9,application/rdf+xml;q=0.5,text/turtle;q=0.7,text/xml" ); # $u; # }; # # $ua->get( $url, ':content_cb' => $callback ); # store_fd \undef, $fh or die "can't store end-of-stream"; # } =item C<< endpoint >> =cut sub endpoint { my $self = shift; if (@_) { $self->[1] = shift; } my $e = $self->[1]; return $e; } =item C<< sparql >> Returns the SPARQL query (as a string) that will be sent to the remote endpoint. =cut sub sparql { my $self = shift; my $sparql = $self->[4]; if (ref($sparql)) { $sparql = $sparql->( @_ ); } return $sparql; } =item C<< lhs >> =cut sub lhs { my $self = shift; my $lhs = $self->[5]; return $lhs; } =item C<< silent >> Returns a boolean value indicating whether the service plan will ignore errors. =cut sub silent { my $self = shift; return $self->[3]; } =item C<< pattern >> Returns the query plan that will be used in the remote service call. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; # XXX this could be set at construction time, if we want to trust the remote # XXX endpoint to return DISTINCT results (when appropriate). return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; # XXX this could be set at construction time, if we want to trust the remote # XXX endpoint to return ORDERED results (when appropriate). return 0; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'service'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(u s); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $expr = $self->[2]; return ($self->endpoint, $self->sparql); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; $g->add_node( "$self", label => "Service (" . $self->endpoint->value . ")" . $self->graph_labels ); $g->add_node( "${self}-sparql", label => $self->sparql ); $g->add_edge( "$self" => "${self}-sparql" ); return "$self"; } =item C<< explain >> Returns a string serialization of the query plan appropriate for display on the command line. =cut sub explain { my $self = shift; my ($s, $count) = (' ', 0); if (@_) { $s = shift; $count = shift; } my $indent = '' . ($s x $count); my $type = $self->plan_node_name; my $sparql = $self->sparql; $sparql =~ s/\n/\n${indent}${s}${s}/g; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)) . "${indent}${s}" . $self->endpoint->as_string . "\n" . "${indent}${s}${sparql}\n"; return $string; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Sort.pm000644 000765 000024 00000013031 12173312155 017775 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Sort # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Sort - Executable query plan for Sorts. =head1 VERSION This document describes RDF::Query::Plan::Sort version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Sort; use strict; use warnings; use Scalar::Util qw(refaddr); use base qw(RDF::Query::Plan); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $pattern, [ $expr1, $rev1 ], ... ) >> =cut sub new { my $class = shift; my $plan = shift; my @exprs = @_; foreach my $e (@exprs) { $e->[1] ||= 0; } my $self = $class->SUPER::new( $plan, \@exprs ); $self->[0]{referenced_variables} = [ $plan->referenced_variables ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "SORT plan can't be executed while already open"; } my $plan = $self->[1]; $plan->execute( $context ); my $l = Log::Log4perl->get_logger("rdf.query.plan.sort"); $l->trace("executing sort"); if ($plan->state == $self->OPEN) { my $exprs = $self->[2]; my @rows = $plan->get_all; if ($l->is_trace) { $l->trace("sorting result list:"); $l->trace("- $_") foreach (@rows); } my $query = $context->query; use sort 'stable'; my @sorted = sort { _cmp_rows( $context, $exprs, $a, $b ) } @rows; if ($l->is_trace) { $l->trace("sorted list:"); $l->trace("- $_") foreach (@sorted); } $self->[0]{rows} = \@sorted; $self->state( $self->OPEN ); } else { warn "could not execute plan in distinct"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open SORT"; } my $bindings = shift(@{ $self->[0]{rows} }); if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open SORT"; } delete $self->[0]{rows}; $self->[1]->close(); $self->SUPER::close(); } sub _cmp_rows { my $context = shift; my $exprs = shift; my $a = shift; my $b = shift; my $l = Log::Log4perl->get_logger("rdf.query.plan.sort"); my $query = $context->query || 'RDF::Query'; my $bridge = $context->model; no warnings 'numeric'; no warnings 'uninitialized'; foreach my $data (@$exprs) { my ($expr, $rev) = @$data; my $a_val = $query->var_or_expr_value( $a, $expr, $context ); my $b_val = $query->var_or_expr_value( $b, $expr, $context ); local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1; $l->trace("comparing $a_val <=> $b_val"); my $cmp = $a_val <=> $b_val; if ($cmp != 0) { if ($rev) { $cmp *= -1; } $l->trace("==> $cmp"); return $cmp; } else { } } $l->trace("==> 0"); return 0; } =item C<< pattern >> Returns the query plan that will be used to produce the data to be sorted. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; return $self->pattern->distinct; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; my $sort = $self->[2]; return [ map { [ $_->[0], ($_->[1] ? 'DESC' : 'ASC') ] } @$sort ]; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'order'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P *\wE); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $exprs = $self->[2]; return ($self->pattern, map { [ ($_->[1] == 0 ? 'asc' : 'desc'), $_->[0] ] } @$exprs); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $c = $self->pattern->graph( $g ); my $expr = join(' ', map { $_->sse( {}, "" ) } @{ $self->[2] }); $g->add_node( "$self", label => "Sort ($expr)" . $self->graph_labels ); $g->add_edge( "$self", $c ); return "$self"; } =item C<< explain >> Returns a string serialization of the plan appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); $string .= "${indent}${s}sory by:\n"; my $exprs = $self->[2]; foreach my $e (@$exprs) { my $dir = ($e->[1] == 0 ? 'asc ' : 'desc '); $string .= "${indent}${s}${s}${dir}" . $e->[0] . "\n"; } $string .= $self->pattern->explain( $s, $count+1 ); return $string; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/SubSelect.pm000644 000765 000024 00000011312 12173312155 020737 0ustar00gregstaff000000 000000 # RDF::Query::Plan::SubSelect # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::SubSelect - Executable query plan for sub-select queries. =head1 VERSION This document describes RDF::Query::Plan::SubSelect version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::SubSelect; use strict; use warnings; use base qw(RDF::Query::Plan); use Data::Dumper; use Scalar::Util qw(blessed); use Storable qw(store_fd fd_retrieve); use URI::Escape; use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $query, [ \%logging_keys ] ) >> Returns a new SubSelect query plan object. C<<$query>> is a RDF:Query object representing a SELECT query. =cut sub new { my $class = shift; my $query = shift; my $plan = shift; unless (blessed($plan) and $plan->isa('RDF::Query::Plan')) { Carp::confess; } my $keys = {}; my $self = $class->SUPER::new( $query, $plan ); $self->[0]{referenced_variables} = [ $query->variables ]; $self->[0]{logging_keys} = $keys; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "SUBSELECT plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.subselect"); $l->trace("executing subselect"); my $plan = $self->plan; $l->trace("subselect plan: " . $plan->sse); my $iter = $plan->execute( $context ); if ($iter) { $self->[0]{iter} = $iter; $self->[0]{'open'} = 1; $self->[0]{'count'} = 0; $self->[0]{logger} = $context->logger; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open SERVICE"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.subselect"); return undef unless ($self->[0]{'open'}); my $iter = $self->[0]{iter}; my $result = $iter->next; return undef unless $result; $l->trace("- got subselect result $result"); $self->[0]{'count'}++; my $row = RDF::Query::VariableBindings->new( $result ); if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; }; =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open SERVICE"; } my $plan = $self->plan; if (defined($plan)) { $plan->close(); } delete $self->[0]{iter}; delete $self->[0]{args}; delete $self->[0]{count}; $self->SUPER::close(); } =item C<< query >> Returns the sub-select query object. =cut sub query { my $self = shift; return $self->[1]; } =item C<< plan >> Returns the sub-select query plan object. =cut sub plan { my $self = shift; return $self->[2]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { my $self = shift; # XXX this could be set at construction time, if we want to trust the remote # XXX endpoint to return DISTINCT results (when appropriate). return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { my $self = shift; # XXX this could be set at construction time, if we want to trust the remote # XXX endpoint to return ORDERED results (when appropriate). return 0; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'subselect'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->plan); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; die "graph is unimplemented for sub-selects"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/ThresholdUnion.pm000644 000765 000024 00000013222 12173312155 022015 0ustar00gregstaff000000 000000 # RDF::Query::Plan::ThresholdUnion # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::ThresholdUnion - Executable query plan for unions. =head1 VERSION This document describes RDF::Query::Plan::ThresholdUnion version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::ThresholdUnion; use strict; use warnings; use base qw(RDF::Query::Plan); use Time::HiRes qw(time); use Scalar::Util qw(blessed); use RDF::Query::ExecutionContext; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $time, @plans ) >> =cut sub new { my $class = shift; my $time = shift; my @plans = @_; my $self = $class->SUPER::new( $time, \@plans ); my %vars; foreach my $plan (@plans) { foreach my $v ($plan->referenced_variables) { $vars{ $v }++; } } $self->[0]{referenced_variables} = [ keys %vars ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "ThresholdUnion plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.thresholdunion"); my $iter = $self->[2][0]; $l->trace("threshold union initialized with first sub-plan: " . $iter->sse); $iter->execute( $context ); if ($iter->state == $self->OPEN) { $self->[0]{iter} = $iter; $self->[0]{idx} = 0; $self->[0]{context} = $context; $self->[0]{start_time} = time; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open ThresholdUnion"; } my $iter = $self->[0]{iter}; my $row = $iter->next; my $l = Log::Log4perl->get_logger("rdf.query.plan.thresholdunion"); if ($row) { if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } else { $l->trace("thresholdunion sub-plan finished"); delete $self->[0]{iter}; return undef unless ($self->[0]{idx} < $#{ $self->[2] }); $iter->close(); my $iter; my $index; my $threshold = $self->threshold_time; my $elapsed = time - $self->[0]{start_time}; if (($threshold == 0) or ($elapsed < $threshold)) { # we haven't passed the threshold of execution time, so go on # to the next optimistic plan: $l->trace("the elapsed time ($elapsed) hasn't passed the threshold time ($threshold); continuing to next plan"); $index = ++$self->[0]{idx}; $iter = $self->[2][ $index ]; } else { # the elapsed time has passed the threshold time, so jump straight to the default plan $l->trace("the elapsed time ($elapsed) has passed the threshold time ($threshold); jumping to the default plan"); $index = ($self->[0]{idx} = $#{ $self->[2] }); $iter = $self->[2][ $index ]; } $l->trace("threshold union executing next sub-plan: " . $iter->sse); $iter->execute( $self->[0]{context} ); if ($iter->state == $self->OPEN) { $self->[0]{iter} = $iter; my $bindings = $self->next; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } else { throw RDF::Query::Error::ExecutionError -text => "execute() on child [${index}] of UNION failed during next()"; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open ThresholdUnion"; } if ($self->[0]{iter}) { $self->[0]{iter}->close(); delete $self->[0]{iter}; } $self->SUPER::close(); } =item C<< children >> =cut sub children { my $self = shift; return @{ $self->[2] }; } =item C<< threshold_time >> =cut sub threshold_time { my $self = shift; return $self->[1]; } =item C<< optimistic >> =cut sub optimistic { my $self = shift; return @{ $self->[2] }[ 0 .. $#{ $self->[2] } - 1 ]; } =item C<< default >> =cut sub default { my $self = shift; return $self->[2][ $#{ $self->[2] } ]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'threshold-union'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(i *P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $exprs = $self->[2]; return ($self->threshold_time, $self->children); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my (@children) = map { $_->graph( $g ) } ($self->children); $g->add_node( "$self", label => "Threshold Union" . $self->graph_labels ); $g->add_edge( "$self", $_ ) for (@children); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Triple.pm000644 000765 000024 00000020315 12173312155 020310 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Triple # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Triple - Executable query plan for Triples. =head1 VERSION This document describes RDF::Query::Plan::Triple version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Triple; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( @triple ) >> =cut sub new { my $class = shift; my @triple = splice(@_, 0, 3); my $keys = shift || {}; my $self = $class->SUPER::new( @triple ); $self->[0]{logging_keys} = $keys; ### the next two loops look for repeated variables because some backends ### can't distinguish a pattern like { ?a ?a ?b } ### from { ?a ?b ?c }. if we find repeated variables (there can be at most ### one since there are only three nodes in a triple), we save the positions ### in the triple that hold the variable, and the code in next() will filter ### out any results that don't have the same value in those positions. ### ### in the first pass, we also set up the mapping that will let us pull out ### values from the result triples to construct result bindings. my %var_to_position; my @methodmap = qw(subject predicate object); my %counts; my $dup_var; foreach my $idx (0 .. 2) { my $node = $triple[ $idx ]; if (blessed($node) and $node->isa('RDF::Trine::Node::Variable')) { my $name = $node->name; $var_to_position{ $name } = $methodmap[ $idx ]; $counts{ $name }++; if ($counts{ $name } >= 2) { $dup_var = $name; } } } $self->[0]{referenced_variables} = [ keys %counts ]; my @positions; if (defined($dup_var)) { foreach my $idx (0 .. 2) { my $var = $triple[ $idx ]; if (blessed($var) and $var->isa('RDF::Trine::Node::Variable')) { my $name = $var->name; if ($name eq $dup_var) { push(@positions, $methodmap[ $idx ]); } } } } $self->[0]{mappings} = \%var_to_position; if (@positions) { $self->[0]{dups} = \@positions; } return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "TRIPLE plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.triple"); $l->trace( "executing RDF::Query::Plan::Triple" ); $self->[0]{start_time} = [gettimeofday]; my @triple = @{ $self }[ 1,2,3 ]; my $bound = $context->bound; if (%$bound) { foreach my $i (0 .. $#triple) { next unless ($triple[$i]->isa('RDF::Trine::Node::Variable')); next unless (blessed($bound->{ $triple[$i]->name })); $triple[ $i ] = $bound->{ $triple[$i]->name }; } } $l->trace( "- triple with bound values is " . join(', ', map { blessed($_) ? $_->sse : '_' } @triple) ); my $nil = RDF::Trine::Node::Nil->new(); # if we want the default graph to be a union of the named graphs, this should be undef instead my $iter = $context->model->get_statements( @triple[0..2], $nil ); if (blessed($iter)) { $self->[0]{iter} = $iter; $self->[0]{bound} = $bound; $self->[0]{logger} = $context->logger; $self->[0]{count} = 0; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open TRIPLE"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.triple"); my $iter = $self->[0]{iter}; LOOP: while (my $row = $iter->next) { if ($l->is_trace) { $l->trace( "- got triple from model: " . $row->as_string ); } if (my $pos = $self->[0]{dups}) { $l->trace( "- checking for duplicate variables in triple" ); my @pos = @$pos; my $first_method = shift(@pos); my $first = $row->$first_method(); foreach my $p (@pos) { unless ($first->equal( $row->$p() )) { next LOOP; } } } my $binding = {}; foreach my $key (keys %{ $self->[0]{mappings} }) { my $method = $self->[0]{mappings}{ $key }; $binding->{ $key } = $row->$method(); } my $pre_bound = $self->[0]{bound}; my $bindings = RDF::Query::VariableBindings->new( $binding ); if ($row->can('label')) { if (my $o = $row->label('origin')) { $bindings->label( origin => [ $o ] ); } } @{ $bindings }{ keys %$pre_bound } = values %$pre_bound; $self->[0]{count}++; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } return; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open TRIPLE"; } # my $l = Log::Log4perl->get_logger("rdf.query.plan.triple"); my $t0 = delete $self->[0]{start_time}; my $count = delete $self->[0]{count}; if (my $log = delete $self->[0]{logger}) { # $l->debug("logging triple execution statistics"); my $elapsed = tv_interval ( $t0 ); if (my $sparql = $self->logging_keys->{sparql}) { # $l->debug("- SPARQL: $sparql"); $log->push_key_value( 'execute_time-triple', $sparql, $elapsed ); $log->push_key_value( 'cardinality-triple', $sparql, $count ); # $l->debug("- elapsed: $elapsed"); # $l->debug("- count: $count"); } if (my $bf = $self->logging_keys->{bf}) { # $l->debug("- cardinality-bf-triple: $bf: $count"); $log->push_key_value( 'cardinality-bf-triple', $bf, $count ); } } delete $self->[0]{iter}; $self->SUPER::close(); } =item C<< nodes >> Returns a list of the three node objects that comprise the triple pattern this plan will return. =cut sub nodes { my $self = shift; return @{ $self }[1,2,3]; } =item C<< triple >> Returns a RDF::Trine::Statement object representing the triple pattern this plan will return. =cut sub triple { my $self = shift; my @nodes = $self->nodes; return RDF::Trine::Statement->new( @nodes ); } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $context = shift; my $bf = ''; my $bound = $context->bound; foreach my $n (@{ $self }[1,2,3]) { if ($n->isa('RDF::Trine::Node::Variable')) { if (my $b = $bound->{ $n->name }) { $bf .= 'b'; } else { $bf .= 'f'; } } else { $bf .= 'b'; } } return $bf; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'triple'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(N N N); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->nodes); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; $g->add_node( "$self", label => "Triple" . $self->graph_labels ); my @names = qw(subject predicate object); foreach my $i (0 .. 2) { my $n = $self->[ $i + 1 ]; my $rel = $names[ $i ]; my $str = $n->sse( {}, '' ); $g->add_node( "${self}$n", label => $str ); $g->add_edge( "$self" => "${self}$n", label => $names[ $i ] ); } return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Union.pm000644 000765 000024 00000011231 12173312155 020136 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Union # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Union - Executable query plan for unions. =head1 VERSION This document describes RDF::Query::Plan::Union version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Union; use strict; use warnings; use base qw(RDF::Query::Plan); use Scalar::Util qw(blessed refaddr); use RDF::Query::ExecutionContext; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $lhs, $rhs ) >> =cut sub new { my $class = shift; my ($lhs, $rhs) = @_; my $self = $class->SUPER::new( [ $lhs, $rhs ] ); my %vars; foreach my $v ($lhs->referenced_variables, $rhs->referenced_variables) { $vars{ $v }++; } $self->[0]{referenced_variables} = [ keys %vars ]; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "BGP plan can't be executed while already open"; } my $iter = $self->[1][0]; $iter->execute( $context ); if ($iter->state == $self->OPEN) { $self->[0]{iter} = $iter; $self->[0]{idx} = 0; $self->[0]{context} = $context; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } $self; } =item C<< next >> =cut sub next { my $self = shift; my $l = Log::Log4perl->get_logger("rdf.query.plan.union"); unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open BGP"; } my $iter = $self->[0]{iter}; return undef unless ($iter); my $row = $iter->next; if (defined($row)) { $l->trace( "union row: $row" ); if (my $d = $self->delegate) { $d->log_result( $self, $row ); } return $row; } else { $self->[0]{iter} = undef; if ($self->[0]{idx} < $#{ $self->[1] }) { $iter->close(); $self->[0]{idx}++; my $index = $self->[0]{idx}; my $iter = $self->[1][ $index ]; $iter->execute( $self->[0]{context} ); if ($iter->state == $self->OPEN) { $l->trace( "union moving to next branch" ); $self->[0]{iter} = $iter; my $bindings = $self->next; if (my $d = $self->delegate) { $d->log_result( $self, $bindings ); } return $bindings; } else { throw RDF::Query::Error::ExecutionError -text => "execute() on RHS of UNION failed during next()"; } } else { $l->trace( "union reached end of last branch" ); $iter->close(); delete $self->[0]{iter}; return undef; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open BGP"; } if (my $iter = $self->[0]{iter}) { $iter->close(); delete $self->[0]{iter}; delete $self->[0]{idx}; } $self->SUPER::close(); } =item C<< lhs >> Returns the left-hand-side plan to the union. =cut sub lhs { my $self = shift; return $self->[1][0]; } =item C<< rhs >> Returns the right-hand-side plan to the union. =cut sub rhs { my $self = shift; return $self->[1][1]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 0; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'union'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(P P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; my $expr = $self->[2]; return ($self->lhs, $self->rhs); } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs); $g->add_node( "$self", label => "Union" . $self->graph_labels ); $g->add_edge( "$self", $l ); $g->add_edge( "$self", $r ); return "$self"; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Update.pm000644 000765 000024 00000017130 12173312155 020274 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Update # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Update - Executable query plan for DELETE/INSERT operations. =head1 VERSION This document describes RDF::Query::Plan::Update version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Update; use strict; use warnings; use base qw(RDF::Query::Plan); use Log::Log4perl; use Scalar::Util qw(blessed refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; use RDF::Query::VariableBindings; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< new ( $delete_template, $insert_template, $pattern, \%dataset ) >> =cut sub new { my $class = shift; my $delete = shift; my $insert = shift; my $pattern = shift; my $dataset = shift; my $self = $class->SUPER::new( $delete, $insert, $pattern, $dataset ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "UPDATE plan can't be executed while already open"; } my $insert_template = $self->insert_template; my $delete_template = $self->delete_template; my $plan = $self->pattern; if ($self->dataset) { my $ds = $context->model->dataset_model( %{ $self->dataset } ); $context = $context->copy( model => $ds ); } $plan->execute( $context ); if ($plan->state == $self->OPEN) { my $l = Log::Log4perl->get_logger("rdf.query.plan.update"); $l->trace( "executing RDF::Query::Plan::Update" ); my @rows; while (my $row = $plan->next) { $l->trace("Update row: $row"); push(@rows, $row); } my @operations = ( [$delete_template, 'remove_statements'], [$insert_template, 'add_statement'], ); foreach my $data (@operations) { my ($template, $method) = @$data; $l->trace("UPDATE running $method"); foreach my $row (@rows) { my @triples = blessed($template) ? $template->quads : (); TRIPLE: foreach my $t (@triples) { my @nodes = $t->nodes; for my $i (0 .. $#nodes) { if ($nodes[$i]->isa('RDF::Trine::Node::Variable')) { my $name = $nodes[$i]->name; if ($method eq 'remove_statements') { if (exists($row->{ $name })) { $nodes[$i] = $row->{ $name }; } else { next TRIPLE; } } else { $nodes[$i] = $row->{ $name }; } } elsif ($nodes[$i]->isa('RDF::Trine::Node::Blank')) { my $id = $nodes[$i]->blank_identifier; unless (exists($self->[0]{blank_map}{ $id })) { if ($method eq 'remove_statements') { $self->[0]{blank_map}{ $id } = RDF::Query::Node::Variable->new(); } else { $self->[0]{blank_map}{ $id } = RDF::Query::Node::Blank->new(); } } $nodes[$i] = $self->[0]{blank_map}{ $id }; } } # my $ok = 1; foreach my $i (0 .. 3) { my $n = $nodes[ $i ]; if (not blessed($n)) { if ($i == 3) { $nodes[ $i ] = RDF::Trine::Node::Nil->new(); } else { next TRIPLE; # $nodes[ $i ] = RDF::Query::Node::Variable->new(); } # $ok = 0; # } elsif ($n->isa('RDF::Trine::Node::Variable')) { # $ok = 0; } } # next unless ($ok); my $st = (scalar(@nodes) == 4) ? RDF::Trine::Statement::Quad->new( @nodes ) : RDF::Trine::Statement->new( @nodes ); $l->trace( "$method: " . $st->as_string ); if ($method eq 'remove_statements') { $context->model->$method( $st->nodes ); } else { $context->model->$method( $st ); } } } } $self->[0]{ok} = 1; $self->state( $self->OPEN ); } else { warn "could not execute Update pattern plan"; } $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open UPDATE"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.update"); $self->close(); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{ok} ); } return $self->[0]{ok}; } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open UPDATE"; } delete $self->[0]{ok}; $self->SUPER::close(); } =item C<< delete_template >> Returns the algebra object representing the RDF template to delete. =cut sub delete_template { my $self = shift; return $self->[1]; } =item C<< insert_template >> Returns the algebra object representing the RDF template to insert. =cut sub insert_template { my $self = shift; return $self->[2]; } =item C<< pattern >> Returns the pattern plan object. =cut sub pattern { my $self = shift; return $self->[3]; } =item C<< dataset >> Returns the dataset HASH reference. =cut sub dataset { my $self = shift; return $self->[4]; } =item C<< distinct >> Returns true if the pattern is guaranteed to return distinct results. =cut sub distinct { return 1; } =item C<< ordered >> Returns true if the pattern is guaranteed to return ordered results. =cut sub ordered { return []; } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { return 'update'; } =item C<< plan_prototype >> Returns a list of scalar identifiers for the type of the content (children) nodes of this plan node. See L for a list of the allowable identifiers. =cut sub plan_prototype { my $self = shift; return qw(A A P); } =item C<< plan_node_data >> Returns the data for this plan node that corresponds to the values described by the signature returned by C<< plan_prototype >>. =cut sub plan_node_data { my $self = shift; return ($self->delete_template, $self->insert_template, $self->pattern); } =item C<< explain >> Returns a string serialization of the algebra appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->plan_node_name; my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); if (my $d = $self->delete_template) { $string .= "${indent}${s}delete:\n"; $string .= $d->explain( $s, $count+2 ); } if (my $i = $self->insert_template) { $string .= "${indent}${s}insert:\n"; $string .= $i->explain( $s, $count+2 ); } if (my $p = $self->pattern) { if ($p->isa('RDF::Query::Plan::Constant') and $p->is_unit) { } else { $string .= "${indent}${s}where:\n"; $string .= $p->explain( $s, $count+2 ); } } return $string; } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $label = $self->graph_labels; my $url = $self->url->uri_value; throw RDF::Query::Error::ExecutionError -text => "RDF::Query::Plan::Update->graph not implemented."; # $g->add_node( "$self", label => "delete" . $self->graph_labels ); # $g->add_node( "${self}$url", label => $url ); # $g->add_edge( "$self" => "${self}$url", label => 'url' ); # return "$self"; } =item C<< is_update >> Returns true if the plan represents an update operation. =cut sub is_update { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Join/NestedLoop.pm000644 000765 000024 00000015703 12173312155 022031 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Join::NestedLoop # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Join::NestedLoop - Executable query plan for nested loop joins. =head1 VERSION This document describes RDF::Query::Plan::Join::NestedLoop version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Join::NestedLoop; use strict; use warnings; use base qw(RDF::Query::Plan::Join); use Log::Log4perl; use Scalar::Util qw(blessed); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Query::Error qw(:try); use RDF::Query::ExecutionContext; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; $RDF::Query::Plan::Join::JOIN_CLASSES{ 'RDF::Query::Plan::Join::NestedLoop' }++; } ###################################################################### =item C<< new ( $lhs, $rhs, $opt, [ \%logging_keys ] ) >> =cut sub new { my $class = shift; my $lhs = shift; my $rhs = shift; my $opt = shift; my $keys = shift; if ($opt) { throw RDF::Query::Error::MethodInvocationError -text => "NestedLoop join does not support optional joins (use PushDownNestedLoop instead)"; } my $self = $class->SUPER::new( $lhs, $rhs, $opt ); $self->[0]{logging_keys} = $keys; return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "NestedLoop join plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.join.nestedloop"); $self->[0]{start_time} = [gettimeofday]; # if ($self->optional) { # my (@inner, @outer); # $self->rhs->execute( $context ); # while (my $row = $self->rhs->next) { # $l->trace("loading inner row: " . $row); # push(@inner, $row); # } # # my @results; # $self->lhs->execute( $context ); # while (my $outer = $self->lhs->next) { # $l->trace("loading outer row: " . $outer); # my $count = 0; # foreach my $inner (@inner) { # if (my $joined = $inner->join( $outer )) { # $count++; # if ($l->is_trace) { # $l->trace("joined bindings: $outer ⋈ $inner"); # } # # warn "-> joined\n"; # $self->[0]{count}++; # push(@results, $joined); # } # } # if ($count == 0) { # # left-join branch # push(@results, $outer); # } # } # # warn Dumper(\@results); # # $self->[0]{results} = \@results; # } else { my @inner; $self->rhs->execute( $context ); while (my $row = $self->rhs->next) { $l->trace("loading inner row cache with: " . $row); push(@inner, $row); } $self->lhs->execute( $context ); if ($self->lhs->state == $self->OPEN) { $self->[0]{inner} = \@inner; $self->[0]{outer} = $self->lhs; $self->[0]{inner_index} = 0; $self->[0]{needs_new_outer} = 1; $self->[0]{inner_count} = 0; $self->[0]{count} = 0; $self->[0]{logger} = $context->logger; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } # } # warn '########################################'; $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open NestedLoop join"; } # if ($self->optional) { # my $result = shift(@{ $self->[0]{results} }); # if (my $d = $self->delegate) { # $d->log_result( $self, $result ); # } # return $result; # } my $outer = $self->[0]{outer}; my $inner = $self->[0]{inner}; my $l = Log::Log4perl->get_logger("rdf.query.plan.join.nestedloop"); while (1) { if ($self->[0]{needs_new_outer}) { $self->[0]{outer_row} = $outer->next; if (ref($self->[0]{outer_row})) { $self->[0]{needs_new_outer} = 0; $self->[0]{inner_index} = 0; $self->[0]{inner_count} = 0; # warn "got new outer row: " . Dumper($self->[0]{outer_row}); } else { # we've exhausted the outer iterator. we're now done. # warn "exhausted"; return undef; } } while ($self->[0]{inner_index} < scalar(@$inner)) { my $inner_row = $inner->[ $self->[0]{inner_index}++ ]; # warn "using inner row: " . Dumper($inner_row); if (my $joined = $inner_row->join( $self->[0]{outer_row} )) { if ($l->is_trace) { $l->trace("joined bindings: $inner_row ⋈ $self->[0]{outer_row}"); } # warn "-> joined\n"; $self->[0]{inner_count}++; $self->[0]{count}++; if (my $d = $self->delegate) { $d->log_result( $self, $joined ); } return $joined; } else { $l->trace("failed to join bindings: $inner_row ⋈ $self->[0]{outer_row}"); } } $self->[0]{needs_new_outer} = 1; } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open NestedLoop join"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.join.nestedloop"); my $t0 = delete $self->[0]{start_time}; my $count = delete $self->[0]{count}; if (my $log = delete $self->[0]{logger}) { $l->debug("logging nestedloop join execution statistics"); my $elapsed = tv_interval ( $t0 ); if (my $sparql = $self->logging_keys->{sparql}) { if ($l->is_trace) { $l->trace("- SPARQL: $sparql"); $l->trace("- elapsed: $elapsed"); $l->trace("- count: $count"); } $log->push_key_value( 'execute_time-nestedloop', $sparql, $elapsed ); $log->push_key_value( 'cardinality-nestedloop', $sparql, $count ); } if (my $bf = $self->logging_keys->{bf}) { if ($l->is_trace) { $l->trace("- bf: $bf"); } $log->push_key_value( 'cardinality-bf-nestedloop', $bf, $count ); } } delete $self->[0]{inner}; delete $self->[0]{outer}; delete $self->[0]{inner_index}; delete $self->[0]{needs_new_outer}; delete $self->[0]{inner_count}; $self->lhs->close(); $self->rhs->close(); $self->SUPER::close(); } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { my $self = shift; my $jtype = $self->optional ? 'leftjoin' : 'join'; return "nestedloop-$jtype"; } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $jtype = $self->optional ? 'Left Join' : 'Join'; my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs); $g->add_node( "$self", label => "$jtype (NL)" . $self->graph_labels ); $g->add_edge( "$self", $l ); $g->add_edge( "$self", $r ); return "$self"; } package RDF::Query::Plan::Join::NestedLoop::Left; use strict; use warnings; use base qw(RDF::Query::Plan::Join::NestedLoop); sub new { my $class = shift; my $lhs = shift; my $rhs = shift; return $class->SUPER::new( $lhs, $rhs, 1 ); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Plan/Join/PushDownNestedLoop.pm000644 000765 000024 00000016115 12173312155 023517 0ustar00gregstaff000000 000000 # RDF::Query::Plan::Join::PushDownNestedLoop # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Plan::Join::PushDownNestedLoop - Executable query plan for nested loop joins. =head1 VERSION This document describes RDF::Query::Plan::Join::PushDownNestedLoop version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Plan::Join::PushDownNestedLoop; use strict; use warnings; use base qw(RDF::Query::Plan::Join); use Scalar::Util qw(blessed refaddr); use Data::Dumper; ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; $RDF::Query::Plan::Join::JOIN_CLASSES{ 'RDF::Query::Plan::Join::PushDownNestedLoop' }++; } ###################################################################### use RDF::Query::ExecutionContext; =item C<< new ( $lhs, $rhs, $opt ) >> =cut sub new { my $class = shift; my $lhs = shift; my $rhs = shift; if ($rhs->isa('RDF::Query::Plan::SubSelect')) { throw RDF::Query::Error::MethodInvocationError -text => "Subselects cannot be the RHS of a PushDownNestedLoop join"; } my $opt = shift || 0; if (not($opt) and $rhs->isa('RDF::Query::Plan::Join') and $rhs->optional) { # we can't push down results to an optional pattern because of the # bottom up semantics. see dawg test algebra/manifest#join-scope-1 # for example. throw RDF::Query::Error::MethodInvocationError -text => "PushDownNestedLoop join does not support optional patterns as RHS due to bottom-up variable scoping rules (use NestedLoop instead)"; } if ($rhs->sse =~ /aggregate/sm) { throw RDF::Query::Error::MethodInvocationError -text => "PushDownNestedLoop join does not support aggregates in the RHS due to aggregate group fragmentation"; } my $self = $class->SUPER::new( $lhs, $rhs, $opt ); return $self; } =item C<< execute ( $execution_context ) >> =cut sub execute ($) { my $self = shift; my $context = shift; $self->[0]{delegate} = $context->delegate; if ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "PushDownNestedLoop join plan can't be executed while already open"; } my $l = Log::Log4perl->get_logger("rdf.query.plan.join.pushdownnestedloop"); $l->trace("executing bind join with plans:"); $l->trace($self->lhs->sse); $l->trace($self->rhs->sse); $self->lhs->execute( $context ); if ($self->lhs->state == $self->OPEN) { delete $self->[0]{stats}; $self->[0]{context} = $context; $self->[0]{outer} = $self->lhs; $self->[0]{needs_new_outer} = 1; $self->[0]{inner_count} = 0; $self->state( $self->OPEN ); } else { warn "no iterator in execute()"; } # warn '########################################'; $self; } =item C<< next >> =cut sub next { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PushDownNestedLoop join"; } my $outer = $self->[0]{outer}; my $inner = $self->rhs; my $opt = $self->[3]; my $l = Log::Log4perl->get_logger("rdf.query.plan.join.pushdownnestedloop"); while (1) { if ($self->[0]{needs_new_outer}) { $self->[0]{outer_row} = $outer->next; my $outer = $self->[0]{outer_row}; if (ref($outer)) { $self->[0]{stats}{outer_rows}++; my $context = $self->[0]{context}; $self->[0]{needs_new_outer} = 0; $self->[0]{inner_count} = 0; if ($self->[0]{inner}) { $self->[0]{inner}->close(); } my %bound = %{ $context->bound }; @bound{ keys %$outer } = values %$outer; my $copy = $context->copy( bound => \%bound ); $l->trace( "executing inner plan with bound: " . Dumper(\%bound) ); if ($inner->state == $inner->OPEN) { $inner->close(); } $self->[0]{inner} = $inner->execute( $copy ); } else { # we've exhausted the outer iterator. we're now done. $l->trace("exhausted outer plan in bind join"); return undef; } } while (defined(my $inner_row = $self->[0]{inner}->next)) { $self->[0]{stats}{inner_rows}++; $l->trace( "using inner row: " . $inner_row->as_string ); if (defined(my $joined = $inner_row->join( $self->[0]{outer_row} ))) { $self->[0]{stats}{results}++; if ($l->is_trace) { $l->trace("joined bindings: $inner_row ⋈ $self->[0]{outer_row}"); } # warn "-> joined\n"; $self->[0]{inner_count}++; if (my $d = $self->delegate) { $d->log_result( $self, $joined ); } return $joined; } else { $l->trace("failed to join bindings: $inner_row |><| $self->[0]{outer_row}"); if ($opt) { $l->trace( "--> but operation is OPTIONAL, so returning $self->[0]{outer_row}" ); if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{outer_row} ); } return $self->[0]{outer_row}; } } } $self->[0]{needs_new_outer} = 1; if ($self->[0]{inner}->state == $self->OPEN) { $self->[0]{inner}->close(); } delete $self->[0]{inner}; if ($opt and $self->[0]{inner_count} == 0) { if (my $d = $self->delegate) { $d->log_result( $self, $self->[0]{outer_row} ); } return $self->[0]{outer_row}; } } } =item C<< close >> =cut sub close { my $self = shift; unless ($self->state == $self->OPEN) { throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PushDownNestedLoop join"; } delete $self->[0]{inner}; delete $self->[0]{outer}; delete $self->[0]{needs_new_outer}; delete $self->[0]{inner_count}; if (blessed($self->lhs) and $self->lhs->state == $self->lhs->OPEN) { $self->lhs->close(); } $self->SUPER::close(); } =item C<< plan_node_name >> Returns the string name of this plan node, suitable for use in serialization. =cut sub plan_node_name { my $self = shift; my $jtype = $self->optional ? 'leftjoin' : 'join'; return "bind-$jtype"; } =item C<< graph ( $g ) >> =cut sub graph { my $self = shift; my $g = shift; my $jtype = $self->optional ? 'Left Join' : 'Join'; my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs); $g->add_node( "$self", label => "$jtype (Bind)" . $self->graph_labels ); $g->add_edge( "$self", $l ); $g->add_edge( "$self", $r ); return "$self"; } =item C<< explain >> Returns a string serialization of the plan appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $type = $self->plan_node_name; my $stats = ''; if ($self->[0]{stats}) { $stats = sprintf(' [%d/%d/%d]', @{ $self->[0]{stats} }{qw(outer_rows inner_rows results)}); } my $string = sprintf("%s%s%s (0x%x)\n", $indent, $type, $stats, refaddr($self)); foreach my $p ($self->plan_node_data) { $string .= $p->explain( $s, $count+1 ); } return $string; } package RDF::Query::Plan::Join::PushDownNestedLoop::Left; use strict; use warnings; use base qw(RDF::Query::Plan::Join::PushDownNestedLoop); sub new { my $class = shift; my $lhs = shift; my $rhs = shift; return $class->SUPER::new( $lhs, $rhs, 1 ); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Parser/RDQL.pm000644 000765 000024 00000035030 12173312155 020155 0ustar00gregstaff000000 000000 # RDF::Query::Parser::RDQL # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Parser::RDQL - An RDQL parser for RDF::Query =head1 VERSION This document describes RDF::Query::Parser::RDQL version 2.910. =cut package RDF::Query::Parser::RDQL; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Parser); use Data::Dumper; use Parse::RecDescent; use Carp qw(carp croak confess); use RDF::Query::Error qw(:try); use Scalar::Util qw(blessed); ###################################################################### our ($VERSION, $lang, $languri); BEGIN { $::RD_TRACE = undef; $::RD_HINT = undef; $VERSION = '2.910'; $lang = 'rdql'; $languri = 'http://jena.hpl.hp.com/2003/07/query/RDQL'; } our($RDQL_GRAMMAR); BEGIN { our $RDQL_GRAMMAR = <<'END'; query: 'SELECT' variable(s) SourceClause(?) 'WHERE' triplepattern(s) constraints(?) OptOrderBy(?) prefixes(?) { my $triples = RDF::Query::Algebra::GroupGraphPattern->new( @{ $item[5] } ); my $filter = ($item[6][0] || []); if (scalar(@$filter)) { $triples = RDF::Query::Parser->new_filter( $filter, $triples ); } $return = { method => 'SELECT', variables => $item[2], sources => $item[3][0], triples => [ $triples ], namespaces => (scalar(@{$item[8]}) ? $item[8][0] : {}) }; if (@{ $item[7] }) { $return->{options}{orderby} = $item[9][0]; } } prefixes: 'USING' namespaces { $return = $item[2] } OptOrderBy: 'ORDER BY' orderbyvariable(s) { $return = $item[2] } orderbyvariable: variable { $return = ['ASC', $item[1]] } | /ASC|DESC/i '[' variable ']' { $return = [uc($item[1]), $item[3]] } SourceClause: ('SOURCE' | 'FROM') Source(s) { $return = $item[2] } Source: URI { $return = [$item[1]] } variable: '?' identifier { $return = RDF::Query::Parser->new_variable($item[2]) } triplepattern: '(' VarUri VarUri VarUriConst ')' { $return = RDF::Query::Parser::RDQL::Triple->new(@item[2,3,4]) } constraints: 'AND' Expression OptExpression(s?) { if (scalar(@{ $item[3] })) { my ($op, $expr) = @{ $item[3][0] }; $return = RDF::Query::Parser->new_function_expression( $op, $item[2], $expr ); } else { $return = $item[2]; } } OptExpression: (',' | 'AND') Expression { $return = [ 'sparql:logical-and', $item[2] ]; } Expression: CondOrExpr { $return = $item[1] } CondOrExpr: CondAndExpr CondOrExprOrPart(?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_function_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } CondOrExprOrPart: '||' CondAndExpr { $return = [ 'sparql:logical-or', $item[2] ] } CondAndExpr: ValueLogical CondAndExprAndPart(?) { if (scalar(@{ $item[2] })) { $return = RDF::Query::Parser->new_function_expression( 'sparql:logical-and', $item[1], $item[2][0][1] ); } else { $return = $item[1]; } } CondAndExprAndPart: '&&' ValueLogical { $return = [ @item[1,2] ] } ValueLogical: StringEqualityExpression { $return = $item[1] } StringEqualityExpression: NumericalLogical StrEqExprPart(s?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; if ($op eq '~~') { $return = RDF::Query::Parser->new_function_expression( 'sparql:regex', $item[1], $expr ); } else { $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } } else { $return = $item[1]; } } StrEqExprPart: ('==' | '!=' | '=~' | '~~') NumericalLogical { $return = [ @item[1,2] ] } NumericalLogical: InclusiveOrExpression { $return = $item[1] } InclusiveOrExpression: ExclusiveOrExpression InclusiveOrExprPart(s?) { if (scalar(@{ $item[2] })) { $return = [ $item[2][0][0], $item[1], $item[2][0][1] ]; } else { $return = $item[1]; } } InclusiveOrExprPart: '|' ExclusiveOrExpression { $return = [ @item[1,2] ] } ExclusiveOrExpression: AndExpression ExclusiveOrExprPart(s?) { if (scalar(@{ $item[2] })) { $return = [ $item[2][0][0], $item[1], map { $_->[1] } @{ $item[2] } ]; } else { $return = $item[1]; } } ExclusiveOrExprPart: '^' AndExpression { $return = [ @item[1,2] ] } AndExpression: ArithmeticCondition AndExprPart(s?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } AndExprPart: '&' ArithmeticCondition { $return = [ @item[1,2] ] } ArithmeticCondition: EqualityExpression { $return = $item[1]; } EqualityExpression: RelationalExpression EqualityExprPart(?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } EqualityExprPart: /(==|!=)/ RelationalExpression { $return = [ @item[1,2] ] } RelationalExpression: NumericExpression RelationalExprPart(?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } RelationalExprPart: /(<|>|<=|>=)/ NumericExpression { $return = [ @item[1,2] ] } NumericExpression: MultiplicativeExpression NumericExprPart(s?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } NumericExprPart: /([-+])/ MultiplicativeExpression { $return = [ @item[1,2] ] } MultiplicativeExpression: UnaryExpression MultExprPart(s?) { if (scalar(@{ $item[2] })) { my ($op, $expr) = @{ $item[2][0] }; $return = RDF::Query::Parser->new_binary_expression( $op, $item[1], $expr ); } else { $return = $item[1]; } } MultExprPart: /([\/*])/ UnaryExpression { $return = [ @item[1,2] ] } UnaryExpression: UnaryExprNotPlusMinus { $return = $item[1] } | /([-+])/ UnaryExpression { $return = [ @item[1,2] ] } UnaryExprNotPlusMinus: /([~!])/ UnaryExpression { $return = [ @item[1,2] ] } | PrimaryExpression { $return = $item[1] } PrimaryExpression: (VarUriConst | FunctionCall) { $return = $item[1] } | '(' Expression ')' { $return = $item[2]; } FunctionCall: identifier '(' ArgList ')' { $return = [ 'function', map { @{ $_ } } @item[1,3] ] } ArgList: VarUriConst MoreArg(s) { $return = [ $item[1], @{ $item[2] } ] } MoreArg: "," VarUriConst { $return = $item[2] } Literal: (URI | CONST) { $return = $item[1] } URL: qURI { $return = $item[1] } VarUri: (variable | URI) { $return = $item[1] } VarUriConst: (variable | CONST | URI) { $return = $item[1] } namespaces: namespace morenamespace(s?) { $return = { map { %{ $_ } } ($item[1], @{ $item[2] }) } } morenamespace: OptComma namespace { $return = $item[2] } namespace: identifier 'FOR' qURI { $return = {@item[1,3]} } OptComma: ',' | '' identifier: /(([a-zA-Z0-9_.-])+)/ { $return = $1 } URI: qURI { $return = RDF::Query::Parser->new_uri( $item[1] ) } | QName { $return = RDF::Query::Parser::RDQL::URI->new( $item[1] ) } qURI: '<' /[A-Za-z0-9_.!~*'()%;\/?:@&=+,#\$-]+/ '>' { $return = $item[2] } QName: identifier ':' /([^ \t<>()]+)/ { $return = [@item[1,3]] } CONST: Text { $return = RDF::Query::Parser->new_literal($item[1]) } | Number { $return = RDF::Query::Parser->new_literal($item[1], undef, ($item[1] =~ /[.]/ ? 'http://www.w3.org/2001/XMLSchema#float' : 'http://www.w3.org/2001/XMLSchema#integer')) } Number: /([0-9]+(\.[0-9]+)?)/ { $return = $item[1] } Text: dQText | sQText | Pattern { $return = $item[1] } sQText: "'" /([^']+)/ '"' { $return = $item[2] } dQText: '"' /([^"]+)/ '"' { $return = $item[2] } Pattern: '/' /([^\/]+(?:\\.[^\/]*)*)/ '/' { $return = $item[2] } END } ###################################################################### =head1 METHODS =over 4 =item C Returns a new RDF::Query object. =cut { my $parser; sub new { my $class = shift; unless ($parser) { $parser = new Parse::RecDescent ($RDQL_GRAMMAR); } my $self = bless( { parser => $parser }, $class ); return $self; } } =item C Parses the supplied RDQL query string, returning a parse tree. =cut sub parse { my $self = shift; my $query = shift; my $parser = $self->parser; my $parsed = $parser->query( $query ); if ($parsed) { my $pattern = $parsed->{triples}[0]; if (blessed($pattern)) { my $ns = $parsed->{namespaces}; $pattern = $self->_fixup_pattern( $pattern, $ns ); my $fixed = $pattern->qualify_uris( $ns ); $parsed->{triples}[0] = $fixed; } $pattern = RDF::Query::Algebra::Project->new( $parsed->{triples}[0], $parsed->{variables} ); $parsed->{triples}[0] = $pattern; return $parsed; } else { return $self->fail( "Failed to parse: '$query'" ); } } sub _fixup_pattern { my $self = shift; my $pattern = shift; my $ns = shift; my @uris = $pattern->subpatterns_of_type('RDF::Query::Parser::RDQL::URI'); foreach my $u (@uris) { my $ns = $ns->{ $u->[0] }; my $uri = join('', $ns, $u->[1]); @{ $u } = ( 'URI', $uri ); bless($u, 'RDF::Query::Node::Resource'); # evil } my @triples = $pattern->subpatterns_of_type('RDF::Query::Parser::RDQL::Triple'); foreach my $t (@triples) { bless($t, 'RDF::Query::Algebra::Triple'); # evil } return $pattern; } sub AUTOLOAD { my $self = $_[0]; throw RDF::Query::Error::MethodInvocationError unless (blessed($self)); my $class = ref($_[0]); our $AUTOLOAD; return if ($AUTOLOAD =~ /DESTROY$/); my $method = $AUTOLOAD; $method =~ s/^.*://; if (exists($self->{ $method })) { no strict 'refs'; *$AUTOLOAD = sub { my $self = shift; my $class = ref($self); return $self->{ $method }; }; goto &$method; } else { throw RDF::Query::Error::MethodError ( -text => qq[Can't locate object method "$method" via package $class] ); } } package RDF::Query::Parser::RDQL::URI; use strict; use warnings; use base qw(RDF::Query::Algebra); sub new { my $class = shift; my $data = shift; my ($ns, $local) = @{ $data }; return bless([$ns, $local], $class); } sub construct_args { my $self = shift; return [ @$self ]; } package RDF::Query::Parser::RDQL::Triple; use strict; use warnings; use base qw(RDF::Query::Algebra); sub new { my $class = shift; my @nodes = @_; return bless([@nodes], $class); } sub construct_args { my $self = shift; return @$self; } 1; __END__ =back =head1 REVISION HISTORY $Log$ Revision 1.5 2006/01/11 06:03:45 greg - Removed use of Data::Dumper::Simple. Revision 1.4 2005/05/08 08:26:09 greg - Added initial support for SPARQL ASK, DESCRIBE and CONSTRUCT queries. - Added new test files for new query types. - Added methods to bridge classes for creating statements and blank nodes. - Added as_string method to bridge classes for getting string versions of nodes. - Broke out triple fixup code into fixup_triple_bridge_variables(). - Updated FILTER test to use new Geo::Distance API. Revision 1.3 2005/04/26 02:54:40 greg - added core support for custom function constraints support - added initial SPARQL support for custom function constraints - SPARQL variables may now begin with the '$' sigil - broke out URL fixups into its own method - added direction support for ORDER BY (ascending/descending) - added 'next', 'current', and 'end' to Stream API Revision 1.2 2005/04/25 00:59:29 greg - streams are now objects usinig the Redland QueryResult API - RDF namespace is now always available in queries - row() now uses a stream when calling execute() - check_constraints() now copies args for recursive calls (instead of pass-by-ref) - added ORDER BY support to RDQL parser - SPARQL constraints now properly use the 'FILTER' keyword - SPARQL constraints can now use '&&' as an operator - SPARQL namespace declaration is now optional Revision 1.1 2005/04/21 02:21:44 greg - major changes (resurecting the project) - broke out the query parser into it's own RDQL class - added initial support for a SPARQL parser - added support for blank nodes - added lots of syntactic sugar (with blank nodes, multiple predicates and objects) - moved model-specific code into RDF::Query::Model::* - cleaned up the model-bridge code - moving over to redland's query API (pass in the model when query is executed) =head1 AUTHOR Gregory Williams =cut RDF-Query-2.910/lib/RDF/Query/Parser/SPARQL.pm000644 000765 000024 00000140743 12173312155 020425 0ustar00gregstaff000000 000000 # RDF::Query::Parser::SPARQL # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Parser::SPARQL - SPARQL Parser. =head1 VERSION This document describes RDF::Query::Parser::SPARQL version 2.910. =head1 SYNOPSIS use RDF::Query::Parser::SPARQL; my $parser = RDF::Query::Parse::SPARQL->new(); my $iterator = $parser->parse( $query, $base_uri ); =head1 DESCRIPTION ... =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Parser::SPARQL; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Parser); use URI; use Data::Dumper; use RDF::Query::Error qw(:try); use RDF::Query::Parser; use RDF::Query::Algebra; use RDF::Trine::Namespace qw(rdf); use Scalar::Util qw(blessed looks_like_number); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#'); our $r_ECHAR = qr/\\([tbnrf\\"'])/; our $r_STRING_LITERAL1 = qr/'(([^\x{27}\x{5C}\x{0A}\x{0D}])|${r_ECHAR})*'/; our $r_STRING_LITERAL2 = qr/"(([^\x{22}\x{5C}\x{0A}\x{0D}])|${r_ECHAR})*"/; our $r_STRING_LITERAL_LONG1 = qr/'''(('|'')?([^'\\]|${r_ECHAR}))*'''/; our $r_STRING_LITERAL_LONG2 = qr/"""(("|"")?([^"\\]|${r_ECHAR}))*"""/; our $r_LANGTAG = qr/@[a-zA-Z]+(-[a-zA-Z0-9]+)*/; our $r_IRI_REF = qr/<([^<>"{}|^`\\\x{00}-\x{20}])*>/; our $r_PN_CHARS_BASE = qr/([A-Z]|[a-z]|[\x{00C0}-\x{00D6}]|[\x{00D8}-\x{00F6}]|[\x{00F8}-\x{02FF}]|[\x{0370}-\x{037D}]|[\x{037F}-\x{1FFF}]|[\x{200C}-\x{200D}]|[\x{2070}-\x{218F}]|[\x{2C00}-\x{2FEF}]|[\x{3001}-\x{D7FF}]|[\x{F900}-\x{FDCF}]|[\x{FDF0}-\x{FFFD}]|[\x{10000}-\x{EFFFF}])/; our $r_PN_CHARS_U = qr/(_|${r_PN_CHARS_BASE})/; our $r_VARNAME = qr/((${r_PN_CHARS_U}|[0-9])(${r_PN_CHARS_U}|[0-9]|\x{00B7}|[\x{0300}-\x{036F}]|[\x{203F}-\x{2040}])*)/; our $r_VAR1 = qr/[?]${r_VARNAME}/; our $r_VAR2 = qr/[\$]${r_VARNAME}/; our $r_PN_CHARS = qr/${r_PN_CHARS_U}|-|[0-9]|\x{00B7}|[\x{0300}-\x{036F}]|[\x{203F}-\x{2040}]/; our $r_PN_PREFIX = qr/(${r_PN_CHARS_BASE}((${r_PN_CHARS}|[.])*${r_PN_CHARS})?)/; our $r_PN_LOCAL = qr/((${r_PN_CHARS_U}|[0-9])((${r_PN_CHARS}|[.])*${r_PN_CHARS})?)/; our $r_PNAME_NS = qr/((${r_PN_PREFIX})?:)/; our $r_PNAME_LN = qr/(${r_PNAME_NS}${r_PN_LOCAL})/; our $r_EXPONENT = qr/[eE][-+]?\d+/; our $r_DOUBLE = qr/\d+[.]\d*${r_EXPONENT}|[.]\d+${r_EXPONENT}|\d+${r_EXPONENT}/; our $r_DECIMAL = qr/(\d+[.]\d*)|([.]\d+)/; our $r_INTEGER = qr/\d+/; our $r_BLANK_NODE_LABEL = qr/_:${r_PN_LOCAL}/; our $r_ANON = qr/\[[\t\r\n ]*\]/; our $r_NIL = qr/\([\n\r\t ]*\)/; =item C<< new >> Returns a new Turtle parser. =cut sub new { my $class = shift; my $self = bless({ bindings => {}, bnode_id => 0, }, $class); return $self; } ################################################################################ =item C<< parse ( $query, $base_uri ) >> Parses the C<< $query >>, using the given C<< $base_uri >>. =cut sub parse { my $self = shift; my $input = shift; my $baseuri = shift; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = {}; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); $self->{build} = { sources => [], triples => $triples }; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_Query(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $self->{error} = $e->text; }; my $data = delete $self->{build}; # $data->{triples} = $self->_pop_pattern_container(); return $data; } =item C<< parse_pattern ( $pattern, $base_uri, \%namespaces ) >> Parses the C<< $pattern >>, using the given C<< $base_uri >> and returns a RDF::Query::Algebra pattern. =cut sub parse_pattern { my $self = shift; my $input = shift; my $baseuri = shift; my $ns = shift; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = $ns; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); $self->{build} = { sources => [], triples => $triples }; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_GroupGraphPattern(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $self->{error} = $e->text; }; my $data = delete $self->{build}; return $data->{triples}[0]; } =item C<< parse_expr ( $pattern, $base_uri, \%namespaces ) >> Parses the C<< $pattern >>, using the given C<< $base_uri >> and returns a RDF::Query::Expression pattern. =cut sub parse_expr { my $self = shift; my $input = shift; my $baseuri = shift; my $ns = shift; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = $ns; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); $self->{build} = { sources => [], triples => $triples }; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_Expression(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $self->{error} = $e->text; }; my $data = splice(@{ $self->{stack} }); return $data; } =item C<< error >> Returns the error encountered during the last parse. =cut sub error { my $self = shift; return $self->{error}; } sub _add_patterns { my $self = shift; my @triples = @_; my $container = $self->{ pattern_container_stack }[0]; push( @{ $container }, @triples ); } sub _remove_pattern { my $self = shift; my $container = $self->{ pattern_container_stack }[0]; my $pattern = pop( @{ $container } ); return $pattern; } sub _peek_pattern { my $self = shift; my $container = $self->{ pattern_container_stack }[0]; my $pattern = $container->[-1]; return $pattern; } sub _push_pattern_container { my $self = shift; my $cont = []; unshift( @{ $self->{ pattern_container_stack } }, $cont ); return $cont; } sub _pop_pattern_container { my $self = shift; my $cont = shift( @{ $self->{ pattern_container_stack } } ); return $cont; } sub _add_stack { my $self = shift; my @items = @_; push( @{ $self->{stack} }, @items ); } sub _add_filter { my $self = shift; my @filters = shift; push( @{ $self->{filters} }, @filters ); } sub _eat { my $self = shift; my $thing = shift; if (not(length($self->{tokens}))) { $self->_syntax_error("no tokens left"); } # if (substr($self->{tokens}, 0, 1) eq '^') { # Carp::cluck( "eating $thing with input $self->{tokens}" ); # } if (blessed($thing) and $thing->isa('Regexp')) { if ($self->{tokens} =~ /^$thing/) { my $match = $&; substr($self->{tokens}, 0, length($match)) = ''; return $match; } $self->_syntax_error( $thing ); } elsif (looks_like_number( $thing )) { my ($token) = substr( $self->{tokens}, 0, $thing, '' ); return $token } else { ### thing is a string if (substr($self->{tokens}, 0, length($thing)) eq $thing) { substr($self->{tokens}, 0, length($thing)) = ''; return $thing; } else { $self->_syntax_error( $thing ); } } print $thing; throw RDF::Query::Error; } sub _syntax_error { my $self = shift; my $thing = shift; my $expect = $thing; my $level = 2; while (my $sub = (caller($level++))[3]) { if ($sub =~ m/::_([A-Z]\w*)$/) { $expect = $1; last; } } my $l = Log::Log4perl->get_logger("rdf.query.parser.sparql"); if ($l->is_debug) { $l->logcluck("Syntax error eating $thing with input <<$self->{tokens}>>"); } throw RDF::Query::Error::ParseError -text => "Syntax error: Expected $expect"; } sub _test { my $self = shift; my $thing = shift; if (blessed($thing) and $thing->isa('Regexp')) { if ($self->{tokens} =~ m/^$thing/) { return 1; } else { return 0; } } else { if (substr($self->{tokens}, 0, length($thing)) eq $thing) { return 1; } else { return 0; } } } sub _ws_test { my $self = shift; unless (length($self->{tokens})) { return 0; } if ($self->{tokens} =~ m/^[\t\r\n #]/) { return 1; } else { return 0; } } sub _ws { my $self = shift; ### #x9 | #xA | #xD | #x20 | comment if ($self->_test('#')) { $self->_eat(qr/#[^\x0d\x0a]*.?/); } else { $self->_eat(qr/[\n\r\t ]/); } } sub __consume_ws_opt { my $self = shift; if ($self->_ws_test) { $self->__consume_ws; } } sub __consume_ws { my $self = shift; $self->_ws; while ($self->_ws_test()) { $self->_ws() } } sub __base { my $self = shift; my $build = $self->{build}; if (defined($build->{base})) { return $build->{base}; } else { return; } } sub __new_statement { my $self = shift; my @nodes = @_; if (my $graph = $self->{named_graph}) { return RDF::Query::Algebra::Quad->new( @nodes, $graph ); } else { return RDF::Query::Algebra::Triple->new( @nodes ); } } ################################################################################ # [1] Query ::= Prologue ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery ) sub _Query { my $self = shift; $self->__consume_ws_opt; $self->_Prologue; $self->__consume_ws_opt; if ($self->_test(qr/SELECT/i)) { $self->_SelectQuery(); } elsif ($self->_test(qr/CONSTRUCT/i)) { $self->_ConstructQuery(); } elsif ($self->_test(qr/DESCRIBE/i)) { $self->_DescribeQuery(); } elsif ($self->_test(qr/ASK/i)) { $self->_AskQuery(); } else { my $l = Log::Log4perl->get_logger("rdf.query"); if ($l->is_debug) { $l->logcluck("Syntax error: Expected query type with input <<$self->{tokens}>>"); } throw RDF::Query::Error::ParseError -text => 'Syntax error: Expected query type'; } my $remaining = $self->{tokens}; if ($remaining =~ m/\S/) { throw RDF::Query::Error::ParseError -text => "Syntax error: Remaining input after query: $remaining"; } # my %query = (%p, %body); # return \%query; } # [2] Prologue ::= BaseDecl? PrefixDecl* # [3] BaseDecl ::= 'BASE' IRI_REF # [4] PrefixDecl ::= 'PREFIX' PNAME_NS IRI_REF sub _Prologue { my $self = shift; my $base; my @base; if ($self->_test( qr/BASE/i )) { $self->_eat( qr/BASE/i ); $self->__consume_ws_opt; my $iriref = $self->_eat( $r_IRI_REF ); my $iri = substr($iriref,1,length($iriref)-2); $base = RDF::Query::Node::Resource->new( $iri ); @base = $base; $self->__consume_ws_opt; $self->{base} = $base; } my %namespaces; while ($self->_test( qr/PREFIX/i )) { $self->_eat( qr/PREFIX/i ); $self->__consume_ws_opt; my $prefix = $self->_eat( $r_PNAME_NS ); my $ns = substr($prefix, 0, length($prefix) - 1); if ($ns eq '') { $ns = '__DEFAULT__'; } $self->__consume_ws_opt; my $iriref = $self->_eat( $r_IRI_REF ); my $iri = substr($iriref,1,length($iriref)-2); if (@base) { my $r = RDF::Query::Node::Resource->new( $iri, @base ); $iri = $r->uri_value; } $self->__consume_ws_opt; $namespaces{ $ns } = $iri; $self->{namespaces}{$ns} = $iri; } $self->{build}{namespaces} = \%namespaces; $self->{build}{base} = $base if (defined($base)); # push(@data, (base => $base)) if (defined($base)); # return @data; } # [5] SelectQuery ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( Var+ | '*' ) DatasetClause* WhereClause SolutionModifier sub _SelectQuery { my $self = shift; $self->_eat(qr/SELECT/i); $self->__consume_ws; if ($self->{tokens} =~ m/^(DISTINCT|REDUCED)/i) { my $mod = $self->_eat( qr/DISTINCT|REDUCED/i ); $self->__consume_ws; $self->{build}{options}{lc($mod)} = 1; } my $star = $self->__SelectVars; $self->_DatasetClause(); $self->__consume_ws_opt; $self->_WhereClause; if ($star) { my $triples = $self->{build}{triples} || []; my @vars = RDF::Query::_uniq( map { $_->referenced_variables } @$triples ); $self->{build}{variables} = [ map { $self->new_variable($_) } @vars ]; } $self->__consume_ws_opt; $self->_SolutionModifier(); if ($self->{build}{options}{orderby}) { my $order = delete $self->{build}{options}{orderby}; my $pattern = pop(@{ $self->{build}{triples} }); my $sort = RDF::Query::Algebra::Sort->new( $pattern, @$order ); push(@{ $self->{build}{triples} }, $sort); } $self->__solution_modifiers( $star ); delete $self->{build}{options}; $self->{build}{method} = 'SELECT'; } sub __SelectVars { my $self = shift; my $star = 0; if ($self->_test('*')) { $self->_eat('*'); $star = 1; $self->__consume_ws_opt; } else { my @vars; $self->__SelectVar; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->__SelectVar_test) { $self->__SelectVar; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } $self->{build}{variables} = \@vars; } return $star; } sub __SelectVar_test { my $self = shift; return $self->{tokens} =~ m'^[?$]'; } sub __SelectVar { my $self = shift; $self->_Var; } # [6] ConstructQuery ::= 'CONSTRUCT' ConstructTemplate DatasetClause* WhereClause SolutionModifier sub _ConstructQuery { my $self = shift; $self->_eat(qr/CONSTRUCT/i); $self->__consume_ws_opt; $self->_ConstructTemplate; $self->__consume_ws_opt; $self->_DatasetClause(); $self->__consume_ws_opt; $self->_WhereClause; $self->__consume_ws_opt; $self->_SolutionModifier(); my $pattern = $self->{build}{triples}[0]; my $triples = delete $self->{build}{construct_triples}; my $construct = RDF::Query::Algebra::Construct->new( $pattern, $triples ); $self->{build}{triples}[0] = $construct; $self->{build}{method} = 'CONSTRUCT'; } # [7] DescribeQuery ::= 'DESCRIBE' ( VarOrIRIref+ | '*' ) DatasetClause* WhereClause? SolutionModifier sub _DescribeQuery { my $self = shift; $self->_eat(qr/DESCRIBE/i); $self->_ws; if ($self->_test('*')) { $self->_eat('*'); $self->{build}{variables} = ['*']; $self->__consume_ws_opt; } else { $self->_VarOrIRIref; $self->__consume_ws_opt; while ($self->_VarOrIRIref_test) { $self->_VarOrIRIref; $self->__consume_ws_opt; } $self->{build}{variables} = [ splice(@{ $self->{stack} }) ]; } $self->_DatasetClause(); $self->__consume_ws_opt; if ($self->_WhereClause_test) { $self->_WhereClause; $self->__consume_ws_opt; } $self->_SolutionModifier(); $self->{build}{method} = 'DESCRIBE'; } # [8] AskQuery ::= 'ASK' DatasetClause* WhereClause sub _AskQuery { my $self = shift; $self->_eat(qr/ASK/i); $self->_ws; $self->_DatasetClause(); $self->__consume_ws_opt; $self->_WhereClause; $self->{build}{variables} = []; $self->{build}{method} = 'ASK'; } # [9] DatasetClause ::= 'FROM' ( DefaultGraphClause | NamedGraphClause ) sub _DatasetClause { my $self = shift; # my @dataset; $self->{build}{sources} = []; while ($self->_test( qr/FROM/i )) { $self->_eat( qr/FROM/i ); $self->__consume_ws; if ($self->_test( qr/NAMED/i )) { $self->_NamedGraphClause; } else { $self->_DefaultGraphClause; } $self->__consume_ws_opt; } } # [10] DefaultGraphClause ::= SourceSelector sub _DefaultGraphClause { my $self = shift; $self->_SourceSelector; my ($source) = splice(@{ $self->{stack} }); push( @{ $self->{build}{sources} }, [$source] ); } # [11] NamedGraphClause ::= 'NAMED' SourceSelector sub _NamedGraphClause { my $self = shift; $self->_eat( qr/NAMED/i ); $self->__consume_ws_opt; $self->_SourceSelector; my ($source) = splice(@{ $self->{stack} }); push( @{ $self->{build}{sources} }, [$source, 'NAMED'] ); } # [12] SourceSelector ::= IRIref sub _SourceSelector { my $self = shift; $self->_IRIref; } # [13] WhereClause ::= 'WHERE'? GroupGraphPattern sub _WhereClause_test { my $self = shift; return $self->_test( qr/WHERE|{/i ); } sub _WhereClause { my $self = shift; if ($self->_test( qr/WHERE/i )) { $self->_eat( qr/WHERE/i ); } $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_peek_pattern; $ggp->check_duplicate_blanks; } # [14] SolutionModifier ::= OrderClause? LimitOffsetClauses? sub _SolutionModifier { my $self = shift; if ($self->_OrderClause_test) { $self->_OrderClause; $self->__consume_ws_opt; } if ($self->_LimitOffsetClauses_test) { $self->_LimitOffsetClauses; } } # [15] LimitOffsetClauses ::= ( LimitClause OffsetClause? | OffsetClause LimitClause? ) sub _LimitOffsetClauses_test { my $self = shift; return $self->_test( qr/LIMIT|OFFSET/i ); } sub _LimitOffsetClauses { my $self = shift; if ($self->_LimitClause_test) { $self->_LimitClause; $self->__consume_ws; if ($self->_OffsetClause_test) { $self->_OffsetClause; } } else { $self->_OffsetClause; $self->__consume_ws; if ($self->_LimitClause_test) { $self->_LimitClause; } } } # [16] OrderClause ::= 'ORDER' 'BY' OrderCondition+ sub _OrderClause_test { my $self = shift; return $self->_test( qr/ORDER[\n\r\t ]+BY/i ); } sub _OrderClause { my $self = shift; $self->_eat( qr/ORDER/i ); $self->__consume_ws; $self->_eat( qr/BY/i ); $self->__consume_ws_opt; my @order; $self->_OrderCondition; $self->__consume_ws_opt; push(@order, splice(@{ $self->{stack} })); while ($self->_OrderCondition_test) { $self->_OrderCondition; $self->__consume_ws_opt; push(@order, splice(@{ $self->{stack} })); } $self->{build}{options}{orderby} = \@order; } # [17] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression ) | ( Constraint | Var ) sub _OrderCondition_test { my $self = shift; return 1 if $self->_test( qr/ASC|DESC|[?\$]/i ); return 1 if $self->_Constraint_test; return 0; } sub _OrderCondition { my $self = shift; my $dir = 'ASC'; if ($self->_test( qr/ASC|DESC/i )) { $dir = uc( $self->_eat( qr/ASC|DESC/i ) ); $self->__consume_ws_opt; $self->_BrackettedExpression; } elsif ($self->_test( qr/[?\$]/ )) { $self->_Var; } else { $self->_Constraint; } my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( [ $dir, $expr ] ); } # [18] LimitClause ::= 'LIMIT' INTEGER sub _LimitClause_test { my $self = shift; return $self->_test( qr/LIMIT/i ); } sub _LimitClause { my $self = shift; $self->_eat( qr/LIMIT/i ); $self->__consume_ws; my $limit = $self->_eat( $r_INTEGER ); $self->{build}{options}{limit} = $limit; } # [19] OffsetClause ::= 'OFFSET' INTEGER sub _OffsetClause_test { my $self = shift; return $self->_test( qr/OFFSET/i ); } sub _OffsetClause { my $self = shift; $self->_eat( qr/OFFSET/i ); $self->__consume_ws; my $off = $self->_eat( $r_INTEGER ); $self->{build}{options}{offset} = $off; } # [20] GroupGraphPattern ::= '{' TriplesBlock? ( ( GraphPatternNotTriples | Filter ) '.'? TriplesBlock? )* '}' sub _GroupGraphPattern { my $self = shift; $self->_push_pattern_container; $self->_eat('{'); $self->__consume_ws_opt; my $got_pattern = 0; my $need_dot = 0; if ($self->_TriplesBlock_test) { $need_dot = 1; $got_pattern++; $self->_TriplesBlock; $self->__consume_ws_opt; } my $pos = length($self->{tokens}); while (not $self->_test('}')) { if ($self->_GraphPatternNotTriples_test) { $need_dot = 0; $got_pattern++; $self->_GraphPatternNotTriples; $self->__consume_ws_opt; my ($data) = splice(@{ $self->{stack} }); $self->__handle_GraphPatternNotTriples( $data ); $self->__consume_ws_opt; } elsif ($self->_test( qr/FILTER/i )) { $got_pattern++; $need_dot = 0; $self->_Filter; $self->__consume_ws_opt; } if ($need_dot or $self->_test('.')) { $self->_eat('.'); if ($got_pattern) { $need_dot = 0; $got_pattern = 0; } else { throw RDF::Query::Error::ParseError -text => "Syntax error: Extra dot found without preceding pattern"; } $self->__consume_ws_opt; } if ($self->_TriplesBlock_test) { my $peek = $self->_peek_pattern; if (blessed($peek) and $peek->isa('RDF::Query::Algebra::BasicGraphPattern')) { $self->_TriplesBlock; my $rhs = $self->_remove_pattern; my $lhs = $self->_remove_pattern; my $merged = RDF::Query::Algebra::BasicGraphPattern->new( map { $_->triples } ($lhs, $rhs) ); $self->_add_patterns( $merged ); } else { $self->_TriplesBlock; } $self->__consume_ws_opt; } $self->__consume_ws_opt; last unless ($self->_test( qr/\S/ )); my $new = length($self->{tokens}); if ($pos == $new) { # we haven't progressed, and so would infinite loop if we don't break out and throw an error. $self->_syntax_error(''); } else { $pos = $new; } } $self->_eat('}'); my $cont = $self->_pop_pattern_container; my @filters = splice(@{ $self->{filters} }); my @patterns; my $pattern = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); while (my $f = shift @filters) { $pattern = RDF::Query::Algebra::Filter->new( $f, $pattern ); } $self->_add_patterns( $pattern ); } sub __handle_GraphPatternNotTriples { my $self = shift; my $data = shift; my ($class, @args) = @$data; if ($class eq 'RDF::Query::Algebra::Optional') { my $cont = $self->_pop_pattern_container; my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_push_pattern_container; # my $ggp = $self->_remove_pattern(); unless ($ggp) { $ggp = RDF::Query::Algebra::GroupGraphPattern->new(); } my $opt = $class->new( $ggp, @args ); $self->_add_patterns( $opt ); } elsif ($class eq 'RDF::Query::Algebra::Union') { # no-op } elsif ($class eq 'RDF::Query::Algebra::NamedGraph') { # no-op } elsif ($class eq 'RDF::Query::Algebra::GroupGraphPattern') { # no-op } else { Carp::confess Dumper($class, \@args); } } # [21] TriplesBlock ::= TriplesSameSubject ( '.' TriplesBlock? )? sub _TriplesBlock_test { my $self = shift; # VarOrTerm | TriplesNode -> (Var | GraphTerm) | (Collection | BlankNodePropertyList) -> Var | IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | Collection | BlankNodePropertyList # but since a triple can't start with a literal, this is reduced to: # Var | IRIref | BlankNode | NIL return $self->_test(qr/[\$?]|<|_:|\[[\n\r\t ]*\]|\([\n\r\t ]*\)|\[|[[(]|${r_PNAME_NS}/); } sub _TriplesBlock { my $self = shift; $self->_push_pattern_container; $self->__TriplesBlock; my $triples = $self->_pop_pattern_container; my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @$triples ); $self->_add_patterns( $bgp ); } ## this one (with two underscores) doesn't pop patterns off the stack and make a BGP. ## instead, things are left on the stack so we can recurse without doing the wrong thing. ## the one with one underscore (_TriplesBlock) will pop everything off and make the BGP. sub __TriplesBlock { my $self = shift; $self->_TriplesSameSubject; $self->__consume_ws_opt; my $got_dot = 0; while ($self->_test('.')) { if ($got_dot) { throw RDF::Query::Error::ParseError -text => "Syntax error: found extra DOT after TriplesBlock"; } $self->_eat('.'); $got_dot++; $self->__consume_ws_opt; if ($self->_TriplesBlock_test) { $got_dot = 0; $self->__TriplesBlock; $self->__consume_ws_opt; } } } # [22] GraphPatternNotTriples ::= OptionalGraphPattern | GroupOrUnionGraphPattern | GraphGraphPattern sub _GraphPatternNotTriples_test { my $self = shift; return $self->_test(qr/OPTIONAL|{|GRAPH/i); } sub _GraphPatternNotTriples { my $self = shift; if ($self->_OptionalGraphPattern_test) { $self->_OptionalGraphPattern; } elsif ($self->_GroupOrUnionGraphPattern_test) { $self->_GroupOrUnionGraphPattern; } else { $self->_GraphGraphPattern; } } # [23] OptionalGraphPattern ::= 'OPTIONAL' GroupGraphPattern sub _OptionalGraphPattern_test { my $self = shift; return $self->_test( qr/OPTIONAL/i ); } sub _OptionalGraphPattern { my $self = shift; $self->_eat( qr/OPTIONAL/i ); $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; my $opt = ['RDF::Query::Algebra::Optional', $ggp]; $self->_add_stack( $opt ); } # [24] GraphGraphPattern ::= 'GRAPH' VarOrIRIref GroupGraphPattern sub _GraphGraphPattern { my $self = shift; $self->_eat( qr/GRAPH/i ); $self->__consume_ws; $self->_VarOrIRIref; my ($graph) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; # if ($graph->isa('RDF::Trine::Node::Resource')) { local($self->{named_graph}) = $graph; $self->_GroupGraphPattern; # } else { # $self->_GroupGraphPattern; # } my $ggp = $self->_remove_pattern; my $pattern = RDF::Query::Algebra::NamedGraph->new( $graph, $ggp ); $self->_add_patterns( $pattern ); $self->_add_stack( [ 'RDF::Query::Algebra::NamedGraph' ] ); } # [25] GroupOrUnionGraphPattern ::= GroupGraphPattern ( 'UNION' GroupGraphPattern )* sub _GroupOrUnionGraphPattern_test { my $self = shift; return $self->_test('{'); } sub _GroupOrUnionGraphPattern { my $self = shift; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; $self->__consume_ws_opt; if ($self->_test( qr/UNION/i )) { while ($self->_test( qr/UNION/i )) { $self->_eat( qr/UNION/i ); $self->__consume_ws_opt; $self->_GroupGraphPattern; $self->__consume_ws_opt; my $rhs = $self->_remove_pattern; $ggp = RDF::Query::Algebra::Union->new( $ggp, $rhs ); } $self->_add_patterns( $ggp ); $self->_add_stack( [ 'RDF::Query::Algebra::Union' ] ); } else { $self->_add_patterns( $ggp ); $self->_add_stack( [ 'RDF::Query::Algebra::GroupGraphPattern' ] ); } } # [26] Filter ::= 'FILTER' Constraint sub _Filter { my $self = shift; $self->_eat( qr/FILTER/i ); $self->__consume_ws_opt; $self->_Constraint; my ($expr) = splice(@{ $self->{stack} }); $self->_add_filter( $expr ); } # [27] Constraint ::= BrackettedExpression | BuiltInCall | FunctionCall sub _Constraint_test { my $self = shift; return 1 if $self->_test( qr/[(]/ ); return 1 if $self->_BuiltInCall_test; return 1 if $self->_FunctionCall_test; return 0; } sub _Constraint { my $self = shift; if ($self->_BrackettedExpression_test) { $self->_BrackettedExpression(); } elsif ($self->_BuiltInCall_test) { $self->_BuiltInCall(); } else { $self->_FunctionCall(); } } # [28] FunctionCall ::= IRIref ArgList sub _FunctionCall_test { my $self = shift; return $self->_IRIref_test; } sub _FunctionCall { my $self = shift; $self->_IRIref; my ($iri) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ArgList; my @args = splice(@{ $self->{stack} }); my $func = $self->new_function_expression( $iri, @args ); $self->_add_stack( $func ); } # [29] ArgList ::= ( NIL | '(' Expression ( ',' Expression )* ')' ) sub _ArgList_test { my $self = shift; return $self->_test('('); } sub _ArgList { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; my @args; unless ($self->_test(')')) { $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); while ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); } } $self->_eat(')'); $self->_add_stack( @args ); } # [30] ConstructTemplate ::= '{' ConstructTriples? '}' sub _ConstructTemplate { my $self = shift; $self->_push_pattern_container; $self->_eat( '{' ); $self->__consume_ws_opt; if ($self->_ConstructTriples_test) { $self->_ConstructTriples; } $self->__consume_ws_opt; $self->_eat( '}' ); my $cont = $self->_pop_pattern_container; $self->{build}{construct_triples} = $cont; } # [31] ConstructTriples ::= TriplesSameSubject ( '.' ConstructTriples? )? sub _ConstructTriples_test { my $self = shift; return $self->_TriplesBlock_test; } sub _ConstructTriples { my $self = shift; $self->_TriplesSameSubject; $self->__consume_ws_opt; while ($self->_test(qr/[.]/)) { $self->_eat( qr/[.]/ ); $self->__consume_ws_opt; if ($self->_ConstructTriples_test) { $self->_TriplesSameSubject; } } } # [32] TriplesSameSubject ::= VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList sub _TriplesSameSubject { my $self = shift; my @triples; if ($self->_TriplesNode_test) { $self->_TriplesNode; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyList; $self->__consume_ws_opt; my @list = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } else { $self->_VarOrTerm; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyListNotEmpty; $self->__consume_ws_opt; my (@list) = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } $self->_add_patterns( @triples ); # return @triples; } # [33] PropertyListNotEmpty ::= Verb ObjectList ( ';' ( Verb ObjectList )? )* sub _PropertyListNotEmpty { my $self = shift; $self->_Verb; my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); my @props = map { [$v, $_] } @l; while ($self->_test(qr'\s*;')) { $self->_eat(';'); $self->__consume_ws_opt; if ($self->_Verb_test) { $self->_Verb; my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); push(@props, map { [$v, $_] } @l); } } $self->_add_stack( @props ); } # [34] PropertyList ::= PropertyListNotEmpty? sub _PropertyList { my $self = shift; if ($self->_Verb_test) { $self->_PropertyListNotEmpty; } } # [35] ObjectList ::= Object ( ',' Object )* sub _ObjectList { my $self = shift; my @list; $self->_Object; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Object; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } $self->_add_stack( @list ); } # [36] Object ::= GraphNode sub _Object { my $self = shift; $self->_GraphNode; } # [37] Verb ::= VarOrIRIref | 'a' sub _Verb_test { my $self = shift; return $self->_test( qr/a[\n\t\r <]|[?\$]|<|${r_PNAME_LN}|${r_PNAME_NS}/ ); } sub _Verb { my $self = shift; if ($self->_test(qr/a[\n\t\r <]/)) { $self->_eat('a'); $self->__consume_ws; my $type = RDF::Query::Node::Resource->new( $rdf->type->uri_value ); $self->_add_stack( $type ); } else { $self->_VarOrIRIref; } } # [38] TriplesNode ::= Collection | BlankNodePropertyList sub _TriplesNode_test { my $self = shift; return $self->_test(qr/[[(](?![\n\r\t ]*\])(?![\n\r\t ]*\))/); } sub _TriplesNode { my $self = shift; if ($self->_test(qr/\(/)) { $self->_Collection; } else { $self->_BlankNodePropertyList; } } # [39] BlankNodePropertyList ::= '[' PropertyListNotEmpty ']' sub _BlankNodePropertyList { my $self = shift; $self->_eat('['); $self->__consume_ws_opt; $self->_PropertyListNotEmpty; $self->__consume_ws_opt; $self->_eat(']'); my @props = splice(@{ $self->{stack} }); my $subj = $self->new_blank; my @triples = map { $self->__new_statement( $subj, @$_ ) } @props; $self->_add_patterns( @triples ); $self->_add_stack( $subj ); } # [40] Collection ::= '(' GraphNode+ ')' sub _Collection { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; $self->_GraphNode; $self->__consume_ws_opt; my @nodes; push(@nodes, splice(@{ $self->{stack} })); while ($self->_GraphNode_test) { $self->_GraphNode; $self->__consume_ws_opt; push(@nodes, splice(@{ $self->{stack} })); } $self->_eat(')'); my $subj = $self->new_blank; my $cur = $subj; my $last; my $first = RDF::Query::Node::Resource->new( $rdf->first->uri_value ); my $rest = RDF::Query::Node::Resource->new( $rdf->rest->uri_value ); my $nil = RDF::Query::Node::Resource->new( $rdf->nil->uri_value ); my @triples; foreach my $node (@nodes) { push(@triples, $self->__new_statement( $cur, $first, $node ) ); my $new = $self->new_blank; push(@triples, $self->__new_statement( $cur, $rest, $new ) ); $last = $cur; $cur = $new; } pop(@triples); push(@triples, $self->__new_statement( $last, $rest, $nil )); $self->_add_patterns( @triples ); $self->_add_stack( $subj ); } # [41] GraphNode ::= VarOrTerm | TriplesNode sub _GraphNode_test { my $self = shift; # VarOrTerm | TriplesNode -> (Var | GraphTerm) | (Collection | BlankNodePropertyList) -> Var | IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | Collection | BlankNodePropertyList # but since a triple can't start with a literal, this is reduced to: # Var | IRIref | BlankNode | NIL return $self->_test(qr/[\$?]|<|['"]|(true\b|false\b)|([+-]?\d)|_:|${r_ANON}|${r_NIL}|\[|[[(]/); } sub _GraphNode { my $self = shift; if ($self->_TriplesNode_test) { $self->_TriplesNode; } else { $self->_VarOrTerm; } } # [42] VarOrTerm ::= Var | GraphTerm sub _VarOrTerm_test { my $self = shift; return 1 if ($self->_test(qr/[\$?]/)); return 1 if ($self->_test(qr/[<'".0-9]|(true|false)\b|_:|\([\n\r\t ]*\)/)); return 0; } sub _VarOrTerm { my $self = shift; if ($self->{tokens} =~ m'^[?$]') { $self->_Var; } else { $self->_GraphTerm; } } # [43] VarOrIRIref ::= Var | IRIref sub _VarOrIRIref_test { my $self = shift; return $self->_test(qr/[\$?]|<|${r_PNAME_LN}|${r_PNAME_NS}/); } sub _VarOrIRIref { my $self = shift; if ($self->{tokens} =~ m'^[?$]') { $self->_Var; } else { $self->_IRIref; } } # [44] Var ::= VAR1 | VAR2 sub _Var { my $self = shift; my $var = ($self->_test( $r_VAR1 )) ? $self->_eat( $r_VAR1 ) : $self->_eat( $r_VAR2 ); $self->_add_stack( RDF::Query::Node::Variable->new( substr($var,1) ) ); } # [45] GraphTerm ::= IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL sub _GraphTerm { my $self = shift; if ($self->_test(qr/(true|false)\b/)) { $self->_BooleanLiteral; } elsif ($self->_test('(')) { $self->_NIL; } elsif ($self->_test( $r_ANON ) or $self->_test('_:')) { $self->_BlankNode; } elsif ($self->_test(qr/[-+]?\d/)) { $self->_NumericLiteral; } elsif ($self->_test(qr/['"]/)) { $self->_RDFLiteral; } else { $self->_IRIref; } } # [46] Expression ::= ConditionalOrExpression sub _Expression { my $self = shift; $self->_ConditionalOrExpression; } # [47] ConditionalOrExpression ::= ConditionalAndExpression ( '||' ConditionalAndExpression )* sub _ConditionalOrExpression { my $self = shift; my @list; $self->_ConditionalAndExpression; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->_test('||')) { $self->_eat('||'); $self->__consume_ws_opt; $self->_ConditionalAndExpression; push(@list, splice(@{ $self->{stack} })); } if (scalar(@list) > 1) { $self->_add_stack( $self->new_function_expression( 'sparql:logical-or', @list ) ); } else { $self->_add_stack( @list ); } Carp::confess $self->{tokens} if (scalar(@{ $self->{stack} }) == 0); } # [48] ConditionalAndExpression ::= ValueLogical ( '&&' ValueLogical )* sub _ConditionalAndExpression { my $self = shift; my @list; $self->_ValueLogical; push(@list, splice(@{ $self->{stack} })); Carp::confess Dumper(\@list) if (scalar(@list) > 1); $self->__consume_ws_opt; while ($self->_test('&&')) { $self->_eat('&&'); $self->__consume_ws_opt; $self->_ValueLogical; push(@list, splice(@{ $self->{stack} })); } if (scalar(@list) > 1) { $self->_add_stack( $self->new_function_expression( 'sparql:logical-and', @list ) ); } else { $self->_add_stack( @list ); } } # [49] ValueLogical ::= RelationalExpression sub _ValueLogical { my $self = shift; $self->_RelationalExpression; } # [50] RelationalExpression ::= NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression )? sub _RelationalExpression { my $self = shift; $self->_NumericExpression; $self->__consume_ws_opt; if ($self->_test(qr/[!<>]?=|[<>]/)) { if ($self->_test( $r_IRI_REF )) { throw RDF::Query::Error::ParseError -text => "Syntax error: IRI found where expression expected"; } my @list = splice(@{ $self->{stack} }); my $op = $self->_eat(qr/[!<>]?=|[<>]/); $op = '==' if ($op eq '='); $self->__consume_ws_opt; $self->_NumericExpression; push(@list, splice(@{ $self->{stack} })); $self->_add_stack( $self->new_binary_expression( $op, @list ) ); } } # [51] NumericExpression ::= AdditiveExpression sub _NumericExpression { my $self = shift; $self->_AdditiveExpression; } # [52] AdditiveExpression ::= MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | NumericLiteralPositive | NumericLiteralNegative )* sub _AdditiveExpression { my $self = shift; $self->_MultiplicativeExpression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; while ($self->_test(qr/[-+]/)) { my $op = $self->_eat(qr/[-+]/); $self->__consume_ws_opt; $self->_MultiplicativeExpression; my ($rhs) = splice(@{ $self->{stack} }); $expr = $self->new_binary_expression( $op, $expr, $rhs ); } $self->_add_stack( $expr ); } # [53] MultiplicativeExpression ::= UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )* sub _MultiplicativeExpression { my $self = shift; $self->_UnaryExpression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; while ($self->_test(qr#[*/]#)) { my $op = $self->_eat(qr#[*/]#); $self->__consume_ws_opt; $self->_UnaryExpression; my ($rhs) = splice(@{ $self->{stack} }); $expr = $self->new_binary_expression( $op, $expr, $rhs ); } $self->_add_stack( $expr ); } # [54] UnaryExpression ::= '!' PrimaryExpression | '+' PrimaryExpression | '-' PrimaryExpression | PrimaryExpression sub _UnaryExpression { my $self = shift; if ($self->_test('!')) { $self->_eat('!'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); my $not = $self->new_unary_expression( '!', $expr ); $self->_add_stack( $not ); } elsif ($self->_test('+')) { $self->_eat('+'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); ### if it's just a literal, force the positive down into the literal if (blessed($expr) and $expr->isa('RDF::Trine::Node::Literal') and $expr->is_numeric_type) { my $value = '+' . $expr->literal_value; $expr->literal_value( $value ); $self->_add_stack( $expr ); } else { $self->_add_stack( $expr ); } } elsif ($self->_test('-')) { $self->_eat('-'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); ### if it's just a literal, force the negative down into the literal instead of make an unnecessary multiplication. if (blessed($expr) and $expr->isa('RDF::Trine::Node::Literal') and $expr->is_numeric_type) { my $value = -1 * $expr->literal_value; $expr->literal_value( $value ); $self->_add_stack( $expr ); } else { my $int = $xsd->integer->uri_value; my $neg = $self->new_binary_expression( '*', $self->new_literal('-1', undef, $int), $expr ); $self->_add_stack( $neg ); } } else { $self->_PrimaryExpression; } } # [55] PrimaryExpression ::= BrackettedExpression | BuiltInCall | IRIrefOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var sub _PrimaryExpression { my $self = shift; if ($self->_BrackettedExpression_test) { $self->_BrackettedExpression; } elsif ($self->_BuiltInCall_test) { $self->_BuiltInCall; } elsif ($self->_IRIref_test) { $self->_IRIrefOrFunction; } elsif ($self->_test(qr/[\$?]/)) { $self->_Var; } elsif ($self->_test(qr/(true|false)\b/)) { $self->_BooleanLiteral; } elsif ($self->_test(qr/[-+]?\d/)) { $self->_NumericLiteral; } else { # if ($self->_test(qr/['"]/)) { $self->_RDFLiteral; } } # [56] BrackettedExpression ::= '(' Expression ')' sub _BrackettedExpression_test { my $self = shift; return $self->_test('('); } sub _BrackettedExpression { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; $self->__consume_ws_opt; $self->_eat(')'); } # [57] BuiltInCall ::= 'STR' '(' Expression ')' | 'LANG' '(' Expression ')' | 'LANGMATCHES' '(' Expression ',' Expression ')' | 'DATATYPE' '(' Expression ')' | 'BOUND' '(' Var ')' | 'sameTerm' '(' Expression ',' Expression ')' | 'isIRI' '(' Expression ')' | 'isURI' '(' Expression ')' | 'isBLANK' '(' Expression ')' | 'isLITERAL' '(' Expression ')' | RegexExpression sub _BuiltInCall_test { my $self = shift; return $self->_test(qr/STR|LANG|LANGMATCHES|DATATYPE|BOUND|sameTerm|isIRI|isURI|isBLANK|isLITERAL|REGEX/i); } sub _BuiltInCall { my $self = shift; if ($self->_RegexExpression_test) { $self->_RegexExpression; } else { my $op = $self->_eat( qr/\w+/ ); my $iri = RDF::Query::Node::Resource->new( 'sparql:' . lc($op) ); $self->__consume_ws_opt; $self->_eat('('); $self->__consume_ws_opt; if ($op =~ /^(STR|LANG|DATATYPE|isIRI|isURI|isBLANK|isLITERAL)$/i) { ### one-arg functions that take an expression $self->_Expression; my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $expr) ); } elsif ($op =~ /^(LANGMATCHES|sameTerm)$/i) { ### two-arg functions that take expressions $self->_Expression; my ($arg1) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my ($arg2) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $arg1, $arg2) ); } else { ### BOUND(Var) $self->_Var; my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $expr) ); } $self->__consume_ws_opt; $self->_eat(')'); } } # [58] RegexExpression ::= 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')' sub _RegexExpression_test { my $self = shift; return $self->_test( qr/REGEX/i ); } sub _RegexExpression { my $self = shift; $self->_eat( qr/REGEX/i ); $self->__consume_ws_opt; $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; my $string = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my $pattern = splice(@{ $self->{stack} }); my @args = ($string, $pattern); if ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; push(@args, splice(@{ $self->{stack} })); } $self->__consume_ws_opt; $self->_eat(')'); my $iri = RDF::Query::Node::Resource->new( 'sparql:regex' ); $self->_add_stack( $self->new_function_expression( $iri, @args ) ); } # [59] IRIrefOrFunction ::= IRIref ArgList? sub _IRIrefOrFunction_test { my $self = shift; $self->_IRIref_test; } sub _IRIrefOrFunction { my $self = shift; $self->_IRIref; if ($self->_ArgList_test) { my ($iri) = splice(@{ $self->{stack} }); $self->_ArgList; my @args = splice(@{ $self->{stack} }); my $func = $self->new_function_expression( $iri, @args ); $self->_add_stack( $func ); } } # [60] RDFLiteral ::= String ( LANGTAG | ( '^^' IRIref ) )? sub _RDFLiteral { my $self = shift; $self->_String; my @args = splice(@{ $self->{stack} }); if ($self->_test('@')) { my $lang = $self->_eat( $r_LANGTAG ); substr($lang,0,1) = ''; # remove '@' push(@args, lc($lang)); } elsif ($self->_test('^^')) { $self->_eat('^^'); push(@args, undef); $self->_IRIref; my ($iri) = splice(@{ $self->{stack} }); push(@args, $iri->uri_value); } $self->_add_stack( RDF::Query::Node::Literal->new( @args ) ); } # [61] NumericLiteral ::= NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative # [62] NumericLiteralUnsigned ::= INTEGER | DECIMAL | DOUBLE # [63] NumericLiteralPositive ::= INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE # [64] NumericLiteralNegative ::= INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE sub _NumericLiteral { my $self = shift; my $sign = 0; if ($self->_test('+')) { $self->_eat('+'); $sign = '+'; } elsif ($self->_test('-')) { $self->_eat('-'); $sign = '-'; } my $value; my $type; if ($self->_test( $r_DOUBLE )) { $value = $self->_eat( $r_DOUBLE ); my $double = RDF::Query::Node::Resource->new( $xsd->double->uri_value ); $type = $double } elsif ($self->_test( $r_DECIMAL )) { $value = $self->_eat( $r_DECIMAL ); my $decimal = RDF::Query::Node::Resource->new( $xsd->decimal->uri_value ); $type = $decimal; } else { $value = $self->_eat( $r_INTEGER ); my $integer = RDF::Query::Node::Resource->new( $xsd->integer->uri_value ); $type = $integer; } if ($sign) { $value = $sign . $value; } $self->_add_stack( RDF::Query::Node::Literal->new( $value, undef, $type->uri_value ) ); } # [65] BooleanLiteral ::= 'true' | 'false' sub _BooleanLiteral { my $self = shift; my $bool = $self->_eat(qr/(true|false)\b/); $self->_add_stack( RDF::Query::Node::Literal->new( $bool, undef, $xsd->boolean->uri_value ) ); } # [66] String ::= STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2 sub _String { my $self = shift; my $value; if ($self->_test( $r_STRING_LITERAL_LONG1 )) { my $string = $self->_eat( $r_STRING_LITERAL_LONG1 ); $value = substr($string, 3, length($string) - 6); } elsif ($self->_test( $r_STRING_LITERAL_LONG2 )) { my $string = $self->_eat( $r_STRING_LITERAL_LONG2 ); $value = substr($string, 3, length($string) - 6); } elsif ($self->_test( $r_STRING_LITERAL1 )) { my $string = $self->_eat( $r_STRING_LITERAL1 ); $value = substr($string, 1, length($string) - 2); } else { # ($self->_test( $r_STRING_LITERAL2 )) { my $string = $self->_eat( $r_STRING_LITERAL2 ); $value = substr($string, 1, length($string) - 2); } # $value =~ s/(${r_ECHAR})/"$1"/ge; $value =~ s/\\t/\t/g; $value =~ s/\\b/\x08/g; $value =~ s/\\n/\n/g; $value =~ s/\\r/\r/g; $value =~ s/\\"/"/g; $value =~ s/\\'/'/g; $value =~ s/\\\\/\\/g; # backslash must come last, so it doesn't accidentally create a new escape $self->_add_stack( $value ); } # [67] IRIref ::= IRI_REF | PrefixedName sub _IRIref_test { my $self = shift; return $self->_test(qr/<|${r_PNAME_LN}|${r_PNAME_NS}/); } sub _IRIref { my $self = shift; if ($self->_test( $r_IRI_REF )) { my $iri = $self->_eat( $r_IRI_REF ); my $node = RDF::Query::Node::Resource->new( substr($iri,1,length($iri)-2), $self->__base ); $self->_add_stack( $node ); } else { $self->_PrefixedName; } } # [68] PrefixedName ::= PNAME_LN | PNAME_NS sub _PrefixedName { my $self = shift; if ($self->_test( $r_PNAME_LN )) { my $ln = $self->_eat( $r_PNAME_LN ); my ($ns,$local) = split(/:/, $ln); if ($ns eq '') { $ns = '__DEFAULT__'; } unless (exists $self->{namespaces}{$ns}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Use of undefined namespace '$ns'"; } my $iri = $self->{namespaces}{$ns} . $local; $self->_add_stack( RDF::Query::Node::Resource->new( $iri, $self->__base ) ); } else { my $ns = $self->_eat( $r_PNAME_NS ); if ($ns eq ':') { $ns = '__DEFAULT__'; } else { chop($ns); } unless (exists $self->{namespaces}{$ns}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Use of undefined namespace '$ns'"; } my $iri = $self->{namespaces}{$ns}; $self->_add_stack( RDF::Query::Node::Resource->new( $iri, $self->__base ) ); } } # [69] BlankNode ::= BLANK_NODE_LABEL | ANON sub _BlankNode { my $self = shift; if ($self->_test( $r_BLANK_NODE_LABEL )) { my $label = $self->_eat( $r_BLANK_NODE_LABEL ); my $id = substr($label,2); $self->_add_stack( $self->new_blank($id) ); } else { $self->_eat( $r_ANON ); $self->_add_stack( $self->new_blank ); } } sub _NIL { my $self = shift; $self->_eat( $r_NIL ); my $nil = RDF::Query::Node::Resource->new( $rdf->nil->uri_value ); $self->_add_stack( $nil ); } sub __solution_modifiers { my $self = shift; my $star = shift; my $vars = $self->{build}{variables}; my $pattern = pop(@{ $self->{build}{triples} }); my $proj = RDF::Query::Algebra::Project->new( $pattern, $vars ); push(@{ $self->{build}{triples} }, $proj); if ($self->{build}{options}{distinct}) { delete $self->{build}{options}{distinct}; my $pattern = pop(@{ $self->{build}{triples} }); my $sort = RDF::Query::Algebra::Distinct->new( $pattern ); push(@{ $self->{build}{triples} }, $sort); } if (exists $self->{build}{options}{offset}) { my $offset = delete $self->{build}{options}{offset}; my $pattern = pop(@{ $self->{build}{triples} }); my $offseted = RDF::Query::Algebra::Offset->new( $pattern, $offset ); push(@{ $self->{build}{triples} }, $offseted); } if (exists $self->{build}{options}{limit}) { my $limit = delete $self->{build}{options}{limit}; my $pattern = pop(@{ $self->{build}{triples} }); my $limited = RDF::Query::Algebra::Limit->new( $pattern, $limit ); push(@{ $self->{build}{triples} }, $limited); } } 1; __END__ =back =cut RDF-Query-2.910/lib/RDF/Query/Parser/SPARQL11.pm000644 000765 000024 00000305653 12173312155 020572 0ustar00gregstaff000000 000000 # RDF::Query::Parser::SPARQL11 # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Parser::SPARQL11 - SPARQL 1.1 Parser. =head1 VERSION This document describes RDF::Query::Parser::SPARQL11 version 2.910. =head1 SYNOPSIS use RDF::Query::Parser::SPARQL11; my $parser = RDF::Query::Parse::SPARQL11->new(); my $iterator = $parser->parse( $query, $base_uri ); =head1 DESCRIPTION ... =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut package RDF::Query::Parser::SPARQL11; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Parser); use URI; use Data::Dumper; use RDF::Query::Error qw(:try); use RDF::Query::Parser; use RDF::Query::Algebra; use RDF::Trine::Namespace qw(rdf); use Scalar::Util qw(blessed looks_like_number reftype); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#'); our $r_ECHAR = qr/\\([tbnrf\\"'])/; our $r_STRING_LITERAL1 = qr/'(([^\x{27}\x{5C}\x{0A}\x{0D}])|${r_ECHAR})*'/; our $r_STRING_LITERAL2 = qr/"(([^\x{22}\x{5C}\x{0A}\x{0D}])|${r_ECHAR})*"/; our $r_STRING_LITERAL_LONG1 = qr/'''(('|'')?([^'\\]|${r_ECHAR}))*'''/; our $r_STRING_LITERAL_LONG2 = qr/"""(("|"")?([^"\\]|${r_ECHAR}))*"""/; our $r_LANGTAG = qr/@[a-zA-Z]+(-[a-zA-Z0-9]+)*/; our $r_IRI_REF = qr/<([^<>"{}|^`\\\x{00}-\x{20}])*>/; our $r_PN_CHARS_BASE = qr/([A-Z]|[a-z]|[\x{00C0}-\x{00D6}]|[\x{00D8}-\x{00F6}]|[\x{00F8}-\x{02FF}]|[\x{0370}-\x{037D}]|[\x{037F}-\x{1FFF}]|[\x{200C}-\x{200D}]|[\x{2070}-\x{218F}]|[\x{2C00}-\x{2FEF}]|[\x{3001}-\x{D7FF}]|[\x{F900}-\x{FDCF}]|[\x{FDF0}-\x{FFFD}]|[\x{10000}-\x{EFFFF}])/; our $r_PN_CHARS_U = qr/([_]|${r_PN_CHARS_BASE})/; our $r_VARNAME = qr/((${r_PN_CHARS_U}|[0-9])(${r_PN_CHARS_U}|[0-9]|\x{00B7}|[\x{0300}-\x{036F}]|[\x{203F}-\x{2040}])*)/; our $r_VAR1 = qr/[?]${r_VARNAME}/; our $r_VAR2 = qr/[\$]${r_VARNAME}/; our $r_PN_CHARS = qr/${r_PN_CHARS_U}|-|[0-9]|\x{00B7}|[\x{0300}-\x{036F}]|[\x{203F}-\x{2040}]/; our $r_PN_PREFIX = qr/(${r_PN_CHARS_BASE}((${r_PN_CHARS}|[.])*${r_PN_CHARS})?)/; our $r_PN_LOCAL_ESCAPED = qr{(\\([-~.!&'()*+,;=/?#@%_\$]))|%[0-9A-Fa-f]{2}}; our $r_PN_LOCAL = qr/((${r_PN_CHARS_U}|[:0-9]|${r_PN_LOCAL_ESCAPED})((${r_PN_CHARS}|${r_PN_LOCAL_ESCAPED}|[:.])*(${r_PN_CHARS}|[:]|${r_PN_LOCAL_ESCAPED}))?)/; our $r_PN_LOCAL_BNODE = qr/((${r_PN_CHARS_U}|[0-9])((${r_PN_CHARS}|[.])*${r_PN_CHARS})?)/; our $r_PNAME_NS = qr/((${r_PN_PREFIX})?:)/; our $r_PNAME_LN = qr/(${r_PNAME_NS}${r_PN_LOCAL})/; our $r_EXPONENT = qr/[eE][-+]?\d+/; our $r_DOUBLE = qr/\d+[.]\d*${r_EXPONENT}|[.]\d+${r_EXPONENT}|\d+${r_EXPONENT}/; our $r_DECIMAL = qr/(\d+[.]\d*)|([.]\d+)/; our $r_INTEGER = qr/\d+/; our $r_BLANK_NODE_LABEL = qr/_:${r_PN_LOCAL_BNODE}/; our $r_ANON = qr/\[[\t\r\n ]*\]/; our $r_NIL = qr/\([\n\r\t ]*\)/; our $r_AGGREGATE_CALL = qr/(MIN|MAX|COUNT|AVG|SUM|SAMPLE|GROUP_CONCAT)\b/i; =item C<< new >> Returns a new SPARQL 1.1 parser object. =cut sub new { my $class = shift; my %args = @_; my $self = bless({ args => \%args, bindings => {}, bnode_id => 0, }, $class); return $self; } ################################################################################ =item C<< parse ( $query, $base_uri, $update_flag ) >> Parses the C<< $query >>, using the given C<< $base_uri >>. If C<< $update_flag >> is true, the query will be parsed allowing SPARQL 1.1 Update statements. =cut sub parse { my $self = shift; my $input = shift; unless (defined($input)) { $self->{build} = undef; $self->{error} = "No query string found to parse"; return; } my $baseuri = shift; my $update = shift || 0; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = {}; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; local($self->{update}) = $update; my $triples = $self->_push_pattern_container(); local($self->{build}); my $build = { sources => [], triples => $triples }; $self->{build} = $build; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_RW_Query(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $build = undef; $self->{error} = $e->stacktrace } otherwise { my $e = shift; $self->{build} = undef; $build = undef; $self->{error} = $e->stacktrace }; delete $self->{build}{star}; my $data = $build; # $data->{triples} = $self->_pop_pattern_container(); return $data; } =item C<< parse_pattern ( $pattern, $base_uri, \%namespaces ) >> Parses the C<< $pattern >>, using the given C<< $base_uri >> and returns a RDF::Query::Algebra pattern. =cut sub parse_pattern { my $self = shift; my $input = shift; my $baseuri = shift; my $ns = shift; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = $ns; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); $self->{build} = { sources => [], triples => $triples }; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_GroupGraphPattern(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $self->{error} = $e->text; }; my $data = delete $self->{build}; return $data->{triples}[0]; } =item C<< parse_expr ( $pattern, $base_uri, \%namespaces ) >> Parses the C<< $pattern >>, using the given C<< $base_uri >> and returns a RDF::Query::Expression pattern. =cut sub parse_expr { my $self = shift; my $input = shift; my $baseuri = shift; my $ns = shift; $input =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/ge; $input =~ s/\\U([0-9A-Fa-f]{8})/chr(hex($1))/ge; delete $self->{error}; local($self->{namespaces}) = $ns; local($self->{blank_ids}) = 1; local($self->{baseURI}) = $baseuri; local($self->{tokens}) = $input; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); $self->{build} = { sources => [], triples => $triples }; if ($baseuri) { $self->{build}{base} = $baseuri; } try { $self->_Expression(); } catch RDF::Query::Error with { my $e = shift; $self->{build} = undef; $self->{error} = $e->text; }; my $data = splice(@{ $self->{stack} }); return $data; } ################################################################################ # [1] Query ::= Prologue ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery | LoadUpdate ) sub _RW_Query { my $self = shift; $self->__consume_ws_opt; $self->_Prologue; $self->__consume_ws_opt; my $read_query = 0; while (1) { if ($self->_test(qr/SELECT/i)) { $self->_SelectQuery(); $read_query++; } elsif ($self->_test(qr/CONSTRUCT/i)) { $self->_ConstructQuery(); $read_query++; } elsif ($self->_test(qr/DESCRIBE/i)) { $self->_DescribeQuery(); $read_query++; } elsif ($self->_test(qr/ASK/i)) { $self->_AskQuery(); $read_query++; } elsif ($self->_test(qr/CREATE\s+(SILENT\s+)?GRAPH/i)) { throw RDF::Query::Error::PermissionError -text => "CREATE GRAPH update forbidden in read-only queries" unless ($self->{update}); $self->_CreateGraph(); } elsif ($self->_test(qr/DROP\s+(SILENT\s+)?/i)) { throw RDF::Query::Error::PermissionError -text => "DROP GRAPH update forbidden in read-only queries" unless ($self->{update}); $self->_DropGraph(); } elsif ($self->_test(qr/LOAD\s+(SILENT\s+)?/i)) { throw RDF::Query::Error::PermissionError -text => "LOAD update forbidden in read-only queries" unless ($self->{update}); $self->_LoadUpdate(); } elsif ($self->_test(qr/CLEAR\s+(SILENT\s+)?/i)) { throw RDF::Query::Error::PermissionError -text => "CLEAR GRAPH update forbidden in read-only queries" unless ($self->{update}); $self->_ClearGraphUpdate(); } elsif ($self->_test(qr/(WITH|INSERT|DELETE)/i)) { throw RDF::Query::Error::PermissionError -text => "INSERT/DELETE update forbidden in read-only queries" unless ($self->{update}); my ($graph); if ($self->_test(qr/WITH/)) { $self->{build}{custom_update_dataset} = 1; $self->_eat(qr/WITH/i); $self->__consume_ws_opt; $self->_IRIref; ($graph) = splice( @{ $self->{stack} } ); $self->__consume_ws_opt; } if ($self->_test(qr/INSERT/ims)) { $self->_eat(qr/INSERT/i); $self->__consume_ws_opt; if ($self->_test(qr/DATA/i)) { throw RDF::Query::Error::PermissionError -text => "INSERT DATA update forbidden in read-only queries" unless ($self->{update}); $self->_eat(qr/DATA/i); $self->__consume_ws_opt; $self->_InsertDataUpdate(); } else { $self->_InsertUpdate($graph); } } elsif ($self->_test(qr/DELETE/ims)) { $self->_eat(qr/DELETE/i); $self->__consume_ws_opt; if ($self->_test(qr/DATA/i)) { throw RDF::Query::Error::PermissionError -text => "DELETE DATA update forbidden in read-only queries" unless ($self->{update}); $self->_eat(qr/DATA/i); $self->__consume_ws_opt; $self->_DeleteDataUpdate(); } else { $self->_DeleteUpdate($graph); } } } elsif ($self->_test(qr/COPY/i)) { $self->_CopyUpdate(); } elsif ($self->_test(qr/MOVE/i)) { $self->_MoveUpdate(); } elsif ($self->_test(qr/ADD/i)) { $self->_AddUpdate(); } elsif ($self->_test(qr/;/)) { $self->_eat(qr/;/) ; $self->__consume_ws_opt; next if ($self->_Query_test); last; } elsif ($self->{tokens} eq '') { last; } else { my $l = Log::Log4perl->get_logger("rdf.query"); if ($l->is_debug) { $l->logcluck("Syntax error: Expected query type with input <<$self->{tokens}>>"); } throw RDF::Query::Error::ParseError -text => 'Syntax error: Expected query type'; } last if ($read_query); $self->__consume_ws_opt; if ($self->_test(qr/;/)) { $self->_eat(qr/;/) ; $self->__consume_ws_opt; if ($self->_Query_test) { next; } } last; } # $self->_eat(qr/;/) if ($self->_test(qr/;/)); $self->__consume_ws_opt; my $count = scalar(@{ $self->{build}{triples} }); my $remaining = $self->{tokens}; if ($remaining =~ m/\S/) { throw RDF::Query::Error::ParseError -text => "Syntax error: Remaining input after query: $remaining"; } if ($count == 0 or $count > 1) { my @patterns = splice(@{ $self->{build}{triples} }); my $pattern = RDF::Query::Algebra::Sequence->new( @patterns ); $pattern->check_duplicate_blanks; $self->{build}{triples} = [ $pattern ]; } # my %query = (%p, %body); # return \%query; } sub _Query_test { my $self = shift; return 1 if ($self->_test(qr/SELECT|CONSTRUCT|DESCRIBE|ASK|LOAD|CLEAR|DROP|ADD|MOVE|COPY|CREATE|INSERT|DELETE|WITH/i)); return 0; } # [2] Prologue ::= BaseDecl? PrefixDecl* # [3] BaseDecl ::= 'BASE' IRI_REF # [4] PrefixDecl ::= 'PREFIX' PNAME_NS IRI_REF sub _Prologue { my $self = shift; my $base; my @base; if ($self->_test( qr/BASE/i )) { $self->_eat( qr/BASE/i ); $self->__consume_ws_opt; my $iriref = $self->_eat( $r_IRI_REF ); my $iri = substr($iriref,1,length($iriref)-2); $base = RDF::Query::Node::Resource->new( $iri ); @base = $base; $self->__consume_ws_opt; $self->{base} = $base; } my %namespaces; while ($self->_test( qr/PREFIX/i )) { $self->_eat( qr/PREFIX/i ); $self->__consume_ws_opt; my $prefix = $self->_eat( $r_PNAME_NS ); my $ns = substr($prefix, 0, length($prefix) - 1); if ($ns eq '') { $ns = '__DEFAULT__'; } $self->__consume_ws_opt; my $iriref = $self->_eat( $r_IRI_REF ); my $iri = substr($iriref,1,length($iriref)-2); if (@base) { my $r = RDF::Query::Node::Resource->new( $iri, @base ); $iri = $r->uri_value; } $self->__consume_ws_opt; $namespaces{ $ns } = $iri; $self->{namespaces}{$ns} = $iri; } $self->{build}{namespaces} = \%namespaces; $self->{build}{base} = $base if (defined($base)); # push(@data, (base => $base)) if (defined($base)); # return @data; } sub _InsertDataUpdate { my $self = shift; $self->_eat('{'); $self->__consume_ws_opt; local($self->{__data_pattern}) = 1; $self->_ModifyTemplate(); $self->__consume_ws_opt; my $data = $self->_remove_pattern; $self->_eat('}'); $self->__consume_ws_opt; my $empty = RDF::Query::Algebra::GroupGraphPattern->new(); my $insert = RDF::Query::Algebra::Update->new(undef, $data, $empty, undef, 1); $self->_add_patterns( $insert ); $self->{build}{method} = 'UPDATE'; } sub _DeleteDataUpdate { my $self = shift; $self->_eat('{'); $self->__consume_ws_opt; local($self->{__data_pattern}) = 1; local($self->{__no_bnodes}) = "DELETE DATA block"; $self->_ModifyTemplate(); $self->__consume_ws_opt; my $data = $self->_remove_pattern; $self->_eat('}'); $self->__consume_ws_opt; my $empty = RDF::Query::Algebra::GroupGraphPattern->new(); my $delete = RDF::Query::Algebra::Update->new($data, undef, $empty, undef, 1); $self->_add_patterns( $delete ); $self->{build}{method} = 'UPDATE'; } sub _InsertUpdate { my $self = shift; my $graph = shift; $self->_eat('{'); $self->__consume_ws_opt; $self->_ModifyTemplate(); $self->__consume_ws_opt; my $data = $self->_remove_pattern; $self->_eat('}'); $self->__consume_ws_opt; if ($graph) { $data = RDF::Query::Algebra::NamedGraph->new( $graph, $data ); } my %dataset; while ($self->_test(qr/USING/i)) { $self->{build}{custom_update_dataset} = 1; $self->_eat(qr/USING/i); $self->__consume_ws_opt; my $named = 0; if ($self->_test(qr/NAMED/i)) { $self->_eat(qr/NAMED/i); $self->__consume_ws_opt; $named = 1; } $self->_IRIref; my ($iri) = splice( @{ $self->{stack} } ); if ($named) { $dataset{named}{$iri->uri_value} = $iri; } else { push(@{ $dataset{default} }, $iri ); } $self->__consume_ws_opt; } $self->_eat(qr/WHERE/i); $self->__consume_ws_opt; if ($graph) { # local($self->{named_graph}) = $graph; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; $ggp = RDF::Query::Algebra::NamedGraph->new( $graph, $ggp ); $self->_add_patterns( $ggp ); } else { $self->_GroupGraphPattern; } my $ggp = $self->_remove_pattern; my @ds_keys = keys %dataset; unless (@ds_keys) { $dataset{ default } = [$graph || ()]; } my $insert = RDF::Query::Algebra::Update->new(undef, $data, $ggp, \%dataset, 0); $self->_add_patterns( $insert ); $self->{build}{method} = 'UPDATE'; } sub _DeleteUpdate { my $self = shift; my $graph = shift; my ($delete_data, $insert_data); my %dataset; my $delete_where = 0; if ($self->_test(qr/WHERE/i)) { if ($graph) { throw RDF::Query::Error::ParseError -text => "Syntax error: WITH clause cannot be used with DELETE WHERE operations"; } $delete_where = 1; } else { { local($self->{__no_bnodes}) = "DELETE block"; $self->_eat('{'); $self->__consume_ws_opt; $self->_ModifyTemplate( $graph ); $self->__consume_ws_opt; $self->_eat('}'); } $delete_data = $self->_remove_pattern; $self->__consume_ws_opt; if ($self->_test(qr/INSERT/i)) { $self->_eat(qr/INSERT/i); $self->__consume_ws_opt; $self->_eat('{'); $self->__consume_ws_opt; $self->_ModifyTemplate( $graph ); $self->__consume_ws_opt; $self->_eat('}'); $self->__consume_ws_opt; $insert_data = $self->_remove_pattern; } while ($self->_test(qr/USING/i)) { $self->{build}{custom_update_dataset} = 1; $self->_eat(qr/USING/i); $self->__consume_ws_opt; my $named = 0; if ($self->_test(qr/NAMED/i)) { $self->_eat(qr/NAMED/i); $self->__consume_ws_opt; $named = 1; } $self->_IRIref; my ($iri) = splice( @{ $self->{stack} } ); if ($named) { $dataset{named}{$iri->uri_value} = $iri; } else { push(@{ $dataset{default} }, $iri ); } $self->__consume_ws_opt; } } $self->_eat(qr/WHERE/i); $self->__consume_ws_opt; if ($graph) { # local($self->{named_graph}) = $graph; $self->{__no_bnodes} = "DELETE WHERE block" if ($delete_where); $self->_GroupGraphPattern; delete $self->{__no_bnodes}; my $ggp = $self->_remove_pattern; $ggp = RDF::Query::Algebra::NamedGraph->new( $graph, $ggp ); $self->_add_patterns( $ggp ); } else { $self->{__no_bnodes} = "DELETE WHERE block" if ($delete_where); $self->_GroupGraphPattern; delete $self->{__no_bnodes}; } my $ggp = $self->_remove_pattern; if ($delete_where) { $delete_data = $ggp; } my @ds_keys = keys %dataset; if ($graph and not(scalar(@ds_keys))) { $dataset{ default } = [$graph || ()]; } my $insert = RDF::Query::Algebra::Update->new($delete_data, $insert_data, $ggp, \%dataset, 0); $self->_add_patterns( $insert ); $self->{build}{method} = 'UPDATE'; } sub _ModifyTemplate_test { my $self = shift; return 1 if ($self->_TriplesBlock_test); return 1 if ($self->_test(qr/GRAPH/i)); return 0; } sub _ModifyTemplate { my $self = shift; my $graph = shift; local($self->{named_graph}); if ($graph) { $self->{named_graph} = $graph; } # $self->__ModifyTemplate; # $self->__consume_ws_opt; # my $data = $self->_remove_pattern; # $data = RDF::Query::Algebra::GroupGraphPattern->new( $data ) unless ($data->isa('RDF::Query::Algebra::GroupGraphPattern')); my $data; while ($self->_ModifyTemplate_test) { $self->__ModifyTemplate( $graph ); $self->__consume_ws_opt; my $d = $self->_remove_pattern; my @patterns = blessed($data) ? $data->patterns : (); $data = RDF::Query::Algebra::GroupGraphPattern->new( @patterns, $d ); } $data = RDF::Query::Algebra::GroupGraphPattern->new() unless (blessed($data)); $data = RDF::Query::Algebra::GroupGraphPattern->new( $data ) unless ($data->isa('RDF::Query::Algebra::GroupGraphPattern')); $self->_add_patterns( $data ); } sub __ModifyTemplate { my $self = shift; my $graph = shift; local($self->{_modify_template}) = 1; if ($self->_TriplesBlock_test) { my $data; $self->_push_pattern_container; $self->_TriplesBlock; ($data) = @{ $self->_pop_pattern_container }; if ($graph) { my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $data ); my $data = RDF::Query::Algebra::NamedGraph->new( $graph, $ggp ); } $self->_add_patterns( $data ); } else { $self->_GraphGraphPattern; { my (@d) = splice(@{ $self->{stack} }); $self->__handle_GraphPatternNotTriples( @d ); } } } sub _LoadUpdate { my $self = shift; my $op = $self->_eat(qr/LOAD\s+(SILENT\s+)?/i); my $silent = ($op =~ /SILENT/); $self->__consume_ws_opt; $self->_IRIref; my ($iri) = splice( @{ $self->{stack} } ); $self->__consume_ws_opt; if ($self->_test(qr/INTO GRAPH/i)) { $self->_eat(qr/INTO GRAPH/i); $self->_ws; $self->_IRIref; my ($graph) = splice( @{ $self->{stack} } ); my $pat = RDF::Query::Algebra::Load->new( $iri, $graph, $silent ); $self->_add_patterns( $pat ); } else { my $pat = RDF::Query::Algebra::Load->new( $iri, undef, $silent ); $self->_add_patterns( $pat ); } $self->{build}{method} = 'LOAD'; } sub _CreateGraph { my $self = shift; my $op = $self->_eat(qr/CREATE\s+(SILENT\s+)?GRAPH/i); my $silent = ($op =~ /SILENT/i); $self->_ws; $self->_IRIref; my ($graph) = splice( @{ $self->{stack} } ); my $pat = RDF::Query::Algebra::Create->new( $graph ); $self->_add_patterns( $pat ); $self->{build}{method} = 'CREATE'; } sub _ClearGraphUpdate { my $self = shift; my $op = $self->_eat(qr/CLEAR(\s+SILENT)?/i); my $silent = ($op =~ /SILENT/i); $self->_ws; if ($self->_test(qr/GRAPH/i)) { $self->_eat(qr/GRAPH/i); $self->_ws; $self->_IRIref; my ($graph) = splice( @{ $self->{stack} } ); my $pat = RDF::Query::Algebra::Clear->new( $graph ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/DEFAULT/i)) { $self->_eat(qr/DEFAULT/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Trine::Node::Nil->new ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/NAMED/i)) { $self->_eat(qr/NAMED/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Query::Node::Resource->new('tag:gwilliams@cpan.org,2010-01-01:RT:NAMED') ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/ALL/i)) { $self->_eat(qr/ALL/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Query::Node::Resource->new('tag:gwilliams@cpan.org,2010-01-01:RT:ALL') ); $self->_add_patterns( $pat ); } $self->{build}{method} = 'CLEAR'; } sub _DropGraph { my $self = shift; my $op = $self->_eat(qr/DROP(\s+SILENT)?/i); my $silent = ($op =~ /SILENT/i); $self->_ws; if ($self->_test(qr/GRAPH/i)) { $self->_eat(qr/GRAPH/i); $self->_ws; $self->_IRIref; my ($graph) = splice( @{ $self->{stack} } ); my $pat = RDF::Query::Algebra::Clear->new( $graph ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/DEFAULT/i)) { $self->_eat(qr/DEFAULT/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Trine::Node::Nil->new ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/NAMED/i)) { $self->_eat(qr/NAMED/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Query::Node::Resource->new('tag:gwilliams@cpan.org,2010-01-01:RT:NAMED') ); $self->_add_patterns( $pat ); } elsif ($self->_test(qr/ALL/i)) { $self->_eat(qr/ALL/i); my $pat = RDF::Query::Algebra::Clear->new( RDF::Query::Node::Resource->new('tag:gwilliams@cpan.org,2010-01-01:RT:ALL') ); $self->_add_patterns( $pat ); } $self->{build}{method} = 'CLEAR'; } sub __graph { my $self = shift; if ($self->_test(qr/DEFAULT/i)) { $self->_eat(qr/DEFAULT/i); return RDF::Trine::Node::Nil->new(); } else { if ($self->_test(qr/GRAPH/)) { $self->_eat(qr/GRAPH/i); $self->__consume_ws_opt; } $self->_IRIref; my ($g) = splice( @{ $self->{stack} } ); return $g; } } sub _CopyUpdate { my $self = shift; my $op = $self->_eat(qr/COPY(\s+SILENT)?/i); my $silent = ($op =~ /SILENT/i); $self->_ws; my $from = $self->__graph(); $self->_ws; $self->_eat(qr/TO/i); $self->_ws; my $to = $self->__graph(); my $pattern = RDF::Query::Algebra::Copy->new( $from, $to, $silent ); $self->_add_patterns( $pattern ); $self->{build}{method} = 'UPDATE'; } sub _MoveUpdate { my $self = shift; my $op = $self->_eat(qr/MOVE(\s+SILENT)?/i); my $silent = ($op =~ /SILENT/i); $self->_ws; my $from = $self->__graph(); $self->_ws; $self->_eat(qr/TO/i); $self->_ws; my $to = $self->__graph(); my $pattern = RDF::Query::Algebra::Move->new( $from, $to, $silent ); $self->_add_patterns( $pattern ); $self->{build}{method} = 'UPDATE'; } sub _AddUpdate { my $self = shift; my $op = $self->_eat(qr/ADD(\s+SILENT)?/i); my $silent = ($op =~ /SILENT/i); $self->_ws; return $self->__UpdateShortcuts( 'ADD', $silent ); } sub __UpdateShortcuts { my $self = shift; my $op = shift; my $silent = shift; my ($from, $to); if ($self->_test(qr/DEFAULT/i)) { $self->_eat(qr/DEFAULT/i); } else { if ($self->_test(qr/GRAPH/)) { $self->_eat(qr/GRAPH/i); $self->__consume_ws_opt; } $self->_IRIref; ($from) = splice( @{ $self->{stack} } ); } $self->_ws; $self->_eat(qr/TO/i); $self->_ws; if ($self->_test(qr/DEFAULT/i)) { $self->_eat(qr/DEFAULT/i); } else { if ($self->_test(qr/GRAPH/)) { $self->_eat(qr/GRAPH/i); $self->__consume_ws_opt; } $self->_IRIref; ($to) = splice( @{ $self->{stack} } ); } my $from_pattern = RDF::Query::Algebra::GroupGraphPattern->new( RDF::Query::Algebra::BasicGraphPattern->new( RDF::Query::Algebra::Triple->new( map { RDF::Query::Node::Variable->new( $_ ) } qw(s p o) ) ) ); if (defined($from)) { $from_pattern = RDF::Query::Algebra::NamedGraph->new( $from, $from_pattern ); } my $to_pattern = RDF::Query::Algebra::GroupGraphPattern->new( RDF::Query::Algebra::BasicGraphPattern->new( RDF::Query::Algebra::Triple->new( map { RDF::Query::Node::Variable->new( $_ ) } qw(s p o) ) ) ); if (defined($to)) { $to_pattern = RDF::Query::Algebra::NamedGraph->new( $to, $to_pattern ); } my $to_graph = $to || RDF::Trine::Node::Nil->new; my $from_graph = $from || RDF::Trine::Node::Nil->new; my $drop_to = RDF::Query::Algebra::Clear->new( $to_graph, $silent ); my $update = RDF::Query::Algebra::Update->new( undef, $to_pattern, $from_pattern, undef, 0 ); my $drop_from = RDF::Query::Algebra::Clear->new( $from_graph ); my $pattern; if ($op eq 'MOVE') { $pattern = RDF::Query::Algebra::Sequence->new( $drop_to, $update, $drop_from ); } elsif ($op eq 'COPY') { $pattern = RDF::Query::Algebra::Sequence->new( $drop_to, $update ); } else { $pattern = $update; } $self->_add_patterns( $pattern ); $self->{build}{method} = 'UPDATE'; } # [5] SelectQuery ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( Var+ | '*' ) DatasetClause* WhereClause SolutionModifier sub _SelectQuery { my $self = shift; $self->_eat(qr/SELECT/i); $self->__consume_ws; if ($self->{tokens} =~ m/^(DISTINCT|REDUCED)/i) { my $mod = $self->_eat( qr/DISTINCT|REDUCED/i ); $self->__consume_ws; $self->{build}{options}{lc($mod)} = 1; } my $star = $self->__SelectVars; $self->_DatasetClause(); $self->__consume_ws_opt; $self->_WhereClause; if ($star) { my $triples = $self->{build}{triples} || []; my @vars; foreach my $t (@$triples) { my @v = $t->potentially_bound; push(@vars, @v); } @vars = RDF::Query::_uniq( @vars ); push( @{ $self->{build}{variables} }, map { $self->new_variable($_) } @vars ); } $self->__consume_ws_opt; $self->_SolutionModifier(); $self->__consume_ws_opt; if ($self->_test( qr/VALUES/i )) { $self->_eat( qr/VALUES/i ); $self->__consume_ws_opt; my @vars; # $self->_Var; # push( @vars, splice(@{ $self->{stack} })); # $self->__consume_ws_opt; my $parens = 0; if ($self->_test(qr/[(]/)) { $self->_eat( qr/[(]/ ); $parens = 1; } while ($self->_test(qr/[\$?]/)) { $self->_Var; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } if ($parens) { $self->_eat( qr/[)]/ ); } $self->__consume_ws_opt; my $count = scalar(@vars); if (not($parens) and $count == 0) { throw RDF::Query::Error::ParseError -text => "Syntax error: Expected VAR in inline data declaration"; } elsif (not($parens) and $count > 1) { throw RDF::Query::Error::ParseError -text => "Syntax error: Inline data declaration can only have one variable when parens are omitted"; } my $short = (not($parens) and $count == 1); $self->_eat('{'); $self->__consume_ws_opt; if (not($short) or ($short and $self->_Binding_test)) { while ($self->_Binding_test) { my $terms = $self->_Binding($count); push( @{ $self->{build}{bindings}{terms} }, $terms ); $self->__consume_ws_opt; } } else { while ($self->_BindingValue_test) { $self->_BindingValue; $self->__consume_ws_opt; my ($term) = splice(@{ $self->{stack} }); push( @{ $self->{build}{bindings}{terms} }, [$term] ); $self->__consume_ws_opt; } } $self->_eat('}'); $self->__consume_ws_opt; $self->{build}{bindings}{vars} = \@vars; } $self->__solution_modifiers( $star ); my $pattern = $self->{build}{triples}[0]; my @agg = $pattern->subpatterns_of_type( 'RDF::Query::Algebra::Aggregate', 'RDF::Query::Algebra::SubSelect' ); if (@agg) { my ($agg) = @agg; my @gvars = $agg->groupby; if (scalar(@gvars) == 0) { # aggregate query with no explicit group keys foreach my $v (@{ $self->{build}{variables} }) { if ($v->isa('RDF::Query::Node::Variable')) { my $name = $v->name; throw RDF::Query::Error::ParseError -text => "Syntax error: Variable used in projection but not present in aggregate grouping ($name)"; } } } } delete $self->{build}{options}; $self->{build}{method} = 'SELECT'; } sub __SelectVars { my $self = shift; my $star = 0; my @vars; my $count = 0; while ($self->_test('*') or $self->__SelectVar_test) { if ($self->_test('*')) { $self->{build}{star}++; $self->_eat('*'); $star = 1; $self->__consume_ws_opt; $count++; } else { $self->__SelectVar; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; $count++; } } my %seen; foreach my $v (@vars) { if ($v->isa('RDF::Query::Node::Variable') or $v->isa('RDF::Query::Expression::Alias')) { my $name = $v->name; if ($v->isa('RDF::Query::Expression::Alias')) { if ($seen{ $name }) { throw RDF::Query::Error::ParseError -text => "Syntax error: Repeated variable ($name) used in projection list"; } } $seen{ $name }++; } } $self->{build}{variables} = \@vars; if ($count == 0) { throw RDF::Query::Error::ParseError -text => "Syntax error: No select variable or expression specified"; } return $star; } sub _BrackettedAliasExpression { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(qr/AS/i); $self->__consume_ws_opt; $self->_Var; my ($var) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(')'); my $alias = $self->new_alias_expression( $var, $expr ); $self->_add_stack( $alias ); } sub __SelectVar_test { my $self = shift; local($self->{__aggregate_call_ok}) = 1; # return 1 if $self->_BuiltInCall_test; return 1 if $self->_test( qr/[(]/i); return $self->{tokens} =~ m'^[?$]'; } sub __SelectVar { my $self = shift; local($self->{__aggregate_call_ok}) = 1; if ($self->_test('(')) { $self->_BrackettedAliasExpression; # } elsif ($self->_BuiltInCall_test) { # $self->_BuiltInCall; } else { $self->_Var; } } # [6] ConstructQuery ::= 'CONSTRUCT' ConstructTemplate DatasetClause* WhereClause SolutionModifier sub _ConstructQuery { my $self = shift; $self->_eat(qr/CONSTRUCT/i); $self->__consume_ws_opt; my $shortcut = 1; if ($self->_test( qr/[{]/ )) { $shortcut = 0; $self->_ConstructTemplate; $self->__consume_ws_opt; } $self->_DatasetClause(); $self->__consume_ws_opt; if ($shortcut) { $self->_TriplesWhereClause; } else { $self->_WhereClause; } $self->__consume_ws_opt; $self->_SolutionModifier(); my $pattern = $self->{build}{triples}[0]; my $triples = delete $self->{build}{construct_triples}; my $construct = RDF::Query::Algebra::Construct->new( $pattern, $triples ); $self->{build}{triples}[0] = $construct; $self->{build}{method} = 'CONSTRUCT'; } # [7] DescribeQuery ::= 'DESCRIBE' ( VarOrIRIref+ | '*' ) DatasetClause* WhereClause? SolutionModifier sub _DescribeQuery { my $self = shift; $self->_eat(qr/DESCRIBE/i); $self->_ws; if ($self->_test('*')) { $self->_eat('*'); $self->{build}{variables} = ['*']; $self->__consume_ws_opt; } else { $self->_VarOrIRIref; $self->__consume_ws_opt; while ($self->_VarOrIRIref_test) { $self->_VarOrIRIref; $self->__consume_ws_opt; } $self->{build}{variables} = [ splice(@{ $self->{stack} }) ]; } $self->_DatasetClause(); $self->__consume_ws_opt; if ($self->_WhereClause_test) { $self->_WhereClause; } else { my $pattern = RDF::Query::Algebra::GroupGraphPattern->new(); $self->_add_patterns( $pattern ); } $self->__consume_ws_opt; $self->_SolutionModifier(); $self->{build}{method} = 'DESCRIBE'; } # [8] AskQuery ::= 'ASK' DatasetClause* WhereClause sub _AskQuery { my $self = shift; $self->_eat(qr/ASK/i); $self->__consume_ws_opt; $self->_DatasetClause(); $self->__consume_ws_opt; $self->_WhereClause; $self->{build}{variables} = []; $self->{build}{method} = 'ASK'; } sub _DatasetClause_test { my $self = shift; return $self->_test( qr/FROM/i ); } # [9] DatasetClause ::= 'FROM' ( DefaultGraphClause | NamedGraphClause ) sub _DatasetClause { my $self = shift; # my @dataset; $self->{build}{sources} = []; while ($self->_test( qr/FROM/i )) { $self->_eat( qr/FROM/i ); $self->__consume_ws; if ($self->_test( qr/NAMED/i )) { $self->_NamedGraphClause; } else { $self->_DefaultGraphClause; } $self->__consume_ws_opt; } } # [10] DefaultGraphClause ::= SourceSelector sub _DefaultGraphClause { my $self = shift; $self->_SourceSelector; my ($source) = splice(@{ $self->{stack} }); push( @{ $self->{build}{sources} }, [$source] ); } # [11] NamedGraphClause ::= 'NAMED' SourceSelector sub _NamedGraphClause { my $self = shift; $self->_eat( qr/NAMED/i ); $self->__consume_ws_opt; $self->_SourceSelector; my ($source) = splice(@{ $self->{stack} }); push( @{ $self->{build}{sources} }, [$source, 'NAMED'] ); } # [12] SourceSelector ::= IRIref sub _SourceSelector { my $self = shift; $self->_IRIref; } # [13] WhereClause ::= 'WHERE'? GroupGraphPattern sub _WhereClause_test { my $self = shift; return $self->_test( qr/WHERE|{/i ); } sub _WhereClause { my $self = shift; if ($self->_test( qr/WHERE/i )) { $self->_eat( qr/WHERE/i ); } $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_peek_pattern; $ggp->check_duplicate_blanks; } sub _TriplesWhereClause { my $self = shift; $self->_push_pattern_container; $self->_eat( qr/WHERE/i ); $self->__consume_ws_opt; $self->_eat(qr/{/); $self->__consume_ws_opt; if ($self->_TriplesBlock_test) { $self->_TriplesBlock; } $self->_eat(qr/}/); my $cont = $self->_pop_pattern_container; $self->{build}{construct_triples} = $cont->[0]; my $pattern = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_add_patterns( $pattern ); } sub _Binding_test { my $self = shift; return $self->_test( '(' ); } sub _Binding { my $self = shift; my $count = shift; $self->_eat( '(' ); $self->__consume_ws_opt; my @terms; foreach my $i (1..$count) { unless ($self->_BindingValue_test) { my $found = $i-1; throw RDF::Query::Error::ParseError -text => "Syntax error: Expected $count BindingValues but only found $found"; } $self->_BindingValue; push( @terms, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } $self->__consume_ws_opt; $self->_eat( ')' ); return \@terms; } sub _BindingValue_test { my $self = shift; return 1 if ($self->_IRIref_test); return 1 if ($self->_test(qr/UNDEF|[<'".0-9]|(true|false)\b|_:|\([\n\r\t ]*\)/)); return 0; } sub _BindingValue { my $self = shift; if ($self->_test(qr/UNDEF/i)) { $self->_eat(qr/UNDEF/i); push(@{ $self->{stack} }, undef); } else { $self->_GraphTerm; } } # [20] GroupCondition ::= ( BuiltInCall | FunctionCall | '(' Expression ( 'AS' Var )? ')' | Var ) sub __GroupByVar_test { my $self = shift; return 1 if ($self->_BuiltInCall_test); return 1 if ($self->_IRIref_test); return 1 if ($self->_test( qr/[(]/i )); return 1 if ($self->_test(qr/[\$?]/)); } sub __GroupByVar { my $self = shift; if ($self->_test('(')) { $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; if ($self->_test(qr/AS/i)) { $self->_eat('AS'); $self->__consume_ws_opt; $self->_Var; my ($var) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; my $alias = $self->new_alias_expression( $var, $expr ); $self->_add_stack( $alias ); } else { $self->_add_stack( $expr ); } $self->_eat(')'); } elsif ($self->_IRIref_test) { $$self->_FunctionCall; } elsif ($self->_BuiltInCall_test) { $self->_BuiltInCall; } else { $self->_Var; } } # [14] SolutionModifier ::= OrderClause? LimitOffsetClauses? sub _SolutionModifier { my $self = shift; if ($self->_test( qr/GROUP\s+BY/i )) { $self->_GroupClause; $self->__consume_ws_opt; } if ($self->_test( qr/HAVING/i )) { $self->_HavingClause; $self->__consume_ws_opt; } if ($self->_OrderClause_test) { $self->_OrderClause; $self->__consume_ws_opt; } if ($self->_LimitOffsetClauses_test) { $self->_LimitOffsetClauses; } } sub _GroupClause { my $self = shift; $self->_eat( qr/GROUP\s+BY/i ); if ($self->{build}{star}) { throw RDF::Query::Error::ParseError -text => "Syntax error: SELECT * cannot be used with aggregate grouping"; } $self->{build}{__aggregate} ||= {}; my @vars; $self->__consume_ws_opt; $self->__GroupByVar; my ($v) = splice(@{ $self->{stack} }); push( @vars, $v ); $self->__consume_ws_opt; while ($self->__GroupByVar_test) { $self->__GroupByVar; my ($v) = splice(@{ $self->{stack} }); push( @vars, $v ); $self->__consume_ws_opt; } my %seen; foreach my $v (@vars) { if ($v->isa('RDF::Query::Node::Variable') or $v->isa('RDF::Query::Expression::Alias')) { my $name = $v->name; $seen{ $name }++; } } foreach my $v (@{ $self->{build}{variables} }) { if ($v->isa('RDF::Query::Node::Variable')) { my $name = $v->name; unless ($seen{ $name }) { throw RDF::Query::Error::ParseError -text => "Syntax error: Variable used in projection but not present in aggregate grouping ($name)"; } } elsif ($v->isa('RDF::Query::Expression::Alias')) { my $expr = $v->expression; # warn 'expression: ' . Dumper($expr); if ($expr->isa('RDF::Query::Node::Variable::ExpressionProxy')) { # RDF::Query::Node::Variable::ExpressionProxy is used for aggregate operations. # we can ignore these because any variable used in an aggreate is valid, even if it's not mentioned in the grouping keys } elsif ($expr->isa('RDF::Query::Expression')) { my @vars = $expr->nonaggregated_referenced_variables; foreach my $name (@vars) { unless ($seen{ $name }) { throw RDF::Query::Error::ParseError -text => "Syntax error: Variable used in projection but not present in aggregate grouping ($name)"; } } } } } $self->{build}{__group_by} = \@vars; $self->__consume_ws_opt; } sub _HavingClause { my $self = shift; $self->_eat(qr/HAVING/i); $self->__consume_ws_opt; $self->{build}{__aggregate} ||= {}; local($self->{__aggregate_call_ok}) = 1; $self->_Constraint; my ($expr) = splice(@{ $self->{stack} }); $self->{build}{__having} = $expr; } # [15] LimitOffsetClauses ::= ( LimitClause OffsetClause? | OffsetClause LimitClause? ) sub _LimitOffsetClauses_test { my $self = shift; return $self->_test( qr/LIMIT|OFFSET/i ); } sub _LimitOffsetClauses { my $self = shift; if ($self->_LimitClause_test) { $self->_LimitClause; $self->__consume_ws_opt; if ($self->_OffsetClause_test) { $self->_OffsetClause; } } else { $self->_OffsetClause; $self->__consume_ws_opt; if ($self->_LimitClause_test) { $self->_LimitClause; } } } # [16] OrderClause ::= 'ORDER' 'BY' OrderCondition+ sub _OrderClause_test { my $self = shift; return $self->_test( qr/ORDER[\n\r\t ]+BY/i ); } sub _OrderClause { my $self = shift; $self->_eat( qr/ORDER/i ); $self->__consume_ws; $self->_eat( qr/BY/i ); $self->__consume_ws_opt; my @order; $self->{build}{__aggregate} ||= {}; local($self->{__aggregate_call_ok}) = 1; $self->_OrderCondition; $self->__consume_ws_opt; push(@order, splice(@{ $self->{stack} })); while ($self->_OrderCondition_test) { $self->_OrderCondition; $self->__consume_ws_opt; push(@order, splice(@{ $self->{stack} })); } $self->{build}{options}{orderby} = \@order; } # [17] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression ) | ( Constraint | Var ) sub _OrderCondition_test { my $self = shift; return 1 if $self->_test( qr/ASC|DESC|[?\$]/i ); return 1 if $self->_Constraint_test; return 0; } sub _OrderCondition { my $self = shift; my $dir = 'ASC'; if ($self->_test( qr/ASC|DESC/i )) { $dir = uc( $self->_eat( qr/ASC|DESC/i ) ); $self->__consume_ws_opt; $self->_BrackettedExpression; } elsif ($self->_test( qr/[?\$]/ )) { $self->_Var; } else { $self->_Constraint; } my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( [ $dir, $expr ] ); } # [18] LimitClause ::= 'LIMIT' INTEGER sub _LimitClause_test { my $self = shift; return $self->_test( qr/LIMIT/i ); } sub _LimitClause { my $self = shift; $self->_eat( qr/LIMIT/i ); $self->__consume_ws; my $limit = $self->_eat( $r_INTEGER ); $self->{build}{options}{limit} = $limit; } # [19] OffsetClause ::= 'OFFSET' INTEGER sub _OffsetClause_test { my $self = shift; return $self->_test( qr/OFFSET/i ); } sub _OffsetClause { my $self = shift; $self->_eat( qr/OFFSET/i ); $self->__consume_ws; my $off = $self->_eat( $r_INTEGER ); $self->{build}{options}{offset} = $off; } # [20] GroupGraphPattern ::= '{' TriplesBlock? ( ( GraphPatternNotTriples | Filter ) '.'? TriplesBlock? )* '}' sub _GroupGraphPattern { my $self = shift; $self->_eat('{'); $self->__consume_ws_opt; if ($self->_SubSelect_test) { $self->_SubSelect; } else { $self->_GroupGraphPatternSub; } $self->__consume_ws_opt; $self->_eat('}'); } sub _GroupGraphPatternSub { my $self = shift; $self->_push_pattern_container; my $got_pattern = 0; my $need_dot = 0; if ($self->_TriplesBlock_test) { $need_dot = 1; $got_pattern++; $self->_TriplesBlock; $self->__consume_ws_opt; } my $pos = length($self->{tokens}); while (not $self->_test('}')) { if ($self->_GraphPatternNotTriples_test) { $need_dot = 0; $got_pattern++; $self->_GraphPatternNotTriples; $self->__consume_ws_opt; my (@data) = splice(@{ $self->{stack} }); $self->__handle_GraphPatternNotTriples( @data ); $self->__consume_ws_opt; } elsif ($self->_test( qr/FILTER/i )) { $got_pattern++; $need_dot = 0; $self->_Filter; $self->__consume_ws_opt; } if ($need_dot or $self->_test('.')) { $self->_eat('.'); if ($got_pattern) { $need_dot = 0; $got_pattern = 0; } else { throw RDF::Query::Error::ParseError -text => "Syntax error: Extra dot found without preceding pattern"; } $self->__consume_ws_opt; } if ($self->_TriplesBlock_test) { my $peek = $self->_peek_pattern; if (blessed($peek) and $peek->isa('RDF::Query::Algebra::BasicGraphPattern')) { $self->_TriplesBlock; my $rhs = $self->_remove_pattern; my $lhs = $self->_remove_pattern; my $merged = $self->__new_bgp( map { $_->triples } ($lhs, $rhs) ); $self->_add_patterns( $merged ); } else { $self->_TriplesBlock; } $self->__consume_ws_opt; } $self->__consume_ws_opt; last unless ($self->_test( qr/\S/ )); my $new = length($self->{tokens}); if ($pos == $new) { # we haven't progressed, and so would infinite loop if we don't break out and throw an error. $self->_syntax_error(''); } else { $pos = $new; } } my $cont = $self->_pop_pattern_container; my @filters = splice(@{ $self->{filters} }); my @patterns; my $pattern = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); if (@filters) { while (my $f = shift @filters) { $pattern = RDF::Query::Algebra::Filter->new( $f, $pattern ); } } $self->_add_patterns( $pattern ); } sub __handle_GraphPatternNotTriples { my $self = shift; my $data = shift; my ($class, @args) = @$data; if ($class =~ /^RDF::Query::Algebra::(Optional|Minus)$/) { my $cont = $self->_pop_pattern_container; my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_push_pattern_container; # my $ggp = $self->_remove_pattern(); unless ($ggp) { $ggp = RDF::Query::Algebra::GroupGraphPattern->new(); } my $opt = $class->new( $ggp, @args ); $self->_add_patterns( $opt ); } elsif ($class eq 'RDF::Query::Algebra::Table') { my ($table) = @args; $self->_add_patterns( $table ); } elsif ($class eq 'RDF::Query::Algebra::Extend') { my $cont = $self->_pop_pattern_container; my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_push_pattern_container; # my $ggp = $self->_remove_pattern(); unless ($ggp) { $ggp = RDF::Query::Algebra::GroupGraphPattern->new(); } my $alias = $args[0]; my %in_scope = map { $_ => 1 } $ggp->potentially_bound(); my $var = $alias->name; if (exists $in_scope{ $var }) { throw RDF::Query::Error::QueryPatternError -text => "Syntax error: BIND used with variable already in scope"; } my $bind = $class->new( $ggp, [$alias] ); $self->_add_patterns( $bind ); } elsif ($class eq 'RDF::Query::Algebra::Service') { my ($endpoint, $pattern, $silent) = @args; if ($endpoint->isa('RDF::Query::Node::Variable')) { # SERVICE ?var my $cont = $self->_pop_pattern_container; my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_push_pattern_container; # my $ggp = $self->_remove_pattern(); unless ($ggp) { $ggp = RDF::Query::Algebra::GroupGraphPattern->new(); } my $service = $class->new( $endpoint, $pattern, $silent, $ggp ); $self->_add_patterns( $service ); } else { # SERVICE # no-op my $service = $class->new( $endpoint, $pattern, $silent ); $self->_add_patterns( $service ); } } elsif ($class =~ /RDF::Query::Algebra::(Union|NamedGraph|GroupGraphPattern)$/) { # no-op } else { throw RDF::Query::Error::ParseError 'Unrecognized GraphPattern: ' . $class; } } sub _SubSelect_test { my $self = shift; return $self->_test(qr/SELECT/i); } sub _SubSelect { my $self = shift; my $pattern; { local($self->{error}); local($self->{namespaces}) = $self->{namespaces}; local($self->{blank_ids}) = $self->{blank_ids}; local($self->{stack}) = []; local($self->{filters}) = []; local($self->{pattern_container_stack}) = []; my $triples = $self->_push_pattern_container(); local($self->{build}) = { triples => $triples}; if ($self->{baseURI}) { $self->{build}{base} = $self->{baseURI}; } $self->_eat(qr/SELECT/i); $self->__consume_ws; if ($self->{tokens} =~ m/^(DISTINCT|REDUCED)/i) { my $mod = $self->_eat( qr/DISTINCT|REDUCED/i ); $self->__consume_ws; $self->{build}{options}{lc($mod)} = 1; } my $star = $self->__SelectVars; $self->__consume_ws_opt; $self->_WhereClause; if ($star) { my $triples = $self->{build}{triples} || []; my @vars; foreach my $t (@$triples) { my @v = $t->potentially_bound; push(@vars, @v); } @vars = RDF::Query::_uniq( @vars ); push( @{ $self->{build}{variables} }, map { $self->new_variable($_) } @vars ); } $self->__consume_ws_opt; $self->_SolutionModifier(); if ($self->{build}{options}{orderby}) { my $order = delete $self->{build}{options}{orderby}; my $pattern = pop(@{ $self->{build}{triples} }); my $sort = RDF::Query::Algebra::Sort->new( $pattern, @$order ); push(@{ $self->{build}{triples} }, $sort); } $self->__consume_ws_opt; if ($self->_test( qr/VALUES/i )) { $self->_eat( qr/VALUES/i ); $self->__consume_ws_opt; my @vars; my $parens = 0; if ($self->_test(qr/[(]/)) { $self->_eat( qr/[(]/ ); $parens = 1; } while ($self->_test(qr/[\$?]/)) { $self->_Var; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } if ($parens) { $self->_eat( qr/[)]/ ); } $self->__consume_ws_opt; my $count = scalar(@vars); if (not($parens) and $count == 0) { throw RDF::Query::Error::ParseError -text => "Syntax error: Expected VAR in inline data declaration"; } elsif (not($parens) and $count > 1) { throw RDF::Query::Error::ParseError -text => "Syntax error: Inline data declaration can only have one variable when parens are omitted"; } my $short = (not($parens) and $count == 1); $self->_eat('{'); $self->__consume_ws_opt; if (not($short) or ($short and $self->_Binding_test)) { while ($self->_Binding_test) { my $terms = $self->_Binding($count); push( @{ $self->{build}{bindings}{terms} }, $terms ); $self->__consume_ws_opt; } } else { while ($self->_BindingValue_test) { $self->_BindingValue; $self->__consume_ws_opt; my ($term) = splice(@{ $self->{stack} }); push( @{ $self->{build}{bindings}{terms} }, [$term] ); $self->__consume_ws_opt; } } $self->_eat('}'); $self->__consume_ws_opt; $self->{build}{bindings}{vars} = \@vars; } $self->__solution_modifiers( $star ); delete $self->{build}{options}; my $data = delete $self->{build}; $data->{method} = 'SELECT'; my $query = RDF::Query->_new( base => $self->{baseURI}, # parser => $self, parsed => { %$data }, ); $pattern = RDF::Query::Algebra::SubSelect->new( $query ); } $self->_add_patterns( $pattern ); } # [21] TriplesBlock ::= TriplesSameSubject ( '.' TriplesBlock? )? sub _TriplesBlock_test { my $self = shift; # VarOrTerm | TriplesNode -> (Var | GraphTerm) | (Collection | BlankNodePropertyList) -> Var | IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | Collection | BlankNodePropertyList # but since a triple can't start with a literal, this is reduced to: # Var | IRIref | BlankNode | NIL return $self->_test(qr/[\$?]|<|_:|\[[\n\r\t ]*\]|\([\n\r\t ]*\)|\[|[[(]|${r_PNAME_NS}/); } sub _TriplesBlock { my $self = shift; $self->_push_pattern_container; $self->__TriplesBlock; my $triples = $self->_pop_pattern_container; my $bgp = $self->__new_bgp( @$triples ); $self->_add_patterns( $bgp ); } ## this one (with two underscores) doesn't pop patterns off the stack and make a BGP. ## instead, things are left on the stack so we can recurse without doing the wrong thing. ## the one with one underscore (_TriplesBlock) will pop everything off and make the BGP. sub __TriplesBlock { my $self = shift; my $got_dot = 0; TRIPLESBLOCKLOOP: $self->_TriplesSameSubjectPath; $self->__consume_ws_opt; while ($self->_test('.')) { if ($got_dot) { throw RDF::Query::Error::ParseError -text => "Syntax error: found extra DOT after TriplesBlock"; } $self->_eat('.'); $got_dot++; $self->__consume_ws_opt; if ($self->_TriplesBlock_test) { $got_dot = 0; goto TRIPLESBLOCKLOOP; } $self->__consume_ws_opt; } $self->__consume_ws_opt; } # [22] GraphPatternNotTriples ::= OptionalGraphPattern | GroupOrUnionGraphPattern | GraphGraphPattern sub _GraphPatternNotTriples_test { my $self = shift; return 1 if $self->_test(qr/VALUES/i); # InlineDataClause return $self->_test(qr/BIND|SERVICE|MINUS|OPTIONAL|{|GRAPH/i); } sub _GraphPatternNotTriples { my $self = shift; if ($self->_test(qr/VALUES/i)) { $self->_InlineDataClause; } elsif ($self->_test(qr/SERVICE/i)) { $self->_ServiceGraphPattern; } elsif ($self->_test(qr/MINUS/i)) { $self->_MinusGraphPattern; } elsif ($self->_test(qr/BIND/i)) { $self->_Bind; } elsif ($self->_OptionalGraphPattern_test) { $self->_OptionalGraphPattern; } elsif ($self->_GroupOrUnionGraphPattern_test) { $self->_GroupOrUnionGraphPattern; } else { $self->_GraphGraphPattern; } } sub _InlineDataClause { my $self = shift; $self->_eat( qr/VALUES/i ); $self->__consume_ws_opt; my @vars; my $parens = 0; if ($self->_test(qr/[(]/)) { $self->_eat( qr/[(]/ ); $parens = 1; } while ($self->_test(qr/[\$?]/)) { $self->_Var; push( @vars, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } if ($parens) { $self->_eat( qr/[)]/ ); } my $count = scalar(@vars); if (not($parens) and $count == 0) { throw RDF::Query::Error::ParseError -text => "Syntax error: Expected VAR in inline data declaration"; } elsif (not($parens) and $count > 1) { throw RDF::Query::Error::ParseError -text => "Syntax error: Inline data declaration can only have one variable when parens are omitted"; } my $short = (not($parens) and $count == 1); $self->_eat('{'); $self->__consume_ws_opt; my @rows; if (not($short) or ($short and $self->_Binding_test)) { # { (term) (term) } while ($self->_Binding_test) { my $terms = $self->_Binding($count); push( @rows, $terms ); $self->__consume_ws_opt; } } else { # { term term } while ($self->_BindingValue_test) { $self->_BindingValue; $self->__consume_ws_opt; my ($term) = splice(@{ $self->{stack} }); push( @rows, [$term] ); } } $self->_eat('}'); $self->__consume_ws_opt; my @vbs = map { my %d; @d{ map { $_->name } @vars } = @$_; RDF::Query::VariableBindings->new(\%d) } @rows; my $table = RDF::Query::Algebra::Table->new( [ map { $_->name } @vars ], @vbs ); $self->_add_stack( ['RDF::Query::Algebra::Table', $table] ); } sub _Bind { my $self = shift; $self->_eat(qr/BIND/i); $self->__consume_ws_opt; $self->_BrackettedAliasExpression; my ($alias) = splice(@{ $self->{stack} }); $self->_add_stack( ['RDF::Query::Algebra::Extend', $alias] ); } sub _ServiceGraphPattern { my $self = shift; my $op = $self->_eat( qr/SERVICE(\s+SILENT)?/i ); my $silent = ($op =~ /SILENT/i); $self->__consume_ws_opt; if ($self->_test(qr/[\$?]/)) { $self->__close_bgp_with_filters; $self->_Var; } else { $self->_IRIref; } my ($endpoint) = splice( @{ $self->{stack} } ); $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; # my $pattern = RDF::Query::Algebra::Service->new( $endpoint, $ggp, $silent ); # $self->_add_patterns( $pattern ); my $opt = ['RDF::Query::Algebra::Service', $endpoint, $ggp, ($silent ? 1 : 0)]; $self->_add_stack( $opt ); } # [23] OptionalGraphPattern ::= 'OPTIONAL' GroupGraphPattern sub _OptionalGraphPattern_test { my $self = shift; return $self->_test( qr/OPTIONAL/i ); } sub __close_bgp_with_filters { my $self = shift; my @filters = splice(@{ $self->{filters} }); if (@filters) { my $cont = $self->_pop_pattern_container; my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @$cont ); $self->_push_pattern_container; # my $ggp = $self->_remove_pattern(); unless ($ggp) { $ggp = RDF::Query::Algebra::GroupGraphPattern->new(); } while (my $f = shift @filters) { $ggp = RDF::Query::Algebra::Filter->new( $f, $ggp ); } $self->_add_patterns($ggp); } } sub _OptionalGraphPattern { my $self = shift; $self->_eat( qr/OPTIONAL/i ); $self->__close_bgp_with_filters; $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; my $opt = ['RDF::Query::Algebra::Optional', $ggp]; $self->_add_stack( $opt ); } sub _MinusGraphPattern { my $self = shift; $self->_eat( qr/MINUS/i ); $self->__close_bgp_with_filters; $self->__consume_ws_opt; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; my $opt = ['RDF::Query::Algebra::Minus', $ggp]; $self->_add_stack( $opt ); } # [24] GraphGraphPattern ::= 'GRAPH' VarOrIRIref GroupGraphPattern sub _GraphGraphPattern { my $self = shift; if ($self->{__data_pattern}) { if ($self->{__graph_nesting_level}++) { throw RDF::Query::Error::ParseError -text => "Syntax error: Nested named GRAPH blocks not allowed in data template."; } } $self->_eat( qr/GRAPH\b/i ); $self->__consume_ws_opt; $self->_VarOrIRIref; my ($graph) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; if ($graph->isa('RDF::Trine::Node::Resource')) { local($self->{named_graph}) = $graph; $self->_GroupGraphPattern; } else { $self->_GroupGraphPattern; } if ($self->{__data_pattern}) { $self->{__graph_nesting_level}--; } my $ggp = $self->_remove_pattern; my $pattern = RDF::Query::Algebra::NamedGraph->new( $graph, $ggp ); $self->_add_patterns( $pattern ); $self->_add_stack( [ 'RDF::Query::Algebra::NamedGraph' ] ); } # [25] GroupOrUnionGraphPattern ::= GroupGraphPattern ( 'UNION' GroupGraphPattern )* sub _GroupOrUnionGraphPattern_test { my $self = shift; return $self->_test('{'); } sub _GroupOrUnionGraphPattern { my $self = shift; $self->_GroupGraphPattern; my $ggp = $self->_remove_pattern; $self->__consume_ws_opt; if ($self->_test( qr/UNION/i )) { while ($self->_test( qr/UNION/i )) { $self->_eat( qr/UNION/i ); $self->__consume_ws_opt; $self->_GroupGraphPattern; $self->__consume_ws_opt; my $rhs = $self->_remove_pattern; $ggp = RDF::Query::Algebra::Union->new( $ggp, $rhs ); } $self->_add_patterns( $ggp ); $self->_add_stack( [ 'RDF::Query::Algebra::Union' ] ); } else { $self->_add_patterns( $ggp ); $self->_add_stack( [ 'RDF::Query::Algebra::GroupGraphPattern' ] ); } } # [26] Filter ::= 'FILTER' Constraint sub _Filter { my $self = shift; $self->_eat( qr/FILTER/i ); $self->__consume_ws_opt; $self->_Constraint; my ($expr) = splice(@{ $self->{stack} }); $self->_add_filter( $expr ); } # [27] Constraint ::= BrackettedExpression | BuiltInCall | FunctionCall sub _Constraint_test { my $self = shift; return 1 if $self->_test( qr/[(]/ ); return 1 if $self->_BuiltInCall_test; return 1 if $self->_FunctionCall_test; return 0; } sub _Constraint { my $self = shift; if ($self->_BrackettedExpression_test) { $self->_BrackettedExpression(); } elsif ($self->_BuiltInCall_test) { $self->_BuiltInCall(); } else { $self->_FunctionCall(); } } # [28] FunctionCall ::= IRIref ArgList sub _FunctionCall_test { my $self = shift; return $self->_IRIref_test; } sub _FunctionCall { my $self = shift; $self->_IRIref; my ($iri) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ArgList; my @args = splice(@{ $self->{stack} }); my $func = $self->new_function_expression( $iri, @args ); $self->_add_stack( $func ); } # [29] ArgList ::= ( NIL | '(' Expression ( ',' Expression )* ')' ) sub _ArgList_test { my $self = shift; return $self->_test('('); } sub _ArgList { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; my @args; unless ($self->_test(')')) { $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); while ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); } } $self->_eat(')'); $self->_add_stack( @args ); } # [30] ConstructTemplate ::= '{' ConstructTriples? '}' sub _ConstructTemplate { my $self = shift; $self->_push_pattern_container; $self->_eat( '{' ); $self->__consume_ws_opt; if ($self->_ConstructTriples_test) { $self->_ConstructTriples; } $self->__consume_ws_opt; $self->_eat( '}' ); my $cont = $self->_pop_pattern_container; $self->{build}{construct_triples} = $cont; } # [31] ConstructTriples ::= TriplesSameSubject ( '.' ConstructTriples? )? sub _ConstructTriples_test { my $self = shift; return $self->_TriplesBlock_test; } sub _ConstructTriples { my $self = shift; $self->_TriplesSameSubject; $self->__consume_ws_opt; while ($self->_test(qr/[.]/)) { $self->_eat( qr/[.]/ ); $self->__consume_ws_opt; if ($self->_ConstructTriples_test) { $self->_TriplesSameSubject; } } } # [32] TriplesSameSubject ::= VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList sub _TriplesSameSubject { my $self = shift; my @triples; if ($self->_TriplesNode_test) { $self->_TriplesNode; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyList; $self->__consume_ws_opt; my @list = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } else { $self->_VarOrTerm; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyListNotEmpty; $self->__consume_ws_opt; my (@list) = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } $self->_add_patterns( @triples ); # return @triples; } # TriplesSameSubjectPath ::= VarOrTerm PropertyListNotEmptyPath | TriplesNode PropertyListPath sub _TriplesSameSubjectPath { my $self = shift; my @triples; if ($self->_TriplesNode_test) { $self->_TriplesNode; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyListPath; $self->__consume_ws_opt; my @list = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } else { $self->_VarOrTerm; my ($s) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_PropertyListNotEmptyPath; $self->__consume_ws_opt; my (@list) = splice(@{ $self->{stack} }); foreach my $data (@list) { push(@triples, $self->__new_statement( $s, @$data )); } } $self->_add_patterns( @triples ); # return @triples; } # [33] PropertyListNotEmpty ::= Verb ObjectList ( ';' ( Verb ObjectList )? )* sub _PropertyListNotEmpty { my $self = shift; $self->_Verb; my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); my @props = map { [$v, $_] } @l; while ($self->_test(qr'\s*;')) { $self->_eat(';'); $self->__consume_ws_opt; if ($self->_Verb_test) { $self->_Verb; my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); push(@props, map { [$v, $_] } @l); } } $self->_add_stack( @props ); } # [34] PropertyList ::= PropertyListNotEmpty? sub _PropertyList { my $self = shift; if ($self->_Verb_test) { $self->_PropertyListNotEmpty; } } # [33] PropertyListNotEmptyPath ::= (VerbPath | VerbSimple) ObjectList ( ';' ( (VerbPath | VerbSimple) ObjectList )? )* sub _PropertyListNotEmptyPath { my $self = shift; if ($self->_VerbPath_test) { $self->_VerbPath; } else { $self->_VerbSimple; } my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); my @props = map { [$v, $_] } @l; while ($self->_test(qr'\s*;')) { $self->_eat(';'); $self->__consume_ws_opt; if ($self->_VerbPath_test or $self->_VerbSimple_test) { if ($self->_VerbPath_test) { $self->_VerbPath; } else { $self->_VerbSimple; } my ($v) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_ObjectList; my @l = splice(@{ $self->{stack} }); push(@props, map { [$v, $_] } @l); } } $self->_add_stack( @props ); } # [34] PropertyListPath ::= PropertyListNotEmptyPath? sub _PropertyListPath { my $self = shift; if ($self->_Verb_test) { $self->_PropertyListNotEmptyPath; } } # [35] ObjectList ::= Object ( ',' Object )* sub _ObjectList { my $self = shift; my @list; $self->_Object; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Object; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; } $self->_add_stack( @list ); } # [36] Object ::= GraphNode sub _Object { my $self = shift; $self->_GraphNode; } # [37] Verb ::= VarOrIRIref | 'a' sub _Verb_test { my $self = shift; return $self->_test( qr/a[\n\t\r <]|[?\$]|<|${r_PNAME_LN}|${r_PNAME_NS}/ ); } sub _Verb { my $self = shift; if ($self->_test(qr/a[\n\t\r <]/)) { $self->_eat('a'); $self->__consume_ws; my $type = RDF::Query::Node::Resource->new( $rdf->type->uri_value ); $self->_add_stack( $type ); } else { $self->_VarOrIRIref; } } # VerbSimple ::= Var sub _VerbSimple_test { my $self = shift; return 1 if ($self->_test(qr/[\$?]/)); } sub _VerbSimple { my $self = shift; $self->_Var; } # VerbPath ::= Path sub _VerbPath_test { my $self = shift; return 1 if ($self->_IRIref_test); return 1 if ($self->_test(qr/\^|[|(a!]/)); return 0; } sub _VerbPath { my $self = shift; $self->_Path } # [74] Path ::= PathAlternative sub _Path { my $self = shift; # my $distinct = 1; # if ($self->_test(qr/DISTINCT[(]/i)) { # $self->_eat(qr/DISTINCT[(]/i); # $self->__consume_ws_opt; # $distinct = 1; # } $self->_PathAlternative; # if ($distinct) { # $self->__consume_ws_opt; # $self->_eat(qr/[)]/); # $self->__consume_ws_opt; # my ($path) = splice(@{ $self->{stack} }); # $self->_add_stack( ['PATH', 'DISTINCT', $path] ); # } } ################################################################################ # [75] PathAlternative ::= PathSequence ( '|' PathSequence )* sub _PathAlternative { my $self = shift; $self->_PathSequence; $self->__consume_ws_opt; while ($self->_test(qr/[|]/)) { my ($lhs) = splice(@{ $self->{stack} }); $self->_eat(qr/[|]/); $self->__consume_ws_opt; # $self->_PathOneInPropertyClass; $self->_PathSequence; $self->__consume_ws_opt; my ($rhs) = splice(@{ $self->{stack} }); $self->_add_stack( ['PATH', '|', $lhs, $rhs] ); } } # [76] PathSequence ::= PathEltOrInverse ( '/' PathEltOrInverse | '^' PathElt )* sub _PathSequence { my $self = shift; $self->_PathEltOrInverse; $self->__consume_ws_opt; while ($self->_test(qr<[/^]>)) { my $op; my ($lhs) = splice(@{ $self->{stack} }); if ($self->_test(qr)) { $op = $self->_eat(qr); $self->__consume_ws_opt; $self->_PathEltOrInverse; } else { $op = $self->_eat(qr<\^>); $self->__consume_ws_opt; $self->_PathElt; } my ($rhs) = splice(@{ $self->{stack} }); $self->_add_stack( ['PATH', $op, $lhs, $rhs] ); } } # [77] PathElt ::= PathPrimary PathMod? sub _PathElt { my $self = shift; $self->_PathPrimary; # $self->__consume_ws_opt; if ($self->_PathMod_test) { my @path = splice(@{ $self->{stack} }); $self->_PathMod; my ($mod) = splice(@{ $self->{stack} }); if (defined($mod)) { $self->_add_stack( ['PATH', $mod, @path] ); } else { # this might happen if we descend into _PathMod by mistaking a + as # a path modifier, but _PathMod figures out it's actually part of a # signed numeric object that follows the path $self->_add_stack( @path ); } } } # [78] PathEltOrInverse ::= PathElt | '^' PathElt sub _PathEltOrInverse { my $self = shift; if ($self->_test(qr/\^/)) { $self->_eat(qr<\^>); $self->__consume_ws_opt; $self->_PathElt; my @props = splice(@{ $self->{stack} }); $self->_add_stack( [ 'PATH', '^', @props ] ); } else { $self->_PathElt; } } # [79] PathMod ::= ( '*' | '?' | '+' | '{' ( Integer ( ',' ( '}' | Integer '}' ) | '}' ) ) ) sub _PathMod_test { my $self = shift; return 1 if ($self->_test(qr/[*?+{]/)); } sub _PathMod { my $self = shift; if ($self->_test(qr/[*?+]/)) { if ($self->_test(qr/[+][.0-9]/)) { return; } else { $self->_add_stack( $self->_eat(qr/[*?+]/) ); $self->__consume_ws_opt; } ### path repetition range syntax :path{n,m}; removed from 1.1 Query 2LC # } else { # $self->_eat(qr/{/); # $self->__consume_ws_opt; # my $value = 0; # if ($self->_test(qr/}/)) { # throw RDF::Query::Error::ParseError -text => "Syntax error: Empty Path Modifier"; # } # if ($self->_test($r_INTEGER)) { # $value = $self->_eat( $r_INTEGER ); # $self->__consume_ws_opt; # } # if ($self->_test(qr/,/)) { # $self->_eat(qr/,/); # $self->__consume_ws_opt; # if ($self->_test(qr/}/)) { # $self->_eat(qr/}/); # $self->_add_stack( "$value-" ); # } else { # my $end = $self->_eat( $r_INTEGER ); # $self->__consume_ws_opt; # $self->_eat(qr/}/); # $self->_add_stack( "$value-$end" ); # } # } else { # $self->_eat(qr/}/); # $self->_add_stack( "$value" ); # } } } # [80] PathPrimary ::= ( IRIref | 'a' | '!' PathNegatedPropertyClass | '(' Path ')' ) sub _PathPrimary { my $self = shift; if ($self->_IRIref_test) { $self->_IRIref; } elsif ($self->_test(qr/a[\n\t\r <]/)) { $self->_eat(qr/a/); my $type = RDF::Query::Node::Resource->new( $rdf->type->uri_value ); $self->_add_stack( $type ); } elsif ($self->_test(qr/[!]/)) { $self->_eat(qr/[!]/); $self->__consume_ws_opt; $self->_PathNegatedPropertyClass; my (@path) = splice(@{ $self->{stack} }); $self->_add_stack( ['PATH', '!', @path] ); } else { $self->_eat(qr/[(]/); $self->__consume_ws_opt; $self->_Path; $self->__consume_ws_opt; $self->_eat(qr/[)]/); } } # [81] PathNegatedPropertyClass ::= ( PathOneInPropertyClass | '(' ( PathOneInPropertyClass ( '|' PathOneInPropertyClass )* )? ')' ) sub _PathNegatedPropertyClass { my $self = shift; if ($self->_test(qr/[(]/)) { $self->_eat(qr/[(]/); $self->__consume_ws_opt; my @nodes; if ($self->_PathOneInPropertyClass_test) { $self->_PathOneInPropertyClass; push(@nodes, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->_test(qr/[|]/)) { $self->_eat(qr/[|]/); $self->__consume_ws_opt; $self->_PathOneInPropertyClass; $self->__consume_ws_opt; push(@nodes, splice(@{ $self->{stack} })); # $self->_add_stack( ['PATH', '|', $lhs, $rhs] ); } } $self->_eat(qr/[)]/); $self->_add_stack( @nodes ); } else { $self->_PathOneInPropertyClass; } } # [82] PathOneInPropertyClass ::= IRIref | 'a' sub _PathOneInPropertyClass_test { my $self = shift; return 1 if $self->_IRIref_test; return 1 if ($self->_test(qr/a[|)\n\t\r <]/)); return 1 if ($self->_test(qr/\^/)); return 0; } sub _PathOneInPropertyClass { my $self = shift; my $rev = 0; if ($self->_test(qr/\^/)) { $self->_eat(qr/\^/); $rev = 1; } if ($self->_test(qr/a[|)\n\t\r <]/)) { $self->_eat(qr/a/); my $type = RDF::Query::Node::Resource->new( $rdf->type->uri_value ); if ($rev) { $self->_add_stack( [ 'PATH', '^', $type ] ); } else { $self->_add_stack( $type ); } } else { $self->_IRIref; if ($rev) { my ($path) = splice(@{ $self->{stack} }); $self->_add_stack( [ 'PATH', '^', $path ] ); } } } ################################################################################ # [38] TriplesNode ::= Collection | BlankNodePropertyList sub _TriplesNode_test { my $self = shift; return $self->_test(qr/[[(](?![\n\r\t ]*\])(?![\n\r\t ]*\))/); } sub _TriplesNode { my $self = shift; if ($self->_test(qr/\(/)) { $self->_Collection; } else { $self->_BlankNodePropertyList; } } # [39] BlankNodePropertyList ::= '[' PropertyListNotEmpty ']' sub _BlankNodePropertyList { my $self = shift; if (my $where = $self->{__no_bnodes}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Blank nodes not allowed in $where"; } $self->_eat('['); $self->__consume_ws_opt; # $self->_PropertyListNotEmpty; $self->_PropertyListNotEmptyPath; $self->__consume_ws_opt; $self->_eat(']'); my @props = splice(@{ $self->{stack} }); my $subj = $self->new_blank; my @triples = map { $self->__new_statement( $subj, @$_ ) } @props; $self->_add_patterns( @triples ); $self->_add_stack( $subj ); } # [40] Collection ::= '(' GraphNode+ ')' sub _Collection { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; $self->_GraphNode; $self->__consume_ws_opt; my @nodes; push(@nodes, splice(@{ $self->{stack} })); while ($self->_GraphNode_test) { $self->_GraphNode; $self->__consume_ws_opt; push(@nodes, splice(@{ $self->{stack} })); } $self->_eat(')'); my $subj = $self->new_blank; my $cur = $subj; my $last; my $first = RDF::Query::Node::Resource->new( $rdf->first->uri_value ); my $rest = RDF::Query::Node::Resource->new( $rdf->rest->uri_value ); my $nil = RDF::Query::Node::Resource->new( $rdf->nil->uri_value ); my @triples; foreach my $node (@nodes) { push(@triples, $self->__new_statement( $cur, $first, $node ) ); my $new = $self->new_blank; push(@triples, $self->__new_statement( $cur, $rest, $new ) ); $last = $cur; $cur = $new; } pop(@triples); push(@triples, $self->__new_statement( $last, $rest, $nil )); $self->_add_patterns( @triples ); $self->_add_stack( $subj ); } # [41] GraphNode ::= VarOrTerm | TriplesNode sub _GraphNode_test { my $self = shift; # VarOrTerm | TriplesNode -> (Var | GraphTerm) | (Collection | BlankNodePropertyList) -> Var | IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | Collection | BlankNodePropertyList # but since a triple can't start with a literal, this is reduced to: # Var | IRIref | BlankNode | NIL return $self->_test(qr/[\$?]|<|['"]|(true\b|false\b)|([+-]?\d)|_:|${r_ANON}|${r_NIL}|\[|[[(]/); } sub _GraphNode { my $self = shift; if ($self->_TriplesNode_test) { $self->_TriplesNode; } else { $self->_VarOrTerm; } } # [42] VarOrTerm ::= Var | GraphTerm sub _VarOrTerm_test { my $self = shift; return 1 if ($self->_test(qr/[\$?]/)); return 1 if ($self->_IRIref_test); return 1 if ($self->_test(qr/[<'".0-9]|(true|false)\b|_:|\([\n\r\t ]*\)/)); return 0; } sub _VarOrTerm { my $self = shift; if ($self->{tokens} =~ m'^[?$]') { $self->_Var; } else { $self->_GraphTerm; } } # [43] VarOrIRIref ::= Var | IRIref sub _VarOrIRIref_test { my $self = shift; return $self->_test(qr/[\$?]|<|${r_PNAME_LN}|${r_PNAME_NS}/); } sub _VarOrIRIref { my $self = shift; if ($self->{tokens} =~ m'^[?$]') { $self->_Var; } else { $self->_IRIref; } } # [44] Var ::= VAR1 | VAR2 sub _Var { my $self = shift; if ($self->{__data_pattern}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Variable found where Term expected"; } my $var = ($self->_test( $r_VAR1 )) ? $self->_eat( $r_VAR1 ) : $self->_eat( $r_VAR2 ); $self->_add_stack( RDF::Query::Node::Variable->new( substr($var,1) ) ); } # [45] GraphTerm ::= IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL sub _GraphTerm { my $self = shift; if ($self->_test(qr/(true|false)\b/)) { $self->_BooleanLiteral; } elsif ($self->_test('(')) { $self->_NIL; } elsif ($self->_test( $r_ANON ) or $self->_test('_:')) { $self->_BlankNode; } elsif ($self->_test(qr/[-+]?\d/)) { $self->_NumericLiteral; } elsif ($self->_test(qr/['"]/)) { $self->_RDFLiteral; } else { $self->_IRIref; } } # [46] Expression ::= ConditionalOrExpression sub _Expression { my $self = shift; $self->_ConditionalOrExpression; } # [47] ConditionalOrExpression ::= ConditionalAndExpression ( '||' ConditionalAndExpression )* sub _ConditionalOrExpression { my $self = shift; my @list; $self->_ConditionalAndExpression; push(@list, splice(@{ $self->{stack} })); $self->__consume_ws_opt; while ($self->_test('||')) { $self->_eat('||'); $self->__consume_ws_opt; $self->_ConditionalAndExpression; push(@list, splice(@{ $self->{stack} })); } if (scalar(@list) > 1) { $self->_add_stack( $self->new_function_expression( 'sparql:logical-or', @list ) ); } else { $self->_add_stack( @list ); } Carp::confess $self->{tokens} if (scalar(@{ $self->{stack} }) == 0); } # [48] ConditionalAndExpression ::= ValueLogical ( '&&' ValueLogical )* sub _ConditionalAndExpression { my $self = shift; $self->_ValueLogical; my @list = splice(@{ $self->{stack} }); $self->__consume_ws_opt; while ($self->_test('&&')) { $self->_eat('&&'); $self->__consume_ws_opt; $self->_ValueLogical; push(@list, splice(@{ $self->{stack} })); } if (scalar(@list) > 1) { $self->_add_stack( $self->new_function_expression( 'sparql:logical-and', @list ) ); } else { $self->_add_stack( @list ); } } # [49] ValueLogical ::= RelationalExpression sub _ValueLogical { my $self = shift; $self->_RelationalExpression; } # [50] RelationalExpression ::= NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression )? sub _RelationalExpression { my $self = shift; $self->_NumericExpression; $self->__consume_ws_opt; if ($self->_test(qr/[!<>]?=|[<>]/)) { if ($self->_test( $r_IRI_REF )) { throw RDF::Query::Error::ParseError -text => "Syntax error: IRI found where expression expected"; } my @list = splice(@{ $self->{stack} }); my $op = $self->_eat(qr/[!<>]?=|[<>]/); $op = '==' if ($op eq '='); $self->__consume_ws_opt; $self->_NumericExpression; push(@list, splice(@{ $self->{stack} })); $self->_add_stack( $self->new_binary_expression( $op, @list ) ); } elsif ($self->_test(qr/(NOT )?IN/)) { my @list = splice(@{ $self->{stack} }); my $op = lc($self->_eat(qr/(NOT )?IN/)); $op =~ s/\s+//g; $self->__consume_ws_opt; $self->_ExpressionList(); push(@list, splice(@{ $self->{stack} })); $self->_add_stack( $self->new_function_expression( "sparql:$op", @list ) ); } } sub _ExpressionList { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; my @args; unless ($self->_test(')')) { $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); while ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; push( @args, splice(@{ $self->{stack} }) ); } } $self->_eat(')'); $self->_add_stack( @args ); } # [51] NumericExpression ::= AdditiveExpression sub _NumericExpression { my $self = shift; $self->_AdditiveExpression; } # [52] AdditiveExpression ::= MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | NumericLiteralPositive | NumericLiteralNegative )* sub _AdditiveExpression { my $self = shift; $self->_MultiplicativeExpression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; while ($self->_test(qr/[-+]/)) { my $op = $self->_eat(qr/[-+]/); $self->__consume_ws_opt; $self->_MultiplicativeExpression; my ($rhs) = splice(@{ $self->{stack} }); $expr = $self->new_binary_expression( $op, $expr, $rhs ); } $self->_add_stack( $expr ); } # [53] MultiplicativeExpression ::= UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )* sub _MultiplicativeExpression { my $self = shift; $self->_UnaryExpression; my ($expr) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; while ($self->_test(qr#[*/]#)) { my $op = $self->_eat(qr#[*/]#); $self->__consume_ws_opt; $self->_UnaryExpression; my ($rhs) = splice(@{ $self->{stack} }); $expr = $self->new_binary_expression( $op, $expr, $rhs ); } $self->_add_stack( $expr ); } # [54] UnaryExpression ::= '!' PrimaryExpression | '+' PrimaryExpression | '-' PrimaryExpression | PrimaryExpression sub _UnaryExpression { my $self = shift; if ($self->_test('!')) { $self->_eat('!'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); my $not = $self->new_unary_expression( '!', $expr ); $self->_add_stack( $not ); } elsif ($self->_test('+')) { $self->_eat('+'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); ### if it's just a literal, force the positive down into the literal if (blessed($expr) and $expr->isa('RDF::Trine::Node::Literal') and $expr->is_numeric_type) { my $value = '+' . $expr->literal_value; $expr->literal_value( $value ); $self->_add_stack( $expr ); } else { $self->_add_stack( $expr ); } } elsif ($self->_test('-')) { $self->_eat('-'); $self->__consume_ws_opt; $self->_PrimaryExpression; my ($expr) = splice(@{ $self->{stack} }); ### if it's just a literal, force the negative down into the literal instead of make an unnecessary multiplication. if (blessed($expr) and $expr->isa('RDF::Trine::Node::Literal') and $expr->is_numeric_type) { my $value = -1 * $expr->literal_value; $expr->literal_value( $value ); $self->_add_stack( $expr ); } else { my $int = $xsd->integer->uri_value; my $neg = $self->new_binary_expression( '*', $self->new_literal('-1', undef, $int), $expr ); $self->_add_stack( $neg ); } } else { $self->_PrimaryExpression; } } # [55] PrimaryExpression ::= BrackettedExpression | BuiltInCall | IRIrefOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var sub _PrimaryExpression { my $self = shift; if ($self->_BrackettedExpression_test) { $self->_BrackettedExpression; } elsif ($self->_BuiltInCall_test) { $self->_BuiltInCall; } elsif ($self->_IRIref_test) { $self->_IRIrefOrFunction; } elsif ($self->_test(qr/[\$?]/)) { $self->_Var; } elsif ($self->_test(qr/(true|false)\b/)) { $self->_BooleanLiteral; } elsif ($self->_test(qr/[-+]?\d/)) { $self->_NumericLiteral; } else { # if ($self->_test(qr/['"]/)) { $self->_RDFLiteral; } } # [56] BrackettedExpression ::= '(' Expression ')' sub _BrackettedExpression_test { my $self = shift; return $self->_test('('); } sub _BrackettedExpression { my $self = shift; $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; $self->__consume_ws_opt; $self->_eat(')'); } sub _Aggregate { my $self = shift; my $op = uc( $self->_eat( $r_AGGREGATE_CALL ) ); $self->_eat('('); $self->__consume_ws_opt; my $distinct = 0; if ($self->_test( qr/DISTINCT/i )) { $self->_eat( qr/DISTINCT\s*/i ); $self->__consume_ws_opt; $distinct = 1; } my (@expr, %options); if ($self->_test('*')) { @expr = $self->_eat('*'); } else { $self->_Expression; push(@expr, splice(@{ $self->{stack} })); if ($op eq 'GROUP_CONCAT') { $self->__consume_ws_opt; while ($self->_test(qr/,/)) { $self->_eat(qr/,/); $self->__consume_ws_opt; $self->_Expression; push(@expr, splice(@{ $self->{stack} })); } $self->__consume_ws_opt; if ($self->_test(qr/;/)) { $self->_eat(qr/;/); $self->__consume_ws_opt; if ($self->{args}{allow_typos}) { $self->_eat(qr/SEP[AE]RATOR/i); # accept common typo } else { $self->_eat(qr/SEPARATOR/i); } $self->__consume_ws_opt; $self->_eat(qr/=/); $self->__consume_ws_opt; $self->_String; my ($sep) = splice(@{ $self->{stack} }); $options{ seperator } = $sep; } } } $self->__consume_ws_opt; my $arg = join(',', map { blessed($_) ? $_->as_sparql : $_ } @expr); if ($distinct) { $arg = 'DISTINCT ' . $arg; } my $name = sprintf('%s(%s)', $op, $arg); $self->_eat(')'); $self->{build}{__aggregate}{ $name } = [ (($distinct) ? "${op}-DISTINCT" : $op), \%options, @expr ]; my @vars = grep { blessed($_) and $_->isa('RDF::Query::Node::Variable') } @expr; $self->_add_stack( RDF::Query::Node::Variable::ExpressionProxy->new($name, @vars) ); } # [57] BuiltInCall ::= 'STR' '(' Expression ')' | 'LANG' '(' Expression ')' | 'LANGMATCHES' '(' Expression ',' Expression ')' | 'DATATYPE' '(' Expression ')' | 'BOUND' '(' Var ')' | 'sameTerm' '(' Expression ',' Expression ')' | 'isIRI' '(' Expression ')' | 'isURI' '(' Expression ')' | 'isBLANK' '(' Expression ')' | 'isLITERAL' '(' Expression ')' | RegexExpression sub _BuiltInCall_test { my $self = shift; if ($self->{__aggregate_call_ok}) { return 1 if ($self->_test( $r_AGGREGATE_CALL )); } return 1 if $self->_test(qr/((NOT\s+)?EXISTS)|COALESCE/i); return 1 if $self->_test(qr/ABS|CEIL|FLOOR|ROUND|CONCAT|SUBSTR|STRLEN|UCASE|LCASE|ENCODE_FOR_URI|CONTAINS|STRSTARTS|STRENDS|RAND|MD5|SHA1|SHA224|SHA256|SHA384|SHA512|HOURS|MINUTES|SECONDS|DAY|MONTH|YEAR|TIMEZONE|TZ|NOW/i); return $self->_test(qr/UUID|STRUUID|STR|STRDT|STRLANG|STRBEFORE|STRAFTER|REPLACE|BNODE|IRI|URI|LANG|LANGMATCHES|DATATYPE|BOUND|sameTerm|isIRI|isURI|isBLANK|isLITERAL|REGEX|IF|isNumeric/i); } sub _BuiltInCall { my $self = shift; if ($self->{__aggregate_call_ok} and $self->_test( $r_AGGREGATE_CALL )) { $self->_Aggregate; } elsif ($self->_test(qr/(NOT\s+)?EXISTS/i)) { my $op = $self->_eat(qr/(NOT\s+)?EXISTS/i); $self->__consume_ws_opt; local($self->{filters}) = []; $self->_GroupGraphPattern; my $cont = $self->_remove_pattern; my $iri = RDF::Query::Node::Resource->new( 'sparql:exists' ); my $func = $self->new_function_expression($iri, $cont); if ($op =~ /^NOT/i) { $self->_add_stack( $self->new_unary_expression( '!', $func ) ); } else { $self->_add_stack( $func ); } } elsif ($self->_test(qr/COALESCE|BNODE|CONCAT|SUBSTR|RAND|NOW/i)) { # n-arg functions that take expressions my $op = $self->_eat(qr/COALESCE|BNODE|CONCAT|SUBSTR|RAND|NOW/i); my $iri = RDF::Query::Node::Resource->new( 'sparql:' . lc($op) ); $self->_ArgList; my @args = splice(@{ $self->{stack} }); my $func = $self->new_function_expression( $iri, @args ); $self->_add_stack( $func ); } elsif ($self->_RegexExpression_test) { $self->_RegexExpression; } else { my $op = $self->_eat( qr/\w+/ ); my $iri = RDF::Query::Node::Resource->new( 'sparql:' . lc($op) ); $self->__consume_ws_opt; $self->_eat('('); $self->__consume_ws_opt; if ($op =~ /^(STR)?UUID$/i) { # no-arg functions $self->_add_stack( $self->new_function_expression($iri) ); } elsif ($op =~ /^(STR|URI|IRI|LANG|DATATYPE|isIRI|isURI|isBLANK|isLITERAL|isNumeric|ABS|CEIL|FLOOR|ROUND|STRLEN|UCASE|LCASE|ENCODE_FOR_URI|MD5|SHA1|SHA224|SHA256|SHA384|SHA512|HOURS|MINUTES|SECONDS|DAY|MONTH|YEAR|TIMEZONE|TZ)$/i) { ### one-arg functions that take an expression $self->_Expression; my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $expr) ); } elsif ($op =~ /^(STRDT|STRLANG|LANGMATCHES|sameTerm|CONTAINS|STRSTARTS|STRENDS|STRBEFORE|STRAFTER)$/i) { ### two-arg functions that take expressions $self->_Expression; my ($arg1) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my ($arg2) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $arg1, $arg2) ); } elsif ($op =~ /^(IF|REPLACE)$/i) { ### three-arg functions that take expressions $self->_Expression; my ($arg1) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my ($arg2) = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my ($arg3) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $arg1, $arg2, $arg3) ); } else { ### BOUND(Var) $self->_Var; my ($expr) = splice(@{ $self->{stack} }); $self->_add_stack( $self->new_function_expression($iri, $expr) ); } $self->__consume_ws_opt; $self->_eat(')'); } } # [58] RegexExpression ::= 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')' sub _RegexExpression_test { my $self = shift; return $self->_test( qr/REGEX/i ); } sub _RegexExpression { my $self = shift; $self->_eat( qr/REGEX/i ); $self->__consume_ws_opt; $self->_eat('('); $self->__consume_ws_opt; $self->_Expression; my $string = splice(@{ $self->{stack} }); $self->__consume_ws_opt; $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; my $pattern = splice(@{ $self->{stack} }); my @args = ($string, $pattern); if ($self->_test(',')) { $self->_eat(','); $self->__consume_ws_opt; $self->_Expression; push(@args, splice(@{ $self->{stack} })); } $self->__consume_ws_opt; $self->_eat(')'); my $iri = RDF::Query::Node::Resource->new( 'sparql:regex' ); $self->_add_stack( $self->new_function_expression( $iri, @args ) ); } # [59] IRIrefOrFunction ::= IRIref ArgList? sub _IRIrefOrFunction_test { my $self = shift; $self->_IRIref_test; } sub _IRIrefOrFunction { my $self = shift; $self->_IRIref; if ($self->_ArgList_test) { my ($iri) = splice(@{ $self->{stack} }); $self->_ArgList; my @args = splice(@{ $self->{stack} }); my $func = $self->new_function_expression( $iri, @args ); $self->_add_stack( $func ); } } # [60] RDFLiteral ::= String ( LANGTAG | ( '^^' IRIref ) )? sub _RDFLiteral { my $self = shift; $self->_String; my @args = splice(@{ $self->{stack} }); if ($self->_test('@')) { my $lang = $self->_eat( $r_LANGTAG ); substr($lang,0,1) = ''; # remove '@' push(@args, lc($lang)); } elsif ($self->_test('^^')) { $self->_eat('^^'); push(@args, undef); $self->_IRIref; my ($iri) = splice(@{ $self->{stack} }); push(@args, $iri->uri_value); } my $obj = RDF::Query::Node::Literal->new( @args ); if ($self->{args}{canonicalize} and blessed($obj) and $obj->isa('RDF::Trine::Node::Literal')) { $obj = $obj->canonicalize; } $self->_add_stack( $obj ); } # [61] NumericLiteral ::= NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative # [62] NumericLiteralUnsigned ::= INTEGER | DECIMAL | DOUBLE # [63] NumericLiteralPositive ::= INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE # [64] NumericLiteralNegative ::= INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE sub _NumericLiteral { my $self = shift; my $sign = 0; if ($self->_test('+')) { $self->_eat('+'); $sign = '+'; } elsif ($self->_test('-')) { $self->_eat('-'); $sign = '-'; } my $value; my $type; if ($self->_test( $r_DOUBLE )) { $value = $self->_eat( $r_DOUBLE ); my $double = RDF::Query::Node::Resource->new( $xsd->double->uri_value ); $type = $double } elsif ($self->_test( $r_DECIMAL )) { $value = $self->_eat( $r_DECIMAL ); my $decimal = RDF::Query::Node::Resource->new( $xsd->decimal->uri_value ); $type = $decimal; } else { $value = $self->_eat( $r_INTEGER ); my $integer = RDF::Query::Node::Resource->new( $xsd->integer->uri_value ); $type = $integer; } if ($sign) { $value = $sign . $value; } my $obj = RDF::Query::Node::Literal->new( $value, undef, $type->uri_value ); if ($self->{args}{canonicalize} and blessed($obj) and $obj->isa('RDF::Trine::Node::Literal')) { $obj = $obj->canonicalize; } $self->_add_stack( $obj ); } # [65] BooleanLiteral ::= 'true' | 'false' sub _BooleanLiteral { my $self = shift; my $bool = $self->_eat(qr/(true|false)\b/); my $obj = RDF::Query::Node::Literal->new( $bool, undef, $xsd->boolean->uri_value ); if ($self->{args}{canonicalize} and blessed($obj) and $obj->isa('RDF::Trine::Node::Literal')) { $obj = $obj->canonicalize; } $self->_add_stack( $obj ); } # [66] String ::= STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2 sub _String { my $self = shift; my $value; if ($self->_test( $r_STRING_LITERAL_LONG1 )) { my $string = $self->_eat( $r_STRING_LITERAL_LONG1 ); $value = substr($string, 3, length($string) - 6); } elsif ($self->_test( $r_STRING_LITERAL_LONG2 )) { my $string = $self->_eat( $r_STRING_LITERAL_LONG2 ); $value = substr($string, 3, length($string) - 6); } elsif ($self->_test( $r_STRING_LITERAL1 )) { my $string = $self->_eat( $r_STRING_LITERAL1 ); $value = substr($string, 1, length($string) - 2); } else { # ($self->_test( $r_STRING_LITERAL2 )) { my $string = $self->_eat( $r_STRING_LITERAL2 ); $value = substr($string, 1, length($string) - 2); } # $value =~ s/(${r_ECHAR})/"$1"/ge; $value =~ s/\\t/\t/g; $value =~ s/\\b/\n/g; $value =~ s/\\n/\n/g; $value =~ s/\\r/\x08/g; $value =~ s/\\"/"/g; $value =~ s/\\'/'/g; $value =~ s/\\\\/\\/g; # backslash must come last, so it doesn't accidentally create a new escape $self->_add_stack( $value ); } # [67] IRIref ::= IRI_REF | PrefixedName sub _IRIref_test { my $self = shift; return $self->_test(qr/<|${r_PNAME_LN}|${r_PNAME_NS}/); } sub _IRIref { my $self = shift; if ($self->_test( $r_IRI_REF )) { my $iri = $self->_eat( $r_IRI_REF ); my $node = RDF::Query::Node::Resource->new( substr($iri,1,length($iri)-2), $self->__base ); $self->_add_stack( $node ); } else { $self->_PrefixedName; } } # [68] PrefixedName ::= PNAME_LN | PNAME_NS sub _PrefixedName { my $self = shift; if ($self->_test( $r_PNAME_LN )) { my $ln = $self->_eat( $r_PNAME_LN ); my ($ns,$local) = split(/:/, $ln, 2); if ($ns eq '') { $ns = '__DEFAULT__'; } $local =~ s{\\([-~.!&'()*+,;=:/?#@%_\$])}{$1}g; unless (exists $self->{namespaces}{$ns}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Use of undefined namespace '$ns'"; } my $iri = $self->{namespaces}{$ns} . $local; $self->_add_stack( RDF::Query::Node::Resource->new( $iri, $self->__base ) ); } else { my $ns = $self->_eat( $r_PNAME_NS ); if ($ns eq ':') { $ns = '__DEFAULT__'; } else { chop($ns); } unless (exists $self->{namespaces}{$ns}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Use of undefined namespace '$ns'"; } my $iri = $self->{namespaces}{$ns}; $self->_add_stack( RDF::Query::Node::Resource->new( $iri, $self->__base ) ); } } # [69] BlankNode ::= BLANK_NODE_LABEL | ANON sub _BlankNode { my $self = shift; if (my $where = $self->{__no_bnodes}) { throw RDF::Query::Error::ParseError -text => "Syntax error: Blank nodes not allowed in $where"; } if ($self->_test( $r_BLANK_NODE_LABEL )) { my $label = $self->_eat( $r_BLANK_NODE_LABEL ); my $id = substr($label,2); $self->_add_stack( $self->new_blank($id) ); } else { $self->_eat( $r_ANON ); $self->_add_stack( $self->new_blank ); } } sub _NIL { my $self = shift; $self->_eat( $r_NIL ); my $nil = RDF::Query::Node::Resource->new( $rdf->nil->uri_value ); $self->_add_stack( $nil ); } sub __solution_modifiers { my $self = shift; my $star = shift; my $having_expr; my $aggdata = delete( $self->{build}{__aggregate} ); my @aggkeys = keys %{ $aggdata || {} }; if (scalar(@aggkeys)) { my $groupby = delete( $self->{build}{__group_by} ) || []; my $pattern = $self->{build}{triples}; my $ggp = shift(@$pattern); if (my $having = delete( $self->{build}{__having} )) { $having_expr = $having; } my $agg = RDF::Query::Algebra::Aggregate->new( $ggp, $groupby, { expressions => [%$aggdata] } ); push(@{ $self->{build}{triples} }, $agg); } my $vars = [ @{ $self->{build}{variables} } ]; { my @vars = grep { $_->isa('RDF::Query::Expression::Alias') } @$vars; if (scalar(@vars)) { my $pattern = pop(@{ $self->{build}{triples} }); my @bound = $pattern->potentially_bound; my %bound = map { $_ => 1 } @bound; foreach my $v (@vars) { my $name = $v->name; if ($bound{ $name }) { throw RDF::Query::Error::ParseError -text => "Syntax error: Already-bound variable ($name) used in project expression"; } } my $proj = RDF::Query::Algebra::Extend->new( $pattern, $vars ); push(@{ $self->{build}{triples} }, $proj); } } if ($having_expr) { my $pattern = pop(@{ $self->{build}{triples} }); my $filter = RDF::Query::Algebra::Filter->new( $having_expr, $pattern ); push(@{ $self->{build}{triples} }, $filter); } if ($self->{build}{options}{orderby}) { my $order = delete $self->{build}{options}{orderby}; my $pattern = pop(@{ $self->{build}{triples} }); my $sort = RDF::Query::Algebra::Sort->new( $pattern, @$order ); push(@{ $self->{build}{triples} }, $sort); } { my $pattern = pop(@{ $self->{build}{triples} }); my $proj = RDF::Query::Algebra::Project->new( $pattern, $vars ); push(@{ $self->{build}{triples} }, $proj); } if ($self->{build}{options}{distinct}) { delete $self->{build}{options}{distinct}; my $pattern = pop(@{ $self->{build}{triples} }); my $sort = RDF::Query::Algebra::Distinct->new( $pattern ); push(@{ $self->{build}{triples} }, $sort); } if (exists $self->{build}{options}{offset}) { my $offset = delete $self->{build}{options}{offset}; my $pattern = pop(@{ $self->{build}{triples} }); my $offseted = RDF::Query::Algebra::Offset->new( $pattern, $offset ); push(@{ $self->{build}{triples} }, $offseted); } if (exists $self->{build}{options}{limit}) { my $limit = delete $self->{build}{options}{limit}; my $pattern = pop(@{ $self->{build}{triples} }); my $limited = RDF::Query::Algebra::Limit->new( $pattern, $limit ); push(@{ $self->{build}{triples} }, $limited); } } ################################################################################ =item C<< error >> Returns the error encountered during the last parse. =cut sub error { my $self = shift; return $self->{error}; } sub _add_patterns { my $self = shift; my @triples = @_; my $container = $self->{ pattern_container_stack }[0]; push( @{ $container }, @triples ); } sub _remove_pattern { my $self = shift; my $container = $self->{ pattern_container_stack }[0]; my $pattern = pop( @{ $container } ); return $pattern; } sub _peek_pattern { my $self = shift; my $container = $self->{ pattern_container_stack }[0]; my $pattern = $container->[-1]; return $pattern; } sub _push_pattern_container { my $self = shift; my $cont = []; unshift( @{ $self->{ pattern_container_stack } }, $cont ); return $cont; } sub _pop_pattern_container { my $self = shift; my $cont = shift( @{ $self->{ pattern_container_stack } } ); return $cont; } sub _add_stack { my $self = shift; my @items = @_; push( @{ $self->{stack} }, @items ); } sub _add_filter { my $self = shift; my @filters = shift; push( @{ $self->{filters} }, @filters ); } sub _eat { my $self = shift; my $thing = shift; if (not(length($self->{tokens}))) { $self->_syntax_error("No tokens left"); } # if (substr($self->{tokens}, 0, 1) eq '^') { # Carp::cluck( "eating $thing with input $self->{tokens}" ); # } if (ref($thing) and $thing->isa('Regexp')) { if ($self->{tokens} =~ /^$thing/) { my $match = $&; substr($self->{tokens}, 0, length($match)) = ''; return $match; } $self->_syntax_error( "Expected $thing" ); } elsif (looks_like_number( $thing )) { my ($token) = substr( $self->{tokens}, 0, $thing, '' ); return $token } else { ### thing is a string if (substr($self->{tokens}, 0, length($thing)) eq $thing) { substr($self->{tokens}, 0, length($thing)) = ''; return $thing; } else { $self->_syntax_error( "Expected $thing" ); } } print $thing; throw RDF::Query::Error; } sub _syntax_error { my $self = shift; my $thing = shift; my $expect = $thing; my $level = 2; while (my $sub = (caller($level++))[3]) { if ($sub =~ m/::_([A-Z]\w*)$/) { $expect = $1; last; } } my $l = Log::Log4perl->get_logger("rdf.query.parser.sparql"); if ($l->is_debug) { $l->logcluck("Syntax error eating $thing with input <<$self->{tokens}>>"); } my $near = "'" . substr($self->{tokens}, 0, 20) . "...'"; $near =~ s/[\r\n ]+/ /g; if ($thing) { # Carp::cluck Dumper($self->{tokens}); # XXX throw RDF::Query::Error::ParseError -text => "Syntax error: $thing in $expect near $near"; } else { throw RDF::Query::Error::ParseError -text => "Syntax error: Expected $expect near $near"; } } sub _test { my $self = shift; my $thing = shift; if (blessed($thing) and $thing->isa('Regexp')) { if ($self->{tokens} =~ m/^$thing/) { return 1; } else { return 0; } } else { if (substr($self->{tokens}, 0, length($thing)) eq $thing) { return 1; } else { return 0; } } } sub _ws_test { my $self = shift; unless (length($self->{tokens})) { return 0; } if ($self->{tokens} =~ m/^[\t\r\n #]/) { return 1; } else { return 0; } } sub _ws { my $self = shift; ### #x9 | #xA | #xD | #x20 | comment if ($self->_test('#')) { $self->_eat(qr/#[^\x0d\x0a]*.?/); } else { $self->_eat(qr/[\n\r\t ]/); } } sub __consume_ws_opt { my $self = shift; if ($self->_ws_test) { $self->__consume_ws; } } sub __consume_ws { my $self = shift; $self->_ws; while ($self->_ws_test()) { $self->_ws() } } sub __base { my $self = shift; my $build = $self->{build}; if (defined($build->{base})) { return $build->{base}; } else { return; } } sub __new_statement { my $self = shift; my @nodes = @_; if ($self->{_modify_template} and my $graph = $self->{named_graph} and $self->{named_graph}->isa('RDF::Trine::Node::Resource')) { return RDF::Query::Algebra::Quad->new( @nodes, $graph ); } else { return RDF::Query::Algebra::Triple->_new( @nodes ); } } sub __new_path { my $self = shift; my $start = shift; my $pdata = shift; my $end = shift; (undef, my $op, my @nodes) = @$pdata; @nodes = map { $self->__strip_path( $_ ) } @nodes; # if (my $graph = $self->{named_graph} and $self->{named_graph}->isa('RDF::Trine::Node::Resource')) { # return RDF::Query::Algebra::Path->new( $start, [$op, @nodes], $end, $graph ); # } else { return RDF::Query::Algebra::Path->new( $start, [$op, @nodes], $end ); # } } sub __strip_path { my $self = shift; my $path = shift; if (blessed($path)) { return $path; } elsif (reftype($path) eq 'ARRAY' and $path->[0] eq 'PATH') { (undef, my $op, my @nodes) = @$path; return [$op, map { $self->__strip_path($_) } @nodes]; } else { return $path; } } sub __new_bgp { # fix up BGPs that might actually have property paths in them. split those # out as their own path algebra objects, and join them with the bgp with a # ggp if necessary my $self = shift; my @patterns = @_; my @paths = grep { reftype($_->predicate) eq 'ARRAY' and $_->predicate->[0] eq 'PATH' } @patterns; my @triples = grep { blessed($_->predicate) } @patterns; if (scalar(@patterns) > scalar(@paths) + scalar(@triples)) { Carp::cluck "more than just triples and paths passed to __new_bgp: " . Dumper(\@patterns); } my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @triples ); if (@paths) { my @p; foreach my $p (@paths) { my $start = $p->subject; my $end = $p->object; my $pdata = $p->predicate; push(@p, $self->__new_path( $start, $pdata, $end )); } my $pgroup = (scalar(@p) == 1) ? $p[0] : RDF::Query::Algebra::GroupGraphPattern->new( @p ); if (scalar(@triples)) { return RDF::Query::Algebra::GroupGraphPattern->new( $bgp, $pgroup ); } else { return $pgroup; } } else { return $bgp; } } 1; __END__ =back =cut RDF-Query-2.910/lib/RDF/Query/Node/Blank.pm000644 000765 000024 00000005245 12173312155 020100 0ustar00gregstaff000000 000000 # RDF::Query::Node::Blank # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Node::Blank - RDF Node class for blank nodes =head1 VERSION This document describes RDF::Query::Node::Blank version 2.910. =head1 METHODS Beyond the methods documented below, this class inherits methods from the L and L classes. =over 4 =cut package RDF::Query::Node::Blank; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Node RDF::Trine::Node::Blank); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### use overload '<=>' => \&_cmp, 'cmp' => \&_cmp, '<' => sub { _cmp(@_[0,1]) == -1 }, '>' => sub { _cmp(@_[0,1]) == 1 }, '!=' => sub { _cmp(@_[0,1]) != 0 }, '==' => sub { _cmp(@_[0,1]) == 0 }, '+' => sub { $_[0] }, '""' => sub { $_[0]->sse }, ; sub _cmp { my $nodea = shift; my $nodeb = shift; my $l = Log::Log4perl->get_logger("rdf.query.node.blank"); $l->debug("blank comparison: " . Dumper($nodea, $nodeb)); return 1 unless blessed($nodeb); return -1 if ($nodeb->isa('RDF::Query::Node::Literal')); return -1 if ($nodeb->isa('RDF::Query::Node::Resource')); return 1 unless ($nodeb->isa('RDF::Query::Node::Blank')); my $cmp = $nodea->blank_identifier cmp $nodeb->blank_identifier; $l->debug("-> $cmp"); return $cmp; } =item C<< new ( [ $name ] ) >> Returns a new Blank node object. If C<< $name >> is supplied, it will be used as the blank node identifier. Otherwise a time-based identifier will be generated and used. =cut sub new { my $class = shift; my $name = shift; unless (defined($name)) { $name = 'r' . time() . 'r' . $RDF::Trine::Node::Blank::COUNTER++; } return $class->_new( $name ); } =item C<< as_sparql >> Returns the SPARQL string for this node. =cut sub as_sparql { my $self = shift; return $self->sse; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => 'node', blank => $self->blank_identifier, }; } =item C<< make_distinguished_variable >> Returns a new variable based on this blank node. =cut sub make_distinguished_variable { my $self = shift; my $id = $self->blank_identifier; my $name = '__ndv_' . $id; my $var = RDF::Query::Node::Variable->new( $name ); return $var; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Node/Literal.pm000644 000765 000024 00000021041 12173312155 020435 0ustar00gregstaff000000 000000 # RDF::Query::Node::Literal # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Node::Literal - RDF Node class for literals =head1 VERSION This document describes RDF::Query::Node::Literal version 2.910. =cut package RDF::Query::Node::Literal; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Node RDF::Trine::Node::Literal); use DateTime; use DateTime::Format::W3CDTF; use RDF::Query::Error; use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(blessed refaddr looks_like_number); use Carp qw(carp croak confess); ###################################################################### our ($VERSION, $LAZY_COMPARISONS); BEGIN { $VERSION = '2.910'; } ###################################################################### use overload '<=>' => \&_cmp, 'cmp' => \&_cmp, '<' => sub { _cmp(@_[0,1], '<') == -1 }, '>' => sub { _cmp(@_[0,1], '>') == 1 }, '!=' => sub { _cmp(@_[0,1], '!=') != 0 }, '==' => sub { _cmp(@_[0,1], '==') == 0 }, '+' => sub { $_[0] }, '""' => sub { $_[0]->sse }, ; my %INSIDE_OUT_DATES; =head1 METHODS Beyond the methods documented below, this class inherits methods from the L and L classes. =over 4 =cut sub _cmp { my $nodea = shift; my $nodeb = shift; my $op = shift; my $l = Log::Log4perl->get_logger("rdf.query.node.literal"); $l->debug('literal comparison: ' . Dumper($nodea, $nodeb)); return 1 unless blessed($nodeb); return 1 if ($nodeb->isa('RDF::Query::Node::Blank')); return 1 if ($nodeb->isa('RDF::Query::Node::Resource')); return 1 unless ($nodeb->isa('RDF::Query::Node::Literal')); my $dta = $nodea->literal_datatype || ''; my $dtb = $nodeb->literal_datatype || ''; my $datetype = '^http://www.w3.org/2001/XMLSchema#dateTime'; my $datecmp = ($dta =~ $datetype and $dtb =~ $datetype); my $numericcmp = ($nodea->is_numeric_type and $nodeb->is_numeric_type); if ($datecmp) { $l->trace('datecmp'); my $datea = $nodea->datetime; my $dateb = $nodeb->datetime; if ($datea and $dateb) { my $cmp = eval { DateTime->compare_ignore_floating( $datea, $dateb ) }; return $cmp unless ($@); } } if ($numericcmp) { $l->trace('both numeric cmp'); return 0 if ($nodea->equal( $nodeb )); # if the nodes are identical, return true (even if the lexical values don't appear to be numeric). i.e., "xyz"^^xsd:integer should equal itself, even though it's not a valid integer. return $nodea->numeric_value <=> $nodeb->numeric_value; } { $l->trace('other cmp'); if ($nodea->has_language and $nodeb->has_language) { $l->trace('both have language'); my $lc = lc($nodea->literal_value_language) cmp lc($nodeb->literal_value_language); my $vc = $nodea->literal_value cmp $nodeb->literal_value; my $c; if ($LAZY_COMPARISONS and ($lc != 0)) { $c = ($vc || $lc); } elsif ($lc == 0) { $c = $vc; } else { $l->debug("Attempt to compare literals with differing languages."); throw RDF::Query::Error::TypeError -text => "Attempt to compare literals with differing languages."; } $l->trace("-> $c"); return $c; } elsif (($nodea->has_datatype and $dta eq 'http://www.w3.org/2001/XMLSchema#string') or ($nodeb->has_datatype and $dtb eq 'http://www.w3.org/2001/XMLSchema#string')) { $l->trace("one is xsd:string"); no warnings 'uninitialized'; my ($na, $nb) = sort { (blessed($b) and $b->isa('RDF::Query::Node::Literal')) ? $b->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string' : ($LAZY_COMPARISONS) ? refaddr($a) <=> refaddr($b) : throw RDF::Query::Error::TypeError -text => "Attempt to compare xsd:string with non-literal"; } ($nodea, $nodeb); my $c; if ($nb->has_language) { $c = -1; } elsif (not($nb->has_datatype) or $nb->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string') { $c = $nodea->literal_value cmp $nodeb->literal_value; } elsif ($LAZY_COMPARISONS) { return $nodea->as_string cmp $nodeb->as_string; } else { throw RDF::Query::Error::TypeError -text => "Attempt to compare typed-literal with xsd:string."; } $l->trace("-> $c"); return $c; } elsif ($nodea->has_datatype and $nodeb->has_datatype) { $l->trace("both have datatype"); my $dc = $nodea->literal_datatype cmp $nodeb->literal_datatype; my $vc = $nodea->literal_value cmp $nodeb->literal_value; my $c; if ($op eq '!=') { throw RDF::Query::Error::TypeError -text => "Attempt to compare (neq) literals with unrecognized datatypes."; } else { if ($LAZY_COMPARISONS) { $c = ($vc || $dc); } elsif ($dc == 0) { $c = $vc; } else { $l->debug("Attempt to compare literals with different datatypes."); throw RDF::Query::Error::TypeError -text => "Attempt to compare literals with differing datatypes."; } $l->trace("-> $c"); return $c; } } elsif ($nodea->has_language or $nodeb->has_language) { $l->trace("one has language"); my $c = ($nodea->has_language) ? 1 : -1; $l->trace("-> $c"); return $c; } elsif ($nodea->has_datatype or $nodeb->has_datatype) { $l->trace("one has datatype"); if ($LAZY_COMPARISONS) { my $c = ($nodea->has_datatype) ? 1 : -1; $l->trace("-> $c"); return $c; } else { $l->debug("Attempt to compare typed-literal with plain-literal"); throw RDF::Query::Error::TypeError -text => "Attempt to compare typed-literal with plain-literal"; } } else { $l->trace("something else"); my $vcmp = $nodea->literal_value cmp $nodeb->literal_value; $l->trace("-> $vcmp"); return $vcmp; } } } =item C<< datetime >> Returns a DateTime object from the literal if the literal value is in W3CDTF format. =cut sub datetime { my $self = shift; my $addr = refaddr( $self ); if (exists($INSIDE_OUT_DATES{ $addr })) { return $INSIDE_OUT_DATES{ $addr }; } else { my $value = $self->literal_value; my $f = DateTime::Format::W3CDTF->new; my $dt = eval { $f->parse_datetime( $value ) }; $INSIDE_OUT_DATES{ $addr } = $dt; return $dt; } } =item C<< as_sparql >> Returns the SPARQL string for this node. =cut sub as_sparql { my $self = shift; if ($self->is_numeric_type) { return $self->literal_value; } else { return $self->sse; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; my $hash = { type => 'node', literal => $self->literal_value, }; $hash->{ language } = $self->literal_value_language if ($self->has_language); $hash->{ datatype } = $self->literal_datatype if ($self->has_datatype); return $hash; } =item C<< is_simple_literal >> Returns true if the literal is "simple" -- is a literal without datatype or language. =cut sub is_simple_literal { my $self = shift; return not($self->has_language or $self->has_datatype); } =item C<< is_numeric_type >> Returns true if the literal is a known (xsd) numeric type. =cut sub is_numeric_type { my $self = shift; return 0 unless ($self->has_datatype); my $type = $self->literal_datatype; if ($type =~ qr<^http://www.w3.org/2001/XMLSchema#(integer|decimal|float|double|non(Positive|Negative)Integer|(positive|negative)Integer|long|int|short|byte|unsigned(Long|Int|Short|Byte))>) { return 1; } else { return 0; } } =item C<< numeric_value >> Returns the numeric value of the literal (even if the literal isn't a known numeric type. =cut sub numeric_value { my $self = shift; if ($self->is_numeric_type) { my $value = $self->literal_value; if (looks_like_number($value)) { my $v = 0 + eval "$value"; return $v; } else { throw RDF::Query::Error::TypeError -text => "Literal with numeric type does not appear to have numeric value."; } } elsif (not $self->has_datatype) { if (looks_like_number($self->literal_value)) { return 0+$self->literal_value; } else { return; } } elsif ($self->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#boolean') { return ($self->literal_value eq 'true') ? 1 : 0; } else { return; } } =item C<< type_list >> Returns a two-item list suitable for use as the second and third arguments to RDF::Query::Node::Literal constructor. The two returned values correspond to literal language tag and literal datatype URI, respectively. =cut sub type_list { my $self = shift; return ($self->literal_value_language, $self->literal_datatype); } sub DESTROY { my $self = shift; my $addr = refaddr($self); delete $INSIDE_OUT_DATES{ $addr }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Node/Resource.pm000644 000765 000024 00000004607 12173312155 020641 0ustar00gregstaff000000 000000 # RDF::Query::Node::Resource # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Node::Resource - RDF Node class for resources =head1 VERSION This document describes RDF::Query::Node::Resource version 2.910. =cut package RDF::Query::Node::Resource; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Node RDF::Trine::Node::Resource); use URI; use Encode; use Data::Dumper; use Scalar::Util qw(blessed reftype); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L and L classes. =over 4 =cut use overload '<=>' => \&_cmp, 'cmp' => \&_cmp, '<' => sub { _cmp(@_) == -1 }, '>' => sub { _cmp(@_) == 1 }, '!=' => sub { _cmp(@_) != 0 }, '==' => sub { _cmp(@_) == 0 }, '+' => sub { $_[0] }, '""' => sub { $_[0]->sse }, ; sub _cmp { my $a = shift; my $b = shift; return 1 unless blessed($b); return -1 if ($b->isa('RDF::Query::Node::Literal')); return 1 if ($b->isa('RDF::Query::Node::Blank')); return 0 unless ($b->isa('RDF::Query::Node::Resource')); my $cmp = $a->uri_value cmp $b->uri_value; return $cmp; } =item C<< as_sparql >> Returns the SPARQL string for this node. =cut sub as_sparql { my $self = shift; my $context = shift || {}; if ($context) { my $uri = $self->uri_value; my $ns = $context->{namespaces} || {}; my %ns = %$ns; foreach my $k (keys %ns) { no warnings 'uninitialized'; if ($k eq '__DEFAULT__') { $k = ''; } my $v = $ns{ $k }; if (index($uri, $v) == 0) { my $local = substr($uri, length($v)); if ($local =~ /^[A-Za-z_]+$/) { my $qname = join(':', $k, $local); return $qname; } } } } my $string = URI->new( encode_utf8($self->uri_value) )->canonical; my $sparql = '<' . $string . '>'; return $sparql; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => 'node', iri => $self->uri_value, }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Node/Variable.pm000644 000765 000024 00000004672 12173312155 020601 0ustar00gregstaff000000 000000 # RDF::Query::Node::Variable # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Node::Variable - RDF Node class for variables =head1 VERSION This document describes RDF::Query::Node::Variable version 2.910. =cut package RDF::Query::Node::Variable; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Node RDF::Trine::Node::Variable); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS =head1 METHODS Beyond the methods documented below, this class inherits methods from the L and L classes. =over 4 =cut use overload '""' => sub { $_[0]->sse }; =item C<< new ( $name ) >> Returns a new variable object. =cut my $COUNTER = 0; sub new { my $class = shift; my $name = shift; unless (defined($name)) { $name = 'v' . time() . 'r' . $COUNTER++; } return $class->SUPER::new( $name ); } =item C<< as_sparql >> Returns the SPARQL string for this node. =cut sub as_sparql { my $self = shift; return $self->sse; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => 'node', variable => $self->name, }; } package RDF::Query::Node::Variable::ExpressionProxy; use strict; use warnings; use base qw(RDF::Query::Node::Variable); use Scalar::Util qw(blessed refaddr); =begin private =item C<< new >> =cut {my %vars; sub new { my $class = shift; my $name = shift; my @vars = @_; my $self = $class->SUPER::new( $name ); if (@vars) { $vars{ refaddr($self) } = [map {blessed($_) ? $_ : RDF::Query::Node::Variable->new($_)} @vars]; } return $self; } =item C<< as_sparql >> =cut sub as_sparql { my $self = shift; return $self->name; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; my @vars = map { blessed($_) ? $_->name : $_ } @{ $vars{ refaddr($self) } || [] }; return RDF::Query::_uniq(@vars); } sub nonaggregated_referenced_variables { return; } sub DESTROY { my $self = shift; delete $vars{ refaddr($self) }; } } =end private =cut 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Functions/Geo.pm000644 000765 000024 00000003571 12173312155 020646 0ustar00gregstaff000000 000000 =head1 NAME RDF::Query::Functions::Geo - Geographic extension functions =head1 VERSION This document describes RDF::Query::Functions::Geo version 2.910. =head1 DESCRIPTION Defines the following function: =over =item * java:com.ldodds.sparql.Distance =back =cut package RDF::Query::Functions::Geo; use strict; use warnings; use Scalar::Util qw(blessed reftype refaddr looks_like_number); use Log::Log4perl; our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions.geo"); $VERSION = '2.910'; } our $GEO_DISTANCE_LOADED; BEGIN { $GEO_DISTANCE_LOADED = do { eval { require Geo::Distance; }; ($@) ? 0 : 1; }; } =begin private =item C<< install >> Documented in L. =end private =cut sub install { RDF::Query::Functions->install_function( "java:com.ldodds.sparql.Distance", sub { # http://xmlarmyknife.com/blog/archives/000281.html my $query = shift; my ($lat1, $lon1, $lat2, $lon2); unless ($GEO_DISTANCE_LOADED) { throw RDF::Query::Error::FilterEvaluationError ( -text => "Cannot compute distance because Geo::Distance is not available" ); } my $geo = ref($query) ? ($query->{_query_cache}{'java:com.ldodds.sparql.Distance'}{_geo_dist_obj} ||= new Geo::Distance) : new Geo::Distance; if (2 == @_) { my ($point1, $point2) = map { $_->literal_value } splice(@_,0,2); ($lat1, $lon1) = split(/ /, $point1); ($lat2, $lon2) = split(/ /, $point2); } else { ($lat1, $lon1, $lat2, $lon2) = map { $_->literal_value } splice(@_,0,4); } my $dist = $geo->distance( 'kilometer', $lon1, $lat1, $lon2, $lat2, ); # warn "ldodds:Distance => $dist\n"; return RDF::Query::Node::Literal->new("$dist", undef, 'http://www.w3.org/2001/XMLSchema#float'); } ); } 1; __END__ =head1 AUTHOR Gregory Williams . =cut RDF-Query-2.910/lib/RDF/Query/Functions/Jena.pm000644 000765 000024 00000010036 12173312155 021003 0ustar00gregstaff000000 000000 =head1 NAME RDF::Query::Functions::Jena - Jena/ARQ work-alike functions =head1 VERSION This document describes RDF::Query::Functions::Jena version 2.910. =head1 DESCRIPTION Defines the following functions: =over =item * java:com.hp.hpl.jena.query.function.library.langeq =item * java:com.hp.hpl.jena.query.function.library.listMember =item * java:com.hp.hpl.jena.query.function.library.now =item * java:com.hp.hpl.jena.query.function.library.sha1sum =back =cut package RDF::Query::Functions::Jena; use strict; use warnings; use Log::Log4perl; our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions.jena"); $VERSION = '2.910'; } use Digest::SHA qw(sha1_hex); use I18N::LangTags; use Scalar::Util qw(blessed reftype refaddr looks_like_number); =begin private =item C<< install >> Documented in L. =end private =cut sub install { RDF::Query::Functions->install_function( ["http://jena.hpl.hp.com/ARQ/function#sha1sum", "java:com.hp.hpl.jena.query.function.library.sha1sum"], sub { my $query = shift; my $node = shift; my $value; if ($node->isa('RDF::Query::Node::Literal')) { $value = $node->literal_value; } elsif ($node->isa('RDF::Query::Node::Resource')) { $value = $node->uri_value; } else { throw RDF::Query::Error::TypeError -text => "jena:sha1sum called without a literal or resource"; } my $hash = sha1_hex( $value ); return RDF::Query::Node::Literal->new( $hash ); } ); RDF::Query::Functions->install_function( ["http://jena.hpl.hp.com/ARQ/function#now", "java:com.hp.hpl.jena.query.function.library.now"], sub { my $query = shift; my $dt = DateTime->now(); my $f = ref($query) ? $query->dateparser : DateTime::Format::W3CDTF->new; my $value = $f->format_datetime( $dt ); return RDF::Query::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); } ); RDF::Query::Functions->install_function( ["http://jena.hpl.hp.com/ARQ/function#langeq", "java:com.hp.hpl.jena.query.function.library.langeq"], sub { my $query = shift; my $node = shift; my $lang = shift; my $litlang = $node->literal_value_language; my $match = $lang->literal_value; return I18N::LangTags::is_dialect_of( $litlang, $match ) ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); RDF::Query::Functions->install_function( ["http://jena.hpl.hp.com/ARQ/function#listMember", "java:com.hp.hpl.jena.query.function.library.listMember"], sub { my $query = shift; my $list = shift; my $value = shift; my $first = RDF::Query::Node::Resource->new( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first' ); my $rest = RDF::Query::Node::Resource->new( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest' ); my $result; LIST: while ($list) { if ($list->is_resource and $list->uri_value eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil') { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { my $stream = $query->model->get_statements( $list, $first, undef ); while (my $stmt = $stream->next()) { my $member = $stmt->object; return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') if ($value->equal( $member )); } my $stmt = $query->model->get_statements( $list, $rest, undef )->next(); return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean') unless ($stmt); my $tail = $stmt->object; if ($tail) { $list = $tail; next; #next LIST; } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } } return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); } 1; __END__ =head1 AUTHOR Gregory Williams . =cut RDF-Query-2.910/lib/RDF/Query/Functions/Kasei.pm000644 000765 000024 00000002342 12173312155 021163 0ustar00gregstaff000000 000000 =head1 NAME RDF::Query::Functions::Kasei - RDF-Query-specific functions =head1 VERSION This document describes RDF::Query::Functions::Kasei version 2.910. =head1 DESCRIPTION Defines the following functions: =over =item * http://kasei.us/2007/09/functions/warn =item * http://kasei.us/code/rdf-query/functions/bloom =item * http://kasei.us/code/rdf-query/functions/bloom/filter =back =cut package RDF::Query::Functions::Kasei; use strict; use warnings; use Log::Log4perl; our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions.kasei"); $VERSION = '2.910'; } use Data::Dumper; use Scalar::Util qw(blessed reftype refaddr looks_like_number); =begin private =item C<< install >> Documented in L. =end private =cut sub install { RDF::Query::Functions->install_function( "http://kasei.us/2007/09/functions/warn", sub { my $query = shift; my $value = shift; my $func = RDF::Query::Expression::Function->new( 'sparql:str', $value ); my $string = Dumper( $func->evaluate( undef, undef, {} ) ); no warnings 'uninitialized'; warn "FILTER VALUE: $string\n"; return $value; } ); } 1; __END__ =head1 AUTHOR Gregory Williams . =cut RDF-Query-2.910/lib/RDF/Query/Functions/SPARQL.pm000644 000765 000024 00000141546 12173312155 021143 0ustar00gregstaff000000 000000 =head1 NAME RDF::Query::Functions::SPARQL - SPARQL built-in functions =head1 VERSION This document describes RDF::Query::Functions::SPARQL version 2.910. =head1 DESCRIPTION Defines the following functions: =over 4 =item * sparql:abs =item * sparql:bnode =item * sparql:bound =item * sparql:ceil =item * sparql:coalesce =item * sparql:concat =item * sparql:contains =item * sparql:datatype =item * sparql:ebv =item * sparql:strends =item * sparql:floor =item * sparql:encode_for_uri =item * sparql:exists =item * sparql:in =item * sparql:iri =item * sparql:isblank =item * sparql:isiri =item * sparql:isliteral =item * sparql:isuri =item * sparql:isNumeric =item * sparql:lang =item * sparql:langmatches =item * sparql:lcase =item * sparql:logical-and =item * sparql:logical-or =item * sparql:notin =item * sparql:rand =item * sparql:regex =item * sparql:round =item * sparql:sameterm =item * sparql:strstarts =item * sparql:str =item * sparql:strdt =item * sparql:strlang =item * sparql:strlen =item * sparql:substr =item * sparql:ucase =item * sparql:uri =item * sparql:uuid =item * sparql:struuid =cut package RDF::Query::Functions::SPARQL; use strict; use warnings; use Log::Log4perl; our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions.sparql"); $VERSION = '2.910'; } use POSIX; use Encode; use URI::Escape; use Carp qw(carp croak confess); use Data::Dumper; use I18N::LangTags; use List::Util qw(sum); use Scalar::Util qw(blessed reftype refaddr looks_like_number); use DateTime::Format::W3CDTF; use RDF::Trine::Namespace qw(rdf xsd); use Digest::MD5 qw(md5_hex); use Digest::SHA qw(sha1_hex sha224_hex sha256_hex sha384_hex sha512_hex); use Data::UUID; use RDF::Query::Error qw(:try); use RDF::Query::Node qw(iri literal); =begin private =item C<< install >> Documented in L. =end private =cut sub install { RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#integer", sub { my $query = shift; my $node = shift; my $value; if (blessed($node) and $node->isa('RDF::Trine::Node::Literal')) { my $type = $node->literal_datatype || ''; $value = $node->literal_value; if ($type eq 'http://www.w3.org/2001/XMLSchema#boolean') { $value = ($value eq 'true') ? '1' : '0'; } elsif ($node->is_numeric_type) { if ($type eq 'http://www.w3.org/2001/XMLSchema#double') { throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot cast to xsd:integer as precision would be lost" ); } elsif (int($value) != $value) { throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot cast to xsd:integer as precision would be lost" ); } else { $value = $node->numeric_value; } } elsif (looks_like_number($value)) { if ($value =~ /[eE]/) { # double throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot to xsd:integer as precision would be lost" ); } elsif (int($value) != $value) { throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot to xsd:integer as precision would be lost" ); } } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:integer" ); } return RDF::Query::Node::Literal->new( "$value", undef, 'http://www.w3.org/2001/XMLSchema#integer' ); } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast node to xsd:integer" ); } } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#decimal", sub { my $query = shift; my $node = shift; my $value; if ($node->is_literal) { my $type = $node->literal_datatype || ''; $value = $node->literal_value; if ($type eq 'http://www.w3.org/2001/XMLSchema#boolean') { $value = ($value eq 'true') ? '1' : '0'; } elsif ($node->is_numeric_type) { if ($type eq 'http://www.w3.org/2001/XMLSchema#double') { throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot to xsd:decimal as precision would be lost" ); } else { $value = $node->numeric_value; } } elsif (looks_like_number($value)) { if ($value =~ /[eE]/) { # double throw RDF::Query::Error::FilterEvaluationError ( -text => "cannot to xsd:decimal as precision would be lost" ); } } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:decimal" ); } return RDF::Query::Node::Literal->new( "$value", undef, 'http://www.w3.org/2001/XMLSchema#decimal' ); } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast node to xsd:integer" ); } } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#float", sub { my $query = shift; my $node = shift; my $value; if ($node->is_literal) { $value = $node->literal_value; my $type = $node->literal_datatype || ''; if ($type eq 'http://www.w3.org/2001/XMLSchema#boolean') { $value = ($value eq 'true') ? '1.0' : '0.0'; } elsif ($node->is_numeric_type) { # noop } elsif (not $node->has_datatype) { if (looks_like_number($value)) { $value = +$value; } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:float" ); } } elsif (not $node->is_numeric_type) { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:float" ); } } elsif ($node->is_resource) { throw RDF::Query::Error::TypeError ( -text => "cannot cast an IRI to xsd:integer" ); } my $num = sprintf("%e", $value); return RDF::Query::Node::Literal->new( $num, undef, 'http://www.w3.org/2001/XMLSchema#float' ); } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#double", sub { my $query = shift; my $node = shift; my $value; if ($node->is_literal) { $value = $node->literal_value; my $type = $node->literal_datatype || ''; if ($type eq 'http://www.w3.org/2001/XMLSchema#boolean') { $value = ($value eq 'true') ? '1.0' : '0.0'; } elsif ($node->is_numeric_type) { # noop } elsif (not $node->has_datatype) { if (looks_like_number($value)) { $value = +$value; } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:double" ); } } elsif (not $node->is_numeric_type) { throw RDF::Query::Error::TypeError ( -text => "cannot cast unrecognized value '$value' to xsd:double" ); } } elsif ($node->is_resource) { throw RDF::Query::Error::TypeError ( -text => "cannot cast an IRI to xsd:double" ); } elsif ($node->is_blank) { throw RDF::Query::Error::TypeError -text => "cannot cast bnode to xsd:double"; } my $num = sprintf("%e", $value); return RDF::Query::Node::Literal->new( $num, undef, 'http://www.w3.org/2001/XMLSchema#double' ); } ); ### Effective Boolean Value RDF::Query::Functions->install_function( "sparql:ebv", sub { my $query = shift; my $node = shift; if ($node->is_literal) { if ($node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') if ($value); return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean') if (not $value); } elsif ($node->has_datatype) { my $type = $node->literal_datatype; my $value = $node->literal_value; if ($type eq 'http://www.w3.org/2001/XMLSchema#boolean') { return ($value eq 'true') ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { throw RDF::Query::Error::TypeError -text => "Unusable type in EBV: " . Dumper($node); } } else { my $value = $node->literal_value; return (length($value)) ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } throw RDF::Query::Error::FilterEvaluationError ( -text => "'$node' cannot be cast to a boolean type (true or false)" ); } elsif ($node->is_resource) { throw RDF::Query::Error::TypeError ( -text => "cannot cast an IRI to xsd:boolean" ); } } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#boolean", sub { my $query = shift; my $node = shift; if ($node->is_literal) { if ($node->is_numeric_type) { my $value = $node->numeric_value; if ($value) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } elsif ($node->has_datatype) { my $type = $node->literal_datatype; my $value = $node->literal_value; if ($value eq 'true') { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } elsif ($value eq 'false') { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { throw RDF::Query::Error::TypeError -text => "Unusable type in boolean cast: " . Dumper($node); } } else { my $value = $node->literal_value; if ($value eq 'true') { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } elsif ($value eq 'false') { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { throw RDF::Query::Error::TypeError -text => "Cannot cast to xsd:boolean: " . Dumper($node); } } } else { throw RDF::Query::Error::TypeError -text => "Cannot cast to xsd:boolean: " . Dumper($node); } } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#string", sub { my $query = shift; my $node = shift; if ($node->is_literal) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new($value, undef, 'http://www.w3.org/2001/XMLSchema#string'); } elsif ($node->is_resource) { my $value = $node->uri_value; return RDF::Query::Node::Literal->new($value, undef, 'http://www.w3.org/2001/XMLSchema#string'); } else { throw RDF::Query::Error::TypeError ( -text => "cannot cast node to xsd:string: " . $node ); } } ); RDF::Query::Functions->install_function( "http://www.w3.org/2001/XMLSchema#dateTime", sub { my $query = shift; my $node = shift; my $f = ref($query) ? $query->dateparser : DateTime::Format::W3CDTF->new; my $value = $node->literal_value; unless ($value =~ m<-?\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d([.]\d+)?(Z|[-+]\d\d:\d\d)?>) { throw RDF::Query::Error::TypeError -text => "Not a valid lexical form for xsd:dateTime: '$value'"; } my $dt = eval { $f->parse_datetime( $value ) }; if ($dt) { my $value = DateTime::Format::W3CDTF->new->format_datetime( $dt ); return RDF::Query::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); } else { throw RDF::Query::Error::TypeError -text => "Failed to parse lexical form as xsd:dateTime: '$value'"; } } ); RDF::Query::Functions->install_function( "sparql:str", sub { my $query = shift; my $node = shift; unless (blessed($node)) { throw RDF::Query::Error::TypeError -text => "STR() must be called with either a literal or resource"; } if ($node->is_literal) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( $value ); } elsif ($node->is_resource) { my $value = $node->uri_value; return RDF::Query::Node::Literal->new( $value ); } else { throw RDF::Query::Error::TypeError -text => "STR() must be called with either a literal or resource"; } } ); RDF::Query::Functions->install_function( ["http://www.w3.org/ns/sparql#strdt", "sparql:strdt"], sub { my $query = shift; my $str = shift; my $dt = shift; unless (blessed($str) and $str->isa('RDF::Query::Node::Literal') and blessed($dt) and $dt->isa('RDF::Query::Node::Resource')) { throw RDF::Query::Error::TypeError -text => "STRDT() must be called with a plain literal and a datatype IRI"; } unless ($str->is_simple_literal) { throw RDF::Query::Error::TypeError -text => "STRDT() not called with a simple literal"; } my $value = $str->literal_value; my $uri = $dt->uri_value; return RDF::Query::Node::Literal->new( $value, undef, $uri ); } ); RDF::Query::Functions->install_function( ["http://www.w3.org/ns/sparql#strlang", "sparql:strlang"], sub { my $query = shift; my $str = shift; my $lang = shift; unless (blessed($str) and $str->isa('RDF::Query::Node::Literal') and blessed($lang) and $lang->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "STRLANG() must be called with two plain literals"; } unless ($str->is_simple_literal) { throw RDF::Query::Error::TypeError -text => "STRLANG() not called with a simple literal"; } my $value = $str->literal_value; my $langtag = $lang->literal_value; return RDF::Query::Node::Literal->new( $value, $langtag ); } ); RDF::Query::Functions->install_function( ["sparql:uri", "sparql:iri"], sub { my $query = shift; my $node = shift; unless (blessed($node)) { throw RDF::Query::Error::TypeError -text => "URI/IRI() must be called with either a literal or resource"; } my $base = $query->{parsed}{base}; if ($node->is_literal) { my $value = $node->literal_value; return RDF::Query::Node::Resource->new( $value, $base ); } elsif ($node->is_resource) { return $node; } else { throw RDF::Query::Error::TypeError -text => "URI/IRI() must be called with either a literal or resource"; } } ); RDF::Query::Functions->install_function( "sparql:bnode", sub { my $query = shift; if (@_) { my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "BNODE() must be called with either a literal or resource"; } my $value = $node->literal_value; if (my $bnode = $query->{_query_row_cache}{'sparql:bnode'}{$value}) { return $bnode; } else { my $bnode = RDF::Query::Node::Blank->new(); $query->{_query_row_cache}{'sparql:bnode'}{$value} = $bnode; return $bnode; } } else { return RDF::Query::Node::Blank->new(); } } ); RDF::Query::Functions->install_function( "sparql:logical-or", sub { my $query = shift; ### Arguments to sparql:logical-* functions are passed lazily via a closure ### so that TypeErrors in arguments can be handled properly. my $args = shift; my $l = Log::Log4perl->get_logger("rdf.query.functions.logicalor"); $l->trace('executing logical-or'); my $ebv = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $arg; my $error; while (1) { my $bool; try { $l->trace('- getting logical-or operand...'); $arg = $args->(); if (defined($arg)) { $l->trace("- logical-or operand: $arg"); my $func = RDF::Query::Expression::Function->new( $ebv, $arg ); my $value = $func->evaluate( $query, {} ); $bool = ($value->literal_value eq 'true') ? 1 : 0; } } otherwise { my $e = shift; $l->debug("error in lhs of logical-or: " . $e->text . " at " . $e->file . " line " . $e->line); $error ||= $e; }; last unless (defined($arg)); if ($bool) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } if ($error) { $l->debug('logical-or error: ' . $error->text); $error->throw; } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); # sparql:logical-and RDF::Query::Functions->install_function( "sparql:logical-and", sub { my $query = shift; ### Arguments to sparql:logical-* functions are passed lazily via a closure ### so that TypeErrors in arguments can be handled properly. my $args = shift; my $l = Log::Log4perl->get_logger("rdf.query.functions.logicaland"); $l->trace('executing logical-and'); my $ebv = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $arg; my $error; while (1) { my $bool; try { $l->trace('- getting logical-and operand...'); $arg = $args->(); if (defined($arg)) { $l->trace("- logical-and operand: $arg"); my $func = RDF::Query::Expression::Function->new( $ebv, $arg ); my $value = $func->evaluate( $query, {} ); $bool = ($value->literal_value eq 'true') ? 1 : 0; } } otherwise { my $e = shift; $l->debug("error in lhs of logical-and: " . $e->text); $error ||= $e; }; last unless (defined($arg)); unless ($bool) { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } if ($error) { $l->debug('logical-and error: ' . $error->text); $error->throw; } else { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); RDF::Query::Functions->install_function( "sparql:in", sub { return __IN_FUNC('in', @_) } ); RDF::Query::Functions->install_function( "sparql:notin", sub { return __IN_FUNC('notin', @_) } ); sub __IN_FUNC { my $op = shift; my $query = shift; my $args = shift; my $node = $args->(); unless (blessed($node)) { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } my $arg; my $error; while (1) { my $bool; try { $l->trace("- getting $op operand..."); $arg = $args->(); if (defined($arg)) { $l->trace("- $op operand: $arg"); my $expr = RDF::Query::Expression::Binary->new('==', $node, $arg); my $value = $expr->evaluate( $query, {} ); $bool = ($value->literal_value eq 'true') ? 1 : 0; } } catch RDF::Query::Error with { my $e = shift; $l->debug("error in lhs of logical-and: " . $e->text); $error ||= $e; } otherwise {}; last unless (defined($arg)); if ($bool) { if ($op eq 'notin') { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } } if ($error) { $l->debug("$op error: " . $error->text); $error->throw; } else { if ($op eq 'notin') { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } } # sparql:bound RDF::Query::Functions->install_function( ["http://www.w3.org/ns/sparql#bound", "sparql:bound"], sub { my $query = shift; my $node = shift; if (blessed($node)) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); RDF::Query::Functions->install_function( ["sparql:isuri", "sparql:isiri"], sub { my $query = shift; my $node = shift; if ($node->isa('RDF::Trine::Node::Resource')) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); # sparql:isblank RDF::Query::Functions->install_function( "sparql:isblank", sub { my $query = shift; my $node = shift; if ($node->isa('RDF::Trine::Node::Blank')) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); # sparql:isliteral RDF::Query::Functions->install_function( "sparql:isliteral", sub { my $query = shift; my $node = shift; if ($node->isa('RDF::Trine::Node::Literal')) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); RDF::Query::Functions->install_function( "sparql:lang", sub { my $query = shift; my $node = shift; if ($node->is_literal) { my $lang = ($node->has_language) ? $node->literal_value_language : ''; return RDF::Query::Node::Literal->new( $lang ); } else { throw RDF::Query::Error::TypeError ( -text => "cannot call lang() on a non-literal value" ); } } ); RDF::Query::Functions->install_function( "sparql:langmatches", sub { my $query = shift; my $l = shift; my $m = shift; my $lang = $l->literal_value; my $match = $m->literal_value; my $true = RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); my $false = RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); if ($match eq '*') { # """A language-range of "*" matches any non-empty language-tag string.""" return ($lang ? $true : $false); } else { return (I18N::LangTags::is_dialect_of( $lang, $match )) ? $true : $false; } } ); RDF::Query::Functions->install_function( "sparql:sameterm", sub { my $query = shift; my $nodea = shift; my $nodeb = shift; my $bool = 0; if ($nodea->isa('RDF::Trine::Node::Resource')) { $bool = $nodea->equal( $nodeb ); } elsif ($nodea->isa('RDF::Trine::Node::Blank')) { $bool = $nodea->equal( $nodeb ); } elsif ($nodea->isa('RDF::Trine::Node::Literal') and $nodeb->isa('RDF::Trine::Node::Literal')) { if ($nodea->literal_value ne $nodeb->literal_value) { $bool = 0; } elsif (not($nodea->has_language == $nodeb->has_language)) { $bool = 0; } elsif (not $nodea->has_datatype == $nodeb->has_datatype) { $bool = 0; } elsif ($nodea->has_datatype or $nodeb->has_datatype) { if ($nodea->literal_datatype ne $nodeb->literal_datatype) { $bool = 0; } else { $bool = 1; } } elsif ($nodea->has_language or $nodeb->has_language) { if ($nodea->literal_value_language ne $nodeb->literal_value_language) { $bool = 0; } else { $bool = 1; } } else { $bool = 1; } } return ($bool) ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); RDF::Query::Functions->install_function( "sparql:datatype", sub { # """Returns the datatype IRI of typedLit; returns xsd:string if the parameter is a simple literal.""" my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node')) { throw RDF::Query::Error::MethodInvocationError -text => "DATATYPE() called without a valid RDF Term"; } if ($node->is_literal) { if ($node->has_language) { return $rdf->langString; } elsif ($node->has_datatype) { my $type = $node->literal_datatype; $l->debug("datatype => $type"); return RDF::Query::Node::Resource->new($type); } else { $l->debug('datatype => string'); return RDF::Query::Node::Resource->new('http://www.w3.org/2001/XMLSchema#string'); } } else { throw RDF::Query::Error::TypeError ( -text => "cannot call datatype() on a non datatyped node" ); } } ); RDF::Query::Functions->install_function( "sparql:regex", sub { my $query = shift; my $node = shift; my $match = shift; unless ($node->is_literal) { throw RDF::Query::Error::TypeError ( -text => 'REGEX() called with non-string data' ); } my $text = $node->literal_value; my $pattern = $match->literal_value; if (index($pattern, '(?{') != -1 or index($pattern, '(??{') != -1) { throw RDF::Query::Error::FilterEvaluationError ( -text => 'REGEX() called with unsafe ?{} pattern' ); } if (@_) { my $data = shift; my $flags = $data->literal_value; if ($flags !~ /^[smix]*$/) { throw RDF::Query::Error::FilterEvaluationError ( -text => 'REGEX() called with unrecognized flags' ); } $pattern = qq[(?${flags}:$pattern)]; } return ($text =~ /$pattern/) ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); RDF::Query::Functions->install_function( "sparql:exists", sub { my $query = shift; my $context = shift; my $bound = shift; my $ggp = shift; my $graph = shift; my ($plan) = RDF::Query::Plan->generate_plans( $ggp, $context, active_graph => $graph ); Carp::confess "No execution contexted passed to sparql:exists" unless (blessed($context)); my $l = Log::Log4perl->get_logger("rdf.query.functions.exists"); my $copy = $context->copy( bound => $bound ); $plan->execute( $copy ); if (my $row = $plan->next) { $l->trace("got EXISTS row: $row"); return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } else { $l->trace("didn't find EXISTS row"); return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } } ); RDF::Query::Functions->install_function( "sparql:coalesce", sub { my $query = shift; my $args = shift; while (defined(my $node = $args->())) { if (blessed($node)) { return $node; } } } ); # sparql:isNumeric RDF::Query::Functions->install_function( "sparql:isnumeric", sub { my $query = shift; my $node = shift; if ($node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type and $node->is_valid_lexical_form) { return RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } return RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); # sparql:abs RDF::Query::Functions->install_function( "sparql:abs", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( abs($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "sparql:abs called without a numeric literal"; } } ); # sparql:ceil RDF::Query::Functions->install_function( "sparql:ceil", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( ceil($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "sparql:ceil called without a numeric literal"; } } ); # sparql:floor RDF::Query::Functions->install_function( "sparql:floor", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( floor($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "sparql:floor called without a numeric literal"; } } ); # sparql:round RDF::Query::Functions->install_function( "sparql:round", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; my $mult = 1; if ($value < 0) { $mult = -1; $value = -$value; } my $round = $mult * POSIX::floor($value + 0.50000000000008); return RDF::Query::Node::Literal->new( $round, undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "sparql:round called without a numeric literal"; } } ); # sparql:concat RDF::Query::Functions->install_function( "sparql:concat", sub { my $query = shift; my $model = $query->model; my @nodes = @_; my $lang; my $all_lang = 1; my $all_str = 1; foreach my $n (@nodes) { unless ($n->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:concat called with a non-literal argument"; } if ($n->has_datatype) { $all_lang = 0; my $dt = $n->literal_datatype; if ($dt ne 'http://www.w3.org/2001/XMLSchema#string') { throw RDF::Query::Error::TypeError -text => "sparql:concat called with a datatyped-literal other than xsd:string"; } } elsif ($n->has_language) { $all_str = 0; if (defined($lang) and $lang ne $n->literal_value_language) { $all_lang = 0; } else { $lang = $n->literal_value_language; } } else { $all_lang = 0; $all_str = 0; } } my @strtype; if ($all_lang) { $strtype[0] = $lang; } elsif ($all_str) { $strtype[1] = 'http://www.w3.org/2001/XMLSchema#string' } my $value = join('', map { $_->literal_value } @nodes); return RDF::Query::Node::Literal->new($value, @strtype); } ); # sparql:substr RDF::Query::Functions->install_function( "sparql:substr", sub { my $query = shift; my $node = shift; my @args = @_; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:substr called without a literal arg1 term"; } my $value = $node->literal_value; my @nums; foreach my $i (0 .. $#args) { my $argnum = $i + 2; my $arg = $args[ $i ]; unless (blessed($arg) and $arg->isa('RDF::Query::Node::Literal') and $arg->is_numeric_type) { throw RDF::Query::Error::TypeError -text => "sparql:substr called without a numeric literal arg${argnum} term"; } push(@nums, $arg->numeric_value); } $nums[0]--; my $substring = (scalar(@nums) > 1) ? substr($value, $nums[0], $nums[1]) : substr($value, $nums[0]); return RDF::Query::Node::Literal->new($substring, $node->type_list); } ); # sparql:strlen RDF::Query::Functions->install_function( "sparql:strlen", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( length($value), undef, $xsd->integer ); } else { throw RDF::Query::Error::TypeError -text => "sparql:strlen called without a literal term"; } } ); # sparql:ucase RDF::Query::Functions->install_function( "sparql:ucase", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( uc($value), $node->type_list ); } else { throw RDF::Query::Error::TypeError -text => "sparql:ucase called without a literal term"; } } ); # sparql:lcase RDF::Query::Functions->install_function( "sparql:lcase", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( lc($value), $node->type_list ); } else { throw RDF::Query::Error::TypeError -text => "sparql:lcase called without a literal term"; } } ); RDF::Query::Functions->install_function("sparql:encode_for_uri", \&_encode_for_uri); RDF::Query::Functions->install_function("sparql:contains", \&_contains); RDF::Query::Functions->install_function("sparql:strstarts", \&_strstarts); RDF::Query::Functions->install_function("sparql:strends", \&_strends); RDF::Query::Functions->install_function("sparql:rand", \&_rand); RDF::Query::Functions->install_function("sparql:md5", \&_md5); RDF::Query::Functions->install_function("sparql:sha1", \&_sha1); RDF::Query::Functions->install_function("sparql:sha224", \&_sha224); RDF::Query::Functions->install_function("sparql:sha256", \&_sha256); RDF::Query::Functions->install_function("sparql:sha384", \&_sha384); RDF::Query::Functions->install_function("sparql:sha512", \&_sha512); RDF::Query::Functions->install_function("sparql:year", \&_year); RDF::Query::Functions->install_function("sparql:month", \&_month); RDF::Query::Functions->install_function("sparql:day", \&_day); RDF::Query::Functions->install_function("sparql:hours", \&_hours); RDF::Query::Functions->install_function("sparql:minutes", \&_minutes); RDF::Query::Functions->install_function("sparql:seconds", \&_seconds); RDF::Query::Functions->install_function("sparql:timezone", \&_timezone); RDF::Query::Functions->install_function("sparql:tz", \&_tz); RDF::Query::Functions->install_function("sparql:now", \&_now); RDF::Query::Functions->install_function("sparql:strbefore", \&_strbefore); RDF::Query::Functions->install_function("sparql:strafter", \&_strafter); RDF::Query::Functions->install_function("sparql:replace", \&_replace); RDF::Query::Functions->install_function("sparql:uuid", \&_uuid); RDF::Query::Functions->install_function("sparql:struuid", \&_struuid); } =item * sparql:encode_for_uri =cut sub _encode_for_uri { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( uri_escape_utf8($value) ); } else { throw RDF::Query::Error::TypeError -text => "sparql:encode_for_uri called without a literal term"; } } =item * sparql:contains =cut sub _contains { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:contains called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:contains called without a literal arg2 term"; } # TODO: what should be returned if one or both arguments are typed as xsd:string? if ($node->has_language and $pat->has_language) { if ($node->literal_value_language ne $pat->literal_value_language) { throw RDF::Query::Error::TypeError -text => "sparql:contains called with literals of different languages"; } } my $lit = $node->literal_value; my $plit = $pat->literal_value; my $pos = index($lit, $plit); if ($pos >= 0) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } =item * sparql:strstarts =cut sub _strstarts { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strstarts called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strstarts called without a literal arg2 term"; } # TODO: what should be returned if one or both arguments are typed as xsd:string? if ($node->has_language and $pat->has_language) { # TODO: if the language tags are different, does this error, or just return false? if ($node->literal_value_language ne $pat->literal_value_language) { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } if (index($node->literal_value, $pat->literal_value) == 0) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } =item * sparql:strends =cut sub _strends { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strends called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strends called without a literal arg2 term"; } # TODO: what should be returned if one or both arguments are typed as xsd:string? if ($node->has_language and $pat->has_language) { # TODO: if the language tags are different, does this error, or just return false? if ($node->literal_value_language ne $pat->literal_value_language) { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } my $lit = $node->literal_value; my $plit = $pat->literal_value; my $pos = length($lit) - length($plit); if (rindex($lit, $plit) == $pos) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } =item * sparql:rand =cut sub _rand { my $query = shift; my $r = rand(); my $value = RDF::Trine::Node::Literal->canonicalize_literal_value( $r, $xsd->double->as_string ); return RDF::Query::Node::Literal->new($value, undef, $xsd->double); } =item * sparql:md5 =cut sub _md5 { my $query = shift; my $node = shift; return literal( md5_hex(encode_utf8($node->literal_value)) ); } =item * sparql:sha1 =cut sub _sha1 { my $query = shift; my $node = shift; return literal( sha1_hex(encode_utf8($node->literal_value)) ); } =item * sparql:sha224 =cut sub _sha224 { my $query = shift; my $node = shift; return literal( sha224_hex(encode_utf8($node->literal_value)) ); } =item * sparql:sha256 =cut sub _sha256 { my $query = shift; my $node = shift; return literal( sha256_hex(encode_utf8($node->literal_value)) ); } =item * sparql:sha384 =cut sub _sha384 { my $query = shift; my $node = shift; return literal( sha384_hex(encode_utf8($node->literal_value)) ); } =item * sparql:sha512 =cut sub _sha512 { my $query = shift; my $node = shift; return literal( sha512_hex(encode_utf8($node->literal_value)) ); } =item * sparql:year =cut sub _year { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:year called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->year, undef, $xsd->integer); } else { throw RDF::Query::Error::TypeError -text => "sparql:year called without a valid dateTime"; } } =item * sparql:month =cut sub _month { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:month called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->month, undef, $xsd->integer); } else { throw RDF::Query::Error::TypeError -text => "sparql:month called without a valid dateTime"; } } =item * sparql:day =cut sub _day { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:day called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->day, undef, $xsd->integer); } else { throw RDF::Query::Error::TypeError -text => "sparql:day called without a valid dateTime"; } } =item * sparql:hours =cut sub _hours { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:hours called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->hour, undef, $xsd->integer); } else { throw RDF::Query::Error::TypeError -text => "sparql:hours called without a valid dateTime"; } } =item * sparql:minutes =cut sub _minutes { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:minutes called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->minute, undef, $xsd->integer); } else { throw RDF::Query::Error::TypeError -text => "sparql:minutes called without a valid dateTime"; } } =item * sparql:seconds =cut sub _seconds { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:seconds called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->second, undef, $xsd->decimal); } else { throw RDF::Query::Error::TypeError -text => "sparql:seconds called without a valid dateTime"; } } =item * sparql:timezone =cut sub _timezone { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:timezone called without a literal term"; } my $dt = $node->datetime; if ($dt) { my $tz = $dt->time_zone; if ($tz->is_floating) { throw RDF::Query::Error::TypeError -text => "sparql:timezone called with a dateTime without a timezone"; } if ($tz) { my $offset = $tz->offset_for_datetime( $dt ); my $minus = ''; if ($offset < 0) { $minus = '-'; $offset = -$offset; } my $duration = "${minus}PT"; if ($offset >= 60*60) { my $h = int($offset / (60*60)); $duration .= "${h}H" if ($h > 0); $offset = $offset % (60*60); } if ($offset >= 60) { my $m = int($offset / 60); $duration .= "${m}M" if ($m > 0); $offset = $offset % 60; } my $s = int($offset); $duration .= "${s}S" if ($s > 0 or $duration eq 'PT'); return RDF::Query::Node::Literal->new($duration, undef, $xsd->dayTimeDuration); } } throw RDF::Query::Error::TypeError -text => "sparql:timezone called without a valid dateTime"; } =item * sparql:tz =cut sub _tz { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:tz called without a literal term"; } my $dt = $node->datetime; if ($dt) { my $tz = $dt->time_zone; if ($tz->is_floating) { return RDF::Query::Node::Literal->new(''); } if ($tz->is_utc) { return RDF::Query::Node::Literal->new('Z'); } if ($tz) { my $offset = $tz->offset_for_datetime( $dt ); my $hours = 0; my $minutes = 0; my $minus = '+'; if ($offset < 0) { $minus = '-'; $offset = -$offset; } if ($offset >= 60*60) { $hours = int($offset / (60*60)); $offset = $offset % (60*60); } if ($offset >= 60) { $minutes = int($offset / 60); $offset = $offset % 60; } my $seconds = int($offset); my $tz = sprintf('%s%02d:%02d', $minus, $hours, $minutes); return RDF::Query::Node::Literal->new($tz); } else { return RDF::Query::Node::Literal->new(''); } } throw RDF::Query::Error::TypeError -text => "sparql:tz called without a valid dateTime"; } =item * sparql:now =cut sub _now { my $query = shift; my $dt = DateTime->now; my $value = DateTime::Format::W3CDTF->new->format_datetime( $dt ); return RDF::Query::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#dateTime' ); } =item * sparql:strbefore =cut sub _strbefore { my $query = shift; my $node = shift; my $substr = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strbefore called without a literal arg1 term"; } unless (blessed($substr) and $substr->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strbefore called without a literal arg2 term"; } if ($node->has_datatype and $node->literal_datatype ne 'http://www.w3.org/2001/XMLSchema#string') { throw RDF::Query::Error::TypeError -text => "sparql:strbefore called with a datatyped (non-xsd:string) literal"; } my $lhs_simple = not($node->has_language or $node->has_datatype); my $lhs_xsd = ($node->has_datatype and $node->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string'); my $rhs_simple = not($substr->has_language or $substr->has_datatype); my $rhs_xsd = ($substr->has_datatype and $substr->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string'); if (($lhs_simple or $lhs_xsd) and ($rhs_simple or $rhs_xsd)) { # ok } elsif ($node->has_language and $substr->has_language and $node->literal_value_language eq $substr->literal_value_language) { # ok } elsif ($node->has_language and ($rhs_simple or $rhs_xsd)) { # ok } else { throw RDF::Query::Error::TypeError -text => "sparql:strbefore called with literals that are not argument compatible"; } my $value = $node->literal_value; my $match = $substr->literal_value; my $i = index($value, $match, 0); if ($i < 0) { return RDF::Query::Node::Literal->new(''); } else { return RDF::Query::Node::Literal->new(substr($value, 0, $i), $node->type_list); } } =item * sparql:strafter =cut sub _strafter { my $query = shift; my $node = shift; my $substr = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strafter called without a literal arg1 term"; } unless (blessed($substr) and $substr->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:strafter called without a literal arg2 term"; } if ($node->has_datatype and $node->literal_datatype ne 'http://www.w3.org/2001/XMLSchema#string') { throw RDF::Query::Error::TypeError -text => "sparql:strafter called with a datatyped (non-xsd:string) literal"; } my $lhs_simple = not($node->has_language or $node->has_datatype); my $lhs_xsd = ($node->has_datatype and $node->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string'); my $rhs_simple = not($substr->has_language or $substr->has_datatype); my $rhs_xsd = ($substr->has_datatype and $substr->literal_datatype eq 'http://www.w3.org/2001/XMLSchema#string'); if (($lhs_simple or $lhs_xsd) and ($rhs_simple or $rhs_xsd)) { # ok } elsif ($node->has_language and $substr->has_language and $node->literal_value_language eq $substr->literal_value_language) { # ok } elsif ($node->has_language and ($rhs_simple or $rhs_xsd)) { # ok } else { throw RDF::Query::Error::TypeError -text => "sparql:strafter called with literals that are not argument compatible"; } my $value = $node->literal_value; my $match = $substr->literal_value; my $i = index($value, $match, 0); if ($i < 0) { return RDF::Query::Node::Literal->new(''); } else { return RDF::Query::Node::Literal->new(substr($value, $i+length($match)), $node->type_list); } } =item * sparql:replace =cut sub _replace { my $query = shift; my $node = shift; my $pat = shift; my $rep = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:replace called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:replace called without a literal arg2 term"; } unless (blessed($rep) and $rep->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "sparql:replace called without a literal arg3 term"; } if ($node->has_datatype and $node->literal_datatype ne 'http://www.w3.org/2001/XMLSchema#string') { throw RDF::Query::Error::TypeError -text => "sparql:replace called with a datatyped (non-xsd:string) literal"; } my $value = $node->literal_value; my $pattern = $pat->literal_value; my $replace = $rep->literal_value; if (index($pattern, '(?{') != -1 or index($pattern, '(??{') != -1) { throw RDF::Query::Error::FilterEvaluationError ( -text => 'REPLACE() called with unsafe ?{} match pattern' ); } if (index($replace, '(?{') != -1 or index($replace, '(??{') != -1) { throw RDF::Query::Error::FilterEvaluationError ( -text => 'REPLACE() called with unsafe ?{} replace pattern' ); } $replace =~ s/\\/\\\\/g; $replace =~ s/\$(\d+)/\$$1/g; $replace =~ s/"/\\"/g; $replace = qq["$replace"]; no warnings 'uninitialized'; $value =~ s/$pattern/"$replace"/eeg; # warn "==> " . Dumper($value); return RDF::Query::Node::Literal->new($value, $node->type_list); } sub _uuid { my $query = shift; my $u = Data::UUID->new(); return iri('urn:uuid:' . $u->to_string( $u->create() )); } sub _struuid { my $query = shift; my $u = Data::UUID->new(); return literal($u->to_string( $u->create() )); } 1; __END__ =item * http://www.w3.org/2001/XMLSchema#boolean =item * http://www.w3.org/2001/XMLSchema#dateTime =item * http://www.w3.org/2001/XMLSchema#decimal =item * http://www.w3.org/2001/XMLSchema#double =item * http://www.w3.org/2001/XMLSchema#float =item * http://www.w3.org/2001/XMLSchema#integer =item * http://www.w3.org/2001/XMLSchema#string =back =head1 AUTHOR Gregory Williams . =cut RDF-Query-2.910/lib/RDF/Query/Functions/Xpath.pm000644 000765 000024 00000042732 12173312155 021222 0ustar00gregstaff000000 000000 =head1 NAME RDF::Query::Functions::Xpath - XPath functions =head1 VERSION This document describes RDF::Query::Functions::Xpath version 2.910. =head1 DESCRIPTION Defines the following function: =over =item * http://www.w3.org/2005/xpath-functions#matches =item * http://www.w3.org/2005/xpath-functions#abs =item * http://www.w3.org/2005/xpath-functions#ceiling =item * http://www.w3.org/2005/xpath-functions#floor =item * http://www.w3.org/2005/xpath-functions#round =item * http://www.w3.org/2005/xpath-functions#round-half-to-even =item * http://www.w3.org/2005/xpath-functions#compare =item * http://www.w3.org/2005/xpath-functions#concat =item * http://www.w3.org/2005/xpath-functions#substring =item * http://www.w3.org/2005/xpath-functions#string-length =item * http://www.w3.org/2005/xpath-functions#upper-case =item * http://www.w3.org/2005/xpath-functions#lower-case =item * http://www.w3.org/2005/xpath-functions#encode-for-uri =item * http://www.w3.org/2005/xpath-functions#contains =item * http://www.w3.org/2005/xpath-functions#starts-with =item * http://www.w3.org/2005/xpath-functions#ends-with =item * http://www.w3.org/2005/xpath-functions#year-from-dateTime =item * http://www.w3.org/2005/xpath-functions#month-from-dateTime =item * http://www.w3.org/2005/xpath-functions#day-from-dateTime =item * http://www.w3.org/2005/xpath-functions#hours-from-dateTime =item * http://www.w3.org/2005/xpath-functions#minutes-from-dateTime =item * http://www.w3.org/2005/xpath-functions#seconds-from-dateTime =item * http://www.w3.org/2005/xpath-functions#timezone-from-dateTime =back =cut package RDF::Query::Functions::Xpath; use strict; use warnings; use Log::Log4perl; use POSIX; use URI::Escape; use RDF::Trine::Namespace qw(xsd); our ($VERSION, $l); BEGIN { $l = Log::Log4perl->get_logger("rdf.query.functions.xpath"); $VERSION = '2.910'; } use Scalar::Util qw(blessed reftype refaddr looks_like_number); =begin private =item C<< install >> Documented in L. =end private =cut sub install { # # fn:compare # $RDF::Query::functions{"http://www.w3.org/2005/04/xpath-functionscompare"} = sub { # my $query = shift; # my $nodea = shift; # my $nodeb = shift; # my $cast = 'sop:str'; # return ($RDF::Query::functions{$cast}->($query, $nodea) cmp $RDF::Query::functions{$cast}->($query, $nodeb)); # }; # # # fn:not # $RDF::Query::functions{"http://www.w3.org/2005/04/xpath-functionsnot"} = sub { # my $query = shift; # my $nodea = shift; # my $nodeb = shift; # my $cast = 'sop:str'; # return (0 != ($RDF::Query::functions{$cast}->($query, $nodea) cmp $RDF::Query::functions{$cast}->($query, $nodeb))); # }; # fn:matches RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#matches", sub { my $query = shift; my $node = shift; my $match = shift; my $f = shift; my $string; if ($node->isa('RDF::Query::Node::Resource')) { $string = $node->uri_value; } elsif ($node->isa('RDF::Query::Node::Literal')) { $string = $node->literal_value; } else { throw RDF::Query::Error::TypeError -text => "xpath:matches called without a literal or resource"; } my $pattern = $match->literal_value; return undef if (index($pattern, '(?{') != -1); return undef if (index($pattern, '(??{') != -1); my $flags = blessed($f) ? $f->literal_value : ''; my $matches; if ($flags) { $pattern = "(?${flags}:${pattern})"; warn 'pattern: ' . $pattern; $matches = $string =~ /$pattern/; } else { $matches = ($string =~ /$pattern/) ? 1 : 0; } return ($matches) ? RDF::Query::Node::Literal->new('true', undef, 'http://www.w3.org/2001/XMLSchema#boolean') : RDF::Query::Node::Literal->new('false', undef, 'http://www.w3.org/2001/XMLSchema#boolean'); } ); # fn:abs RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#abs", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( abs($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "xpath:abs called without a numeric literal"; } } ); # fn:ceiling RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#ceiling", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( ceil($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "xpath:ceiling called without a numeric literal"; } } ); # fn:floor RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#floor", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( floor($value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "xpath:floor called without a numeric literal"; } } ); # fn:round RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#round", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; my $mult = 1; if ($value < 0) { $mult = -1; $value = -$value; } my $round = $mult * POSIX::floor($value + 0.50000000000008); return RDF::Query::Node::Literal->new( $round, undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "xpath:round called without a numeric literal"; } } ); # fn:round-half-to-even RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#round-half-to-even", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal') and $node->is_numeric_type) { my $value = $node->numeric_value; return RDF::Query::Node::Literal->new( sprintf('%.0f', $value), undef, $node->literal_datatype ); } else { throw RDF::Query::Error::TypeError -text => "xpath:round-half-to-even called without a numeric literal"; } } ); # fn:compare RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#compare", sub { my $query = shift; my $nodea = shift; my $nodeb = shift; unless (blessed($nodea) and $nodea->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:compare called without a literal arg1 term"; } unless (blessed($nodeb) and $nodeb->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:compare called without a literal arg2 term"; } my $value = ($nodea->literal_value cmp $nodeb->literal_value); return RDF::Query::Node::Literal->new( $value, undef, $xsd->integer ); } ); # fn:concat RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#concat", sub { my $query = shift; my $model = $query->model; my @nodes = @_; my @strings = map { $query->call_function($model, {}, 'sparql:str', $_) } @nodes; my $value = join('', map { $_->literal_value } @strings); return RDF::Query::Node::Literal->new($value); } ); # fn:substring RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#substring", sub { my $query = shift; my $node = shift; my @args = @_; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:substring called without a literal arg1 term"; } my $value = $node->literal_value; my @nums; foreach my $i (0 .. $#args) { my $argnum = $i + 2; my $arg = $args[ $i ]; unless (blessed($arg) and $arg->isa('RDF::Query::Node::Literal') and $arg->is_numeric_type) { throw RDF::Query::Error::TypeError -text => "xpath:substring called without a numeric literal arg${argnum} term"; } push(@nums, $arg->numeric_value); } my $substring = substr($value, @nums); return RDF::Query::Node::Literal->new($substring); } ); # fn:string-length RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#string-length", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( length($value), undef, $xsd->integer ); } else { throw RDF::Query::Error::TypeError -text => "xpath:string-length called without a literal term"; } } ); # fn:upper-case RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#upper-case", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( uc($value) ); } else { throw RDF::Query::Error::TypeError -text => "xpath:upper-case called without a literal term"; } } ); # fn:lower-case RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#lower-case", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( lc($value) ); } else { throw RDF::Query::Error::TypeError -text => "xpath:lower-case called without a literal term"; } } ); # fn:encode-for-uri RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#encode-for-uri", sub { my $query = shift; my $node = shift; if (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; return RDF::Query::Node::Literal->new( uri_escape($value) ); } else { throw RDF::Query::Error::TypeError -text => "xpath:escape-for-uri called without a literal term"; } } ); # fn:contains RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#contains", sub { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:contains called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:contains called without a literal arg2 term"; } my $lit = $node->literal_value; my $plit = $pat->literal_value; my $pos = index($lit, $plit); if ($pos >= 0) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } ); # fn:starts-with RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#starts-with", sub { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:starts-with called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:starts-with called without a literal arg2 term"; } if (index($node->literal_value, $pat->literal_value) == 0) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } ); # fn:ends-with RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#ends-with", sub { my $query = shift; my $node = shift; my $pat = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:ends-with called without a literal arg1 term"; } unless (blessed($pat) and $pat->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:ends-with called without a literal arg2 term"; } my $lit = $node->literal_value; my $plit = $pat->literal_value; my $pos = length($lit) - length($plit); if (rindex($lit, $plit) == $pos) { return RDF::Query::Node::Literal->new('true', undef, $xsd->boolean); } else { return RDF::Query::Node::Literal->new('false', undef, $xsd->boolean); } } ); # op:dateTime-equal # op:dateTime-less-than # op:dateTime-greater-than # fn:year-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#year-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:year-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->year); } else { throw RDF::Query::Error::TypeError -text => "xpath:year-from-dateTime called without a valid dateTime"; } } ); # fn:month-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#month-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:month-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->month); } else { throw RDF::Query::Error::TypeError -text => "xpath:month-from-dateTime called without a valid dateTime"; } } ); # fn:day-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#day-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:day-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->day); } else { throw RDF::Query::Error::TypeError -text => "xpath:day-from-dateTime called without a valid dateTime"; } } ); # fn:hours-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#hours-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:hours-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->hour); } else { throw RDF::Query::Error::TypeError -text => "xpath:hours-from-dateTime called without a valid dateTime"; } } ); # fn:minutes-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#minutes-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:minutes-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->minute); } else { throw RDF::Query::Error::TypeError -text => "xpath:minutes-from-dateTime called without a valid dateTime"; } } ); # fn:seconds-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#seconds-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:seconds-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { return RDF::Query::Node::Literal->new($dt->second); } else { throw RDF::Query::Error::TypeError -text => "xpath:seconds-from-dateTime called without a valid dateTime"; } } ); # fn:timezone-from-dateTime RDF::Query::Functions->install_function( "http://www.w3.org/2005/xpath-functions#timezone-from-dateTime", sub { my $query = shift; my $node = shift; unless (blessed($node) and $node->isa('RDF::Query::Node::Literal')) { throw RDF::Query::Error::TypeError -text => "xpath:timezone-from-dateTime called without a literal term"; } my $dt = $node->datetime; if ($dt) { my $tz = $dt->time_zone; if ($tz) { my $offset = $tz->offset_for_datetime( $dt ); my $minus = ''; if ($offset < 0) { $minus = '-'; $offset = -$offset; } my $duration = "${minus}PT"; if ($offset >= 60*60) { my $h = int($offset / (60*60)); $duration .= "${h}H" if ($h > 0); $offset = $offset % (60*60); } if ($offset >= 60) { my $m = int($offset / 60); $duration .= "${m}M" if ($m > 0); $offset = $offset % 60; } my $s = int($offset); $duration .= "${s}S" if ($s > 0 or $duration eq 'PT'); return RDF::Query::Node::Literal->new($duration); } } throw RDF::Query::Error::TypeError -text => "xpath:timezone-from-dateTime called without a valid dateTime"; } ); } 1; __END__ =head1 AUTHOR Gregory Williams . =cut RDF-Query-2.910/lib/RDF/Query/Federate/Plan.pm000644 000765 000024 00000021077 12173312155 020576 0ustar00gregstaff000000 000000 # RDF::Query::Federate::Plan # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Federate::Plan - Executable query plan nodes. =head1 VERSION This document describes RDF::Query::Federate::Plan version 2.910. =head1 STATUS This module's API and functionality should be considered deprecated. If you need functionality that this module provides, please L. =head1 METHODS =over 4 =cut package RDF::Query::Federate::Plan; use strict; use warnings; use base qw(RDF::Query::Plan); use Data::Dumper; use Set::Scalar; use List::Util qw(reduce); use Scalar::Util qw(blessed refaddr); use RDF::Query::Error qw(:try); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =item C<< generate_plans ( $algebra, $execution_context, %args ) >> Returns a list of equivalent query plan objects for the given algebra object. =cut sub generate_plans { my $self = shift; my $class = ref($self) || $self; my $algebra = shift; my $context = shift; my %args = @_; my $l = Log::Log4perl->get_logger('rdf.query.federate.plan'); my $query = $context->query; my $cache = ($query->{_query_cache}{federate_plans} ||= {}); my $sse = $algebra->sse(); if ($cache->{ $sse }) { return @{ $cache->{ $sse } }; } else { my $aclass = ref($algebra); my ($type) = ($aclass =~ m<::(\w+)$>); if ($type eq 'BasicGraphPattern') { # my ($plan) = $self->prune_plans( $context, $self->SUPER::generate_plans( $algebra, $context, %args ) ); my ($plan) = $self->SUPER::generate_plans( $algebra, $context, %args ); my @triples = $algebra->triples(); my @fplans = map { $_->[0] } $self->_optimistic_triple_join_plans( $context, \@triples, %args, method => 'triples' ); $l->debug("generating plans for federated query with algebra: $sse"); my @optimistic_plans; # foreach my $plan (@plans) { if (@fplans) { my $time = $context->optimistic_threshold_time || 0; my $oplan = RDF::Query::Plan::ThresholdUnion->new( $time, @fplans, $plan ); $oplan->label( services => $plan->label( 'services' ) ); push(@optimistic_plans, $oplan); } unless (@optimistic_plans) { push(@optimistic_plans, $plan); } # } $cache->{ $sse } = \@optimistic_plans; if ($l->is_debug) { foreach my $i (0 .. $#optimistic_plans) { my $p = $optimistic_plans[ $i ]; my $sse = $p->sse({}, ''); $l->debug("optimistic plan $i: $sse"); } } return @optimistic_plans; } else { return $self->SUPER::generate_plans( $algebra, $context, %args ); } } } sub _optimistic_triple_join_plans { my $self = shift; my $context = shift; my $triples = shift; my %args = @_; my $l = Log::Log4perl->get_logger('rdf.query.federate.plan'); my $method = $args{ method }; my @join_types = RDF::Query::Plan::Join->join_classes; my @triples = @$triples; my %triples = map { refaddr($_) => $_ } @triples; my %ids = map { refaddr($triples[$_]) => $_ } (0 .. $#triples); my %tplans = map { refaddr($_) => [ $self->generate_plans( $_, $context, %args ) ] } @triples; my %per_service; my @service_plans; foreach my $id (0 .. $#triples) { my $r = refaddr($triples[$id]); my $ps = $tplans{$r}; my $t = $triples{ $r }; foreach my $pid (0 .. $#{ $ps }) { my $p = $ps->[ $pid ]; $self->label_plan_with_services( $p, $context ); push(@service_plans, { plan => $p, size => 1, coverage => [$id] }); foreach my $service (@{ $p->label('services') || [] }) { push( @{ $per_service{ $service }{ $r } }, $p ); } } } foreach my $s (sort keys %per_service) { $l->trace("SERVICE: $s"); my $data = $per_service{ $s }; my @triples; my @ids; foreach my $r (sort { $ids{ $a } <=> $ids{ $b } } keys %$data) { my $t = $triples{ $r }; push(@ids, $ids{ $r }); push(@triples, $t); $l->trace("\tTRIPLE $ids{$r}: " . $t->sse); my @plans = @{ $data->{ $r } }; foreach my $p (@plans) { $l->trace("\t\tPLAN: " . $p->sse); } } my ($join) = $self->_triple_join_plans( $context, \@triples, %args ); my ($plan, $algebras) = @$join; my $size = scalar(@$algebras); my $algebra = ($size > 1) ? RDF::Query::Algebra::BasicGraphPattern->new( @$algebras ) : $algebras->[0]; $plan->label('algebra', $algebra); my $service = RDF::Query::Plan::Service->new_from_plan( $s, $plan, $context ); push(@service_plans, { service => $s, plan => $service, size => $size, coverage => [sort { $a <=> $b } @ids] }); } my %plans_by_coverage; foreach my $sp (@service_plans) { my @cover = @{ $sp->{ coverage } }; my $data = \%plans_by_coverage; while (@cover) { my $c = shift(@cover); $data = ($data->{ $c } ||= {}); } $data->{ '_service' } = $sp; } my @plans; my $full_coverage = join('', 0..$#triples); my @join_service_plans = sort { $b->{size} <=> $a->{size} } grep { $_->{size} >= 2 } @service_plans; SP: foreach my $sp (@join_service_plans) { $l->trace("----------------------->"); my $plan = $sp->{plan}; my $coverage = join('', @{$sp->{coverage}}); $l->trace("trying service $sp->{service} with BGP coverage $coverage"); while ($coverage ne $full_coverage) { $l->trace("coverage ($coverage) isn't full yet (needs to be $full_coverage)"); my $needed = $full_coverage; foreach my $c (split(//, $coverage)) { $needed =~ s/$c//; } my @needed = split('', $needed); # XXX this is where things go naive. ideally, we would start with # XXX any triple that yielded the optimal bin packing of plans to # XXX produce full coverage, but instead we start with the lowest # XXX numbered triple, and use a greedy search from there. my $start = shift(@needed); $l->trace("starting remote BGP with triple $start"); $coverage .= $start; my $access_key = $start; my $data = $plans_by_coverage{ $start }; while (@needed and ref($data->{ $needed[0] })) { my $c = shift(@needed); $access_key .= $c; $l->trace("adding triple $c to the current remote BGP"); $coverage .= $c; $data = $data->{ $c }; } unless (exists $data->{ '_service' }) { $l->trace("the current plan reached a dead end with key '$access_key': " . Dumper($data)); next SP; } $l->trace("no more triples in this remote BGP"); my $join_plan = $data->{ '_service' }{'plan'}; Carp::confess Dumper($full_coverage, $coverage, $data) unless ref($join_plan); $plan = RDF::Query::Plan::Join::NestedLoop->new( $plan, $join_plan, 0, {} ); $coverage = join('', sort split(//, $coverage)); } push(@plans, $plan); $l->trace("<-------------"); } if (@plans) { my $count = scalar(@plans); $l->debug("returning $count possible QEPs for optimistic BGP"); return map {[$_, $triples]} @plans; } else { return; } } =item C<< label_plan_with_services ( $plan, $context ) >> Labels the supplied plan object with the URIs of applicable services that are capable of answering the query represented by the plan. =cut sub label_plan_with_services { my $self = shift; my $plan = shift; my $context = shift; my $query = $context->query; my @sds = $query->services; my $l = Log::Log4perl->get_logger('rdf.query.federate.plan'); if ($plan->isa('RDF::Query::Plan::Triple')) { my @services; foreach my $sd (@sds) { if ($sd->answers_triple_pattern( $plan->triple )) { push(@services, $sd); } } # $plan might have already been labeled with services, in which case # we should just assume the existing label is correct, and save ourselves # the work of re-labeling if (@services and not($plan->label( 'services' ))) { if ($l->is_debug) { $l->debug( "SERVICES that can handle pattern: " . $plan->triple->sse . "\n\t" . join("\n\t", map { $_->url } @services) ); } $plan->label( services => [ map { $_->url } @services ] ); } } elsif ($plan->isa('RDF::Query::Plan::Join')) { $self->label_plan_with_services($_, $context) for ($plan->lhs, $plan->rhs); my $lhs = $plan->lhs->label( 'services' ) || []; my $rhs = $plan->rhs->label( 'services' ) || []; my $set = Set::Scalar->new(@$lhs)->intersection(Set::Scalar->new(@$rhs)); if (my @members = $set->members) { $plan->label( services => [ @members ] ); } } elsif ($plan->isa('RDF::Query::Plan::ThresholdUnion')) { my $dplan = $plan->default; $self->label_plan_with_services($dplan, $context); } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =head1 COPYRIGHT Copyright (c) 2005-2009 Gregory Todd Williams. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut RDF-Query-2.910/lib/RDF/Query/Expression/Alias.pm000644 000765 000024 00000004345 12173312155 021354 0ustar00gregstaff000000 000000 # RDF::Query::Expression::Alias # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression::Alias - Class for aliasing expressions with variable names =head1 VERSION This document describes RDF::Query::Expression::Alias version 2.910. =cut package RDF::Query::Expression::Alias; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Expression); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< name >> Returns the variable name of the aliased expression. =cut sub name { my $self = shift; return $self->alias->name; } =item C<< alias >> Returns the variable object of the aliased expression. =cut sub alias { my $self = shift; my ($alias) = $self->operands; return $alias; } =item C<< expression >> Returns the expression object of the aliased expression. =cut sub expression { my $self = shift; return ($self->operands)[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; return sprintf( '(alias ?%s %s)', $self->name, $self->expression->sse( $context ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $alias = $self->alias; my $expr = $self->expression; return sprintf("(%s AS %s)", $expr->as_sparql, $alias->as_sparql); } =item C<< evaluate ( $query, \%bound, $context ) >> Evaluates the expression using the supplied bound variables. Will return a RDF::Query::Node object. =cut sub evaluate { my $self = shift; my $query = shift; my $bound = shift; my $ctx = shift; my $expr = $self->expression; my $value = $query->var_or_expr_value( $bound, $expr, $ctx ); return $value; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Expression/Binary.pm000644 000765 000024 00000016002 12173312155 021540 0ustar00gregstaff000000 000000 # RDF::Query::Expression::Binary # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression::Binary - Algebra class for binary expressions =head1 VERSION This document describes RDF::Query::Expression::Binary version 2.910. =cut package RDF::Query::Expression::Binary; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Expression); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; return sprintf( '(%s %s %s)', $self->op, map { $_->sse( $context ) } $self->operands, ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $op = $self->op; $op = '=' if ($op eq '=='); return sprintf("(%s $op %s)", map { $_->as_sparql( $context, $indent ) } $self->operands); } =item C<< evaluate ( $query, \%bound ) >> Evaluates the expression using the supplied bound variables. Will return a RDF::Query::Node object. =cut sub evaluate { my $self = shift; my $query = shift; my $bound = shift; my $l = Log::Log4perl->get_logger("rdf.query.expression.binary"); my $op = $self->op; my @operands = $self->operands; my ($lhs, $rhs) = map { throw RDF::Query::Error::ExecutionError ( -text => "error in evaluating operands to binary $op" ) unless (blessed($_)); $_->isa('RDF::Query::Algebra') ? $_->evaluate( $query, $bound, @_ ) : ($_->isa('RDF::Trine::Node::Variable')) ? $bound->{ $_->name } : $_ } @operands; $l->debug("Binary Operator '$op': " . Dumper($lhs, $rhs)); ### This does overloading of infix<+> on literal values to perform string concatenation # if ($op eq '+') { # if (blessed($lhs) and $lhs->isa('RDF::Query::Node::Literal') and blessed($rhs) and $rhs->isa('RDF::Query::Node::Literal')) { # if (not($lhs->has_datatype) and not($rhs->has_datatype)) { # my $value = $lhs->literal_value . $rhs->literal_value; # return RDF::Query::Node::Literal->new( $value ); # } # } # } if ($op =~ m#^[-+/*]$#) { if (blessed($lhs) and blessed($rhs) and $lhs->isa('RDF::Query::Node::Literal') and $rhs->isa('RDF::Query::Node::Literal') and $lhs->is_numeric_type and $rhs->is_numeric_type) { my $type = $self->promote_type( $op, $lhs->literal_datatype, $rhs->literal_datatype ); my $value; if ($op eq '+') { my $lhsv = $lhs->numeric_value; my $rhsv = $rhs->numeric_value; if (defined($lhsv) and defined($rhsv)) { $value = $lhsv + $rhsv; } else { throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<+> on non-numeric types"; } } elsif ($op eq '-') { my $lhsv = $lhs->numeric_value; my $rhsv = $rhs->numeric_value; if (defined($lhsv) and defined($rhsv)) { $value = $lhsv - $rhsv; } else { throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<-> on non-numeric types"; } } elsif ($op eq '*') { my $lhsv = $lhs->numeric_value; my $rhsv = $rhs->numeric_value; if (defined($lhsv) and defined($rhsv)) { $value = $lhsv * $rhsv; } else { throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<*> on non-numeric types"; } } elsif ($op eq '/') { my $lhsv = $lhs->numeric_value; my $rhsv = $rhs->numeric_value; my ($lt, $rt) = ($lhs->literal_datatype, $rhs->literal_datatype); if ($lt eq $rt and $lt eq 'http://www.w3.org/2001/XMLSchema#integer') { $type = 'http://www.w3.org/2001/XMLSchema#decimal'; } if (defined($lhsv) and defined($rhsv)) { if ($rhsv == 0) { throw RDF::Query::Error::FilterEvaluationError -text => "Illegal division by zero"; } $value = $lhsv / $rhsv; } else { throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix: on non-numeric types"; } } else { throw RDF::Query::Error::ExecutionError -text => "Unrecognized binary operator '$op'"; } return RDF::Query::Node::Literal->new( $value, undef, $type, 1 ); } else { throw RDF::Query::Error::ExecutionError -text => "Numeric binary operator '$op' with non-numeric data"; } } elsif ($op =~ m#^([<>]=?)|!?=$#) { my @types = qw(RDF::Query::Node::Literal RDF::Query::Node::Resource RDF::Query::Node::Blank); if ($op =~ /[<>]/) { # if it's a relational operation other than equality testing, # the two nodes must be of the same type. my $ok = 0; foreach my $type (@types) { $ok ||= 1 if ($lhs->isa($type) and $rhs->isa($type)); } if (not($ok) and not($RDF::Query::Node::Literal::LAZY_COMPARISONS)) { throw RDF::Query::Error::TypeError -text => "Attempt to compare two nodes of different types."; } } my $bool; if ($op eq '<') { $bool = ($lhs < $rhs); } elsif ($op eq '<=') { $bool = ($lhs <= $rhs); } elsif ($op eq '>') { $bool = ($lhs > $rhs); } elsif ($op eq '>=') { $bool = ($lhs >= $rhs); } elsif ($op eq '==') { $bool = ($lhs == $rhs); } elsif ($op eq '!=') { $bool = ($lhs != $rhs); } else { throw RDF::Query::Error::ExecutionError -text => "Unrecognized binary operator '$op'"; } my $value = ($bool) ? 'true' : 'false'; $l->debug("-> $value"); return RDF::Query::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); } else { $l->logdie("Unknown operator: $op"); } } my $xsd = 'http://www.w3.org/2001/XMLSchema#'; my %integer_types = map { join('', $xsd, $_) => 1 } qw(nonPositiveInteger nonNegativeInteger positiveInteger negativeInteger short unsignedShort byte unsignedByte long unsignedLong); my %rel = ( "${xsd}integer" => 0, "${xsd}int" => 1, "${xsd}unsignedInt" => 2, "${xsd}nonPositiveInteger" => 3, "${xsd}nonNegativeInteger" => 4, "${xsd}positiveInteger" => 5, "${xsd}negativeInteger" => 6, "${xsd}short" => 7, "${xsd}unsignedShort" => 8, "${xsd}byte" => 9, "${xsd}unsignedByte" => 10, "${xsd}long" => 11, "${xsd}unsignedLong" => 12, "${xsd}decimal" => 13, "${xsd}float" => 14, "${xsd}double" => 15, ); =item C<< promote_type ( $op, $lhs_datatype, $rhs_datatype ) >> Returns the XSD type URI (as a string) for the resulting value of performing the supplied operation on arguments of the indicated XSD types. =cut sub promote_type { my $self = shift; my $op = shift; no warnings 'uninitialized'; my @types = sort { $rel{$b} <=> $rel{$a} } @_; my $type = $types[0]; $type = "${xsd}integer" if ($integer_types{ $type }); return $type; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Expression/Function.pm000644 000765 000024 00000017231 12173312155 022106 0ustar00gregstaff000000 000000 # RDF::Query::Expression::Function # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression::Function - Class for Function expressions =head1 VERSION This document describes RDF::Query::Expression::Function version 2.910. =cut package RDF::Query::Expression::Function; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Expression); use RDF::Query::Error qw(:try); use Data::Dumper; use Scalar::Util qw(blessed reftype); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### our %FUNCTION_MAP = ( str => "STR", strdt => "STRDT", strlang => "STRLANG", lang => "LANG", langmatches => "LANGMATCHES", sameterm => "sameTerm", datatype => "DATATYPE", bound => "BOUND", isuri => "isURI", isiri => "isIRI", isblank => "isBlank", isliteral => "isLiteral", regex => "REGEX", iri => "IRI", uri => "IRI", bnode => "BNODE", in => "IN", notin => "NOT IN", if => "IF", 'logical-or' => "||", 'logical-and' => "&&", ); =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Expr structure. =cut sub new { my $class = shift; my $uri = shift; my @args = @_; unless (blessed($uri) and $uri->isa('RDF::Trine::Node::Resource')) { $uri = RDF::Query::Node::Resource->new( $uri ); } return $class->SUPER::new( $uri, @args ); } =item C<< uri >> Returns the URI of the function. =cut sub uri { my $self = shift; return $self->op; } =item C<< arguments >> Returns a list of the arguments to the function. =cut sub arguments { my $self = shift; return $self->operands; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $uri = $self->uri->uri_value; if ($uri =~ m/^(sop|sparql):(in|notin|str|strdt|strlang|if|iri|uri|bnode|lang|langmatches|sameTerm|datatype|regex|bound|is(URI|IRI|Blank|Literal))/i) { my $func = $2; return sprintf( '(%s %s)', $func, join(' ', map { $_->sse( $context ) } $self->arguments), ); } else { return sprintf( '(%s %s)', $self->uri->sse( $context ), join(' ', map { $_->sse( $context ) } $self->arguments), ); } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my @args = $self->arguments; my $uri = $self->uri->uri_value; my $func = ($uri =~ m/^(sop|sparql):(logical-and|logical-or|in|notin|str|strdt|strlang|if|iri|uri|bnode|lang|langmatches|sameTerm|datatype|regex|bound|is(URI|IRI|Blank|Literal))/i) ? $FUNCTION_MAP{ lc($2) } : $self->uri->as_sparql( $context, $indent ); if ($func eq 'IN' or $func eq 'NOT IN') { my $term = shift(@args); my $string = sprintf( "%s %s (%s)", $term->as_sparql( $context, $indent ), $func, join(', ', map { $_->as_sparql( $context, $indent ) } @args), ); return $string; } elsif ($func eq '||' or $func eq '&&') { my $string = sprintf( "(%s) $func (%s)", (map { $_->as_sparql( $context, $indent ) } @args), ); return $string; } else { my $string = sprintf( "%s(%s)", $func, join(', ', map { $_->as_sparql( $context, $indent ) } @args), ); return $string; } } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'FUNCTION'; } =item C<< qualify_uris ( \%namespaces, $base_uri ) >> Returns a new algebra pattern where all referenced Resource nodes representing QNames (ns:local) are qualified using the supplied %namespaces. =cut sub qualify_uris { my $self = shift; my $class = ref($self); my $ns = shift; my $base_uri = shift; my @args; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@args, $arg->qualify_uris( $ns, $base_uri )); } elsif (blessed($arg) and $arg->isa('RDF::Query::Node::Resource')) { my $uri = $arg->uri; if (ref($uri)) { my ($n,$l) = @$uri; unless (exists($ns->{ $n })) { throw RDF::Query::Error::QuerySyntaxError -text => "Namespace $n is not defined"; } my $resolved = RDF::Query::Node::Resource->new( join('', $ns->{ $n }, $l), $base_uri ); push(@args, $resolved); } else { push(@args, $arg); } } else { push(@args, $arg); } } return $class->new( @args ); } =item C<< evaluate ( $query, \%bound, $context ) >> Evaluates the expression using the supplied bound variables. Will return a RDF::Query::Node object. =cut sub evaluate { my $self = shift; my $query = shift || 'RDF::Query'; my $bound = shift; my $context = shift; my $active_graph = shift; my $uri = $self->uri; no warnings 'uninitialized'; my $uriv = $uri->uri_value; if ($uriv =~ /^sparql:logical-(.+)$/ or $uriv =~ /^sparql:(not)?in$/ or $uriv eq 'sparql:coalesce') { # logical operators must have their arguments passed lazily, because # some of them can still succeed even if some of their arguments throw # TypeErrors (e.g. true || fail ==> true). my @args = $self->arguments; my $args = sub { my $value = shift(@args); return unless (blessed($value)); my $val = 0; try { $val = $value->isa('RDF::Query::Expression') ? $value->evaluate( $query, $bound, $context, $active_graph ) : ($value->isa('RDF::Trine::Node::Variable')) ? $bound->{ $value->name } : $value; } otherwise {}; return $val || 0; }; my $func = $query->get_function( $uri ); my $value = $func->( $query, $args ); return $value; } elsif ($uriv =~ /^sparql:if$/) { my @args = $self->arguments; my $ebv = RDF::Query::Node::Resource->new( "sparql:ebv" ); my $expr = shift(@args); my $index = 1; my $ok = 1; try { my $exprval = $query->var_or_expr_value( $bound, $expr, $context ); my $func = RDF::Query::Expression::Function->new( $ebv, $exprval ); my $value = $func->evaluate( $query, {}, $context, $active_graph ); my $bool = ($value->literal_value eq 'true') ? 1 : 0; if ($bool) { $index = 0; } } catch RDF::Query::Error::TypeError with { $ok = 0; }; if ($ok) { my $expr2 = $args[$index]; return $query->var_or_expr_value( $bound, $expr2, $context ); } else { return; } } elsif ($uriv eq 'sparql:exists') { my $func = $query->get_function($uri); my ($ggp) = $self->arguments; return $func->( $query, $context, $bound, $ggp, $active_graph ); } else { my $model = ref($query) ? $query->{model} : undef; if (blessed($context)) { $model = $context->model; } my @args; if (ref($query)) { # localize the model in the query object (legacy code wants the model accessible from the query object) local($query->{model}) = $model; @args = map { $_->isa('RDF::Query::Algebra') ? $_->evaluate( $query, $bound, $context, $active_graph ) : ($_->isa('RDF::Trine::Node::Variable')) ? $bound->{ $_->name } : $_ } $self->arguments; } else { @args = map { $_->isa('RDF::Query::Algebra') ? $_->evaluate( $query, $bound, $context, $active_graph ) : ($_->isa('RDF::Trine::Node::Variable')) ? $bound->{ $_->name } : $_ } $self->arguments; } my $func = $query->get_function($uri); unless ($func) { throw RDF::Query::Error::ExecutionError -text => "Failed to get function for IRI $uri"; } my $value = $func->( $query, @args ); return $value; } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Expression/Nary.pm000644 000765 000024 00000002627 12173312155 021235 0ustar00gregstaff000000 000000 # RDF::Query::Expression::Nary # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression::Nary - Class for n-ary expressions =head1 VERSION This document describes RDF::Query::Expression::Nary version 2.910. =cut package RDF::Query::Expression::Nary; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Expression); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; return sprintf( '(%s %s)', $self->op, join(' ', map { $_->sse( $context ) } $self->operands), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $op = $self->op; my @args = map { $_->as_sparql( $context, $indent ) } $self->operands; return join(" $op ", @args); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Expression/Unary.pm000644 000765 000024 00000005213 12173312155 021414 0ustar00gregstaff000000 000000 # RDF::Query::Expression::Unary # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Expression::Unary - Class for unary expressions =head1 VERSION This document describes RDF::Query::Expression::Unary version 2.910. =cut package RDF::Query::Expression::Unary; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Expression); use Data::Dumper; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; return sprintf( '(%s %s)', $self->op, map { $_->sse( $context ) } $self->operands, ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $op = $self->op; return sprintf("($op %s)", map { $_->as_sparql( $context, $indent ) } $self->operands); } =item C<< evaluate ( $query, \%bound ) >> Evaluates the expression using the supplied bound variables. Will return a RDF::Query::Node object. =cut sub evaluate { my $self = shift; my $query = shift; my $bound = shift; my $context = shift; my $op = $self->op; my ($data) = $self->operands; if ($op eq '+' or $op eq '-') { my $l = $data->isa('RDF::Query::Algebra') ? $data->evaluate( $query, $bound, $context, @_ ) : ($data->isa('RDF::Query::Node::Variable')) ? $bound->{ $data->name } : $data; my $value; if ($op eq '+') { $value = $l->numeric_value; } elsif ($op eq '-') { $value = -1 * $l->numeric_value; } return RDF::Query::Node::Literal->new( $value, undef, $l->literal_datatype ); } elsif ($op eq '!') { my $alg = RDF::Query::Expression::Function->new( "sparql:ebv", $data ); my $bool = $alg->evaluate( $query, $bound, $context, @_ ); if ($bool->literal_value eq 'true') { return RDF::Query::Node::Literal->new( 'false', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); } else { return RDF::Query::Node::Literal->new( 'true', undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); } } else { warn "unknown unary op: $op"; throw RDF::Query::Error::ExecutionError -text => "Unknown unary op '$op'"; } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Compiler/SQL.pm000644 000765 000024 00000064030 12173312155 020372 0ustar00gregstaff000000 000000 # RDF::Query::Compiler::SQL # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Compiler::SQL - Compile a SPARQL query directly to SQL. =head1 VERSION This document describes RDF::Query::Compiler::SQL version 2.910. =head1 STATUS This module's API and functionality should be considered deprecated. If you need functionality that this module provides, please L. =cut package RDF::Query::Compiler::SQL; use strict; use warnings; no warnings 'redefine'; use RDF::Query::Error qw(:try); use Log::Log4perl; use List::Util qw(first); use Data::Dumper; use Math::BigInt; use Digest::MD5 ('md5'); #use Digest::Perl::MD5 (); #('md5'); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype); use RDF::Query::Error qw(:try); ###################################################################### my (@NODE_TYPE_TABLES, %NODE_TYPE_TABLES); our ($VERSION); BEGIN { $VERSION = '2.910'; @NODE_TYPE_TABLES = ( ['Resources', 'ljr', 'URI'], ['Literals', 'ljl', qw(Value Language Datatype)], ['Bnodes', 'ljb', qw(Name)] ); %NODE_TYPE_TABLES = map { $_->[0] => [ @{ $_ }[1 .. $#{ $_ }] ] } @NODE_TYPE_TABLES; } ###################################################################### use constant INDENT => "\t"; =head1 METHODS =over 4 =cut =item C<< new ( $parse_tree ) >> Returns a new compiler object. =cut sub new { my $class = shift; my $parsed = shift; my $model = shift; my $l = Log::Log4perl->get_logger("rdf.query.compiler.sql"); my $stable; if ($model) { my $mhash = _mysql_hash( $model ); $l->debug("Model: $model => $mhash\n"); $stable = "Statements${mhash}"; } else { $stable = 'Statements'; } my $self = bless( { parsed => $parsed, stable => $stable, vars => {}, from => [], where => [], }, $class ); return $self; } =item C<< compile () >> Returns a SQL query string for the specified parse tree. =cut sub compile { my $self = shift; my $parsed = $self->{parsed}; my $sql; try { my $method = uc $parsed->{'method'}; if ($method eq 'SELECT') { $sql = $self->emit_select(); } else { throw RDF::Query::Error::CompilationError( -text => "SQL compilation of $method queries not yet implemented." ); } } catch RDF::Query::Error::CompilationError with { my $err = shift; throw $err; }; return $sql; } =item C<< emit_select >> Returns a SQL query string representing the query. =cut sub emit_select { my $self = shift; my $parsed = $self->{parsed}; my $level = \do { my $a = 0 }; my @vars = map { $_->name } @{ $parsed->{variables} }; my %select_vars = map { $_ => 1 } @vars; $self->patterns2sql( $parsed->{'triples'}, $level ); my ($varcols, @cols) = $self->add_variable_values_joins; my $vars = $self->{vars}; my $from = $self->{from}; my $where = $self->{where}; my $options = $self->{options} || {}; my $unique = $options->{'distinct'}; my $from_clause; foreach my $f (@$from) { $from_clause .= ",\n" . INDENT if ($from_clause and $from_clause =~ m/[^(]$/ and $f !~ m/^([)]|LEFT JOIN)/); $from_clause .= $f; } my $where_clause = @$where ? "WHERE\n" . INDENT . join(" AND\n" . INDENT, @$where) : ''; my @sql = ( "SELECT" . ($unique ? ' DISTINCT' : ''), INDENT . join(",\n" . INDENT, @cols), "FROM", INDENT . $from_clause, $where_clause, ); push(@sql, $self->order_by_clause( $varcols, $level ) ); push(@sql, $self->limit_clause( $options ) ); my $sql = join("\n", grep {length} @sql); return $sql; } =item C<< limit_clause >> Returns a SQL LIMIT clause, or an empty string if the query does not need limiting. =cut sub limit_clause { my $self = shift; my $options = shift; if (my $limit = $options->{limit}) { return "LIMIT ${limit}"; } else { return ""; } } =item C<< order_by_clause >> Returns a SQL ORDER BY clause, or an empty string if the query does not use ordering. =cut sub order_by_clause { my $self = shift; my $varcols = shift; my $level = shift || \do{ my $a = 0 }; my $vars = $self->{vars}; my $options = $self->{options} || {}; my %variable_value_cols = %$varcols; my $sql = ''; if ($options->{orderby}) { my $data = $options->{orderby}[0]; my ($dir, @operands) = @$data; if (scalar(@operands) > 1) { throw RDF::Query::Error::CompilationError( -text => "Can't sort by more than one column yet." ); } my $sort = $operands[0]; if (blessed($sort) and $sort->type eq 'VAR') { my $var = $sort->name; my @cols = $self->variable_columns( $var ); $sql .= "ORDER BY\n" . INDENT . join(', ', map { "$_ $dir" } @cols ); } elsif (blessed($sort) and $sort->type eq 'FUNCTION') { my $uri = $self->qualify_uri( $sort->uri ); my $col = $self->expr2sql( $sort, $level ); my @sort; foreach my $var (keys %$vars) { my ($l_sort_col, $r_sort_col, $b_sort_col) = @{ $variable_value_cols{ $var } }; my $varcol = $vars->{ $var }; if ($col =~ /${varcol}/) { my ($l, $r, $b) = ($col) x 3; $l =~ s/$varcol/${l_sort_col}/; $r =~ s/$varcol/${r_sort_col}/; $b =~ s/$varcol/${b_sort_col}/; push(@sort, "$l $dir, $r $dir, $b $dir"); last; } } unless (@sort) { push(@sort, "${col} $dir"); } $sql .= "ORDER BY\n" . INDENT . join(', ', @sort); } else { throw RDF::Query::Error::CompilationError( -text => "Can't sort by $$data[1][0] yet." ); } } return $sql; } =item C<< variable_columns ( $var ) >> Given a variable name, returns the set of column aliases that store the values for the column (values for Literals, URIs, and Blank Nodes). =cut sub variable_columns { my $self = shift; my $var = shift; return map { "${var}_$_" } (qw(Value URI Name)); } =item C<< add_variable_values_joins >> Modifies the query by adding LEFT JOINs to the tables in the database that contain the node values (for literals, resources, and blank nodes). =cut sub add_variable_values_joins { my $self = shift; my $l = Log::Log4perl->get_logger("rdf.query.algebra.service"); my $parsed = $self->{parsed}; my @vars = map { $_->name } @{ $parsed->{variables} }; my %select_vars = map { $_ => 1 } @vars; my %variable_value_cols; my $vars = $self->{vars}; my $from = $self->{from}; my $where = $self->{where}; my @cols; my $uniq_count = 0; my (%seen_vars, %seen_joins); foreach my $var (grep { not $seen_vars{ $_ }++ } (@vars, keys %$vars)) { my $col = $vars->{ $var }; unless ($col) { throw RDF::Query::Error::CompilationError "*** Nothing is known about the variable ?${var}"; } my $col_table = (split(/[.]/, $col))[0]; my ($count) = ($col_table =~ /\w(\d+)/); $l->debug("var: $var\t\tcol: $col\t\tcount: $count\t\tunique count: $uniq_count\n"); push(@cols, "${col} AS ${var}_Node") if ($select_vars{ $var }); foreach (@NODE_TYPE_TABLES) { my ($table, $alias, @join_cols) = @$_; foreach my $jc (@join_cols) { my $column_real_name = "${alias}${uniq_count}.${jc}"; my $column_alias_name = "${var}_${jc}"; push(@cols, "${column_real_name} AS ${column_alias_name}"); push( @{ $variable_value_cols{ $var } }, $column_real_name); foreach my $i (0 .. $#{ $where }) { if ($where->[$i] =~ /\b$column_alias_name\b/) { $where->[$i] =~ s/\b${column_alias_name}\b/${column_real_name}/g; } } } } foreach my $i (0 .. $#{ $from }) { my $f = $from->[ $i ]; next if ($from->[ $i ] =~ m/^[()]$/); my ($alias) = ($f =~ m/Statements\d* (\w\d+)/); #split(/ /, $f))[1]; if ($alias eq $col_table) { # my (@tables, @where); foreach (@NODE_TYPE_TABLES) { my ($vtable, $vname) = @$_; my $valias = join('', $vname, $uniq_count); next if ($seen_joins{ $valias }++); # push(@tables, "${vtable} ${valias}"); # push(@where, "${col} = ${valias}.ID"); $f .= " LEFT JOIN ${vtable} ${valias} ON (${col} = ${valias}.ID)"; } # my $join = sprintf("LEFT JOIN (%s) ON (%s)", join(', ', @tables), join(' AND ', @where)); # $from->[ $i ] = join(' ', $f, $join); $from->[ $i ] = $f; next; } } $uniq_count++; } return (\%variable_value_cols, @cols); } =item C<< patterns2sql ( \@triples, \$level, %args ) >> Builds the SQL query in instance data from the supplied C<@triples>. C<$level> is used as a unique identifier for recursive calls. C<%args> may contain callback closures for the following keys: 'where_hook' 'from_hook' When present, these closures are used to add SQL FROM and WHERE clauses to the query instead of adding them directly to the object's instance data. =cut sub patterns2sql { my $self = shift; my $triples = shift; my $level = shift || \do{ my $a = 0 }; my %args = @_; # my %vars = scalar(@_) ? %{ $_[0] } : (); my $parsed = $self->{parsed}; my $parsed_vars = $parsed->{variables}; my %queryvars = map { $_->name => 1 } @$parsed_vars; # my (@from, @where); my $from = $self->{from}; my $where = $self->{where}; my $vars = $self->{vars}; my $add_where = sub { my $w = shift; if (my $hook = $args{ where_hook }) { push(@$where, $hook->( $w )); } else { push(@$where, $w); } return $w; }; my $add_from = sub { my $f = shift; if (my $hook = $args{ from_hook }) { push(@$from, $hook->( $f )); } else { push(@$from, $f); } return $f; }; my $triple = shift(@$triples); Carp::confess "unblessed atom: " . Dumper($triple) unless (blessed($triple)); if ($triple->isa('RDF::Query::Algebra::Triple') or $triple->isa('RDF::Query::Algebra::Quad')) { my $quad = $triple->isa('RDF::Query::Algebra::Quad'); my @posmap = ($quad) ? qw(subject predicate object context) : qw(subject predicate object); # $add_from->('('); my $table = "s${$level}"; my $stable = $self->{stable}; $add_from->( "${stable} ${table}" ); foreach my $method (@posmap) { my $node = $triple->$method(); my $pos = $method; my $col = "${table}.${pos}"; if ($node->isa('RDF::Query::Node::Variable')) { my $name = $node->name; if (exists $vars->{ $name }) { my $existing_col = $vars->{ $name }; $add_where->( "$col = ${existing_col}" ); } else { $vars->{ $name } = $col; } } elsif ($node->isa('RDF::Query::Node::Resource')) { my $uri = $node->uri_value; my $id = $self->_mysql_node_hash( $node ); $id =~ s/\D//; $add_where->( "${col} = $id" ); } elsif ($node->isa('RDF::Query::Node::Blank')) { my $id = $node->blank_identifier; my $b = "b${$level}"; $add_from->( "Bnodes $b" ); $add_where->( "${col} = ${b}.ID" ); $add_where->( "${b}.Name = '$id'" ); } elsif ($node->isa('RDF::Query::Node::Literal')) { my $id = $self->_mysql_node_hash( $node ); $id =~ s/\D//; $add_where->( "${col} = $id" ); } else { throw RDF::Query::Error::CompilationError( -text => "Unknown node type: " . Dumper($node) ); } } # $add_from->(')'); } else { if ($triple->isa('RDF::Query::Algebra::Optional')) { throw RDF::Query::Error::CompilationError( -text => "SQL compilation of OPTIONAL blocks is currently broken" ); } elsif ($triple->isa('RDF::Query::Algebra::NamedGraph')) { $self->patterns2sql( [ $triple->pattern ], $level, %args ); # my $graph = $triple->graph; # my $pattern = $triple->pattern; # if ($graph->isa('RDF::Query::Node::Variable')) { # my $name = $graph->name; # my $context; # my $hook = sub { # my $f = shift; # if ($f =~ /^Statements/i) { # my $alias = (split(/ /, $f))[1]; # if (defined($context)) { # $context =~ s/\D//; # $add_where->( "${alias}.Context = ${context}" ); # } else { # $context = "${alias}.Context"; # $vars->{ $name } = $context; # } # } # return $f; # }; # $self->patterns2sql( [ $pattern ], $level, from_hook => $hook ); # } else { # my $hash = $self->_mysql_node_hash( $graph ); # my $hook = sub { # my $f = shift; # if ($f =~ /^Statements/i) { # my $alias = (split(/ /, $f))[1]; # $hash =~ s/\D//; # $add_where->( "${alias}.Context = ${hash}" ); # } # return $f; # }; # $self->patterns2sql( [ $pattern ], $level, from_hook => $hook ); # } } elsif ($triple->isa('RDF::Query::Algebra::Filter')) { ++$$level; my $expr = $triple->expr; my $pattern = $triple->pattern; $self->expr2sql( $expr, $level, from_hook => $add_from, where_hook => $add_where ); ++$$level; $self->patterns2sql( [ $pattern ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::BasicGraphPattern')) { ++$$level; $self->patterns2sql( [ $triple->triples ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::GroupGraphPattern')) { ++$$level; $self->patterns2sql( [ $triple->patterns ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::Distinct')) { $self->{options}{distinct} = 1; my $pattern = $triple->pattern; $self->patterns2sql( [ $pattern ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::Limit')) { $self->{options}{limit} = $triple->limit; my $pattern = $triple->pattern; $self->patterns2sql( [ $pattern ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::Offset')) { $self->{options}{offset} = $triple->offset; my $pattern = $triple->pattern; $self->patterns2sql( [ $pattern ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::Sort')) { $self->{options}{orderby} = [ $triple->orderby ]; my $pattern = $triple->pattern; $self->patterns2sql( [ $pattern ], $level, %args ); } elsif ($triple->isa('RDF::Query::Algebra::Project')) { my $pattern = $triple->pattern; $self->patterns2sql( [ $pattern ], $level, %args ); } else { throw RDF::Query::Error::CompilationError( -text => "Unknown pattern type '$triple' in SQL compilation." ); } } if (scalar(@$triples)) { ++$$level; $self->patterns2sql( $triples, $level ); } return; # return (\%vars, \@from, \@where); } =item C<< expr2sql ( $expression, \$level, %args ) >> Returns a SQL expression for the supplied query C<$expression>. C<$level> is used as a unique identifier for recursive calls. C<%args> may contain callback closures for the following keys: 'where_hook' 'from_hook' When present, these closures are used to add necessary SQL FROM and WHERE clauses to the query. =cut sub expr2sql { my $self = shift; my $expr = shift; my $level = shift || \do{ my $a = 0 }; my %args = @_; my $equality = do { no warnings 'uninitialized'; ($args{'equality'} eq 'rdf') ? 'rdf' : 'xpath' }; my $from = $self->{from}; my $where = $self->{where}; my $vars = $self->{vars}; my $sql; my $add_where = sub { my $w = shift; $sql ||= $w; if (my $hook = $args{ where_hook }) { $hook->( $w ); } }; my $add_from = sub { my $f = shift; if (my $hook = $args{ from_hook }) { $hook->( $f ); } }; my $parsed = $self->{parsed}; my $parsed_vars = $parsed->{variables}; my %queryvars = map { $_->name => 1 } @$parsed_vars; Carp::confess unless ref($expr); my $blessed = blessed($expr); if ($blessed and $expr->isa('RDF::Query::Node')) { if ($expr->isa('RDF::Query::Node::Literal')) { my $literal = $expr->literal_value; my $dt = $expr->literal_datatype; my $hash = $self->_mysql_node_hash( $expr ); if ($equality eq 'rdf') { $literal = $hash; } else { if (defined($dt)) { my $uri = $dt; my $func = $self->get_function( $self->qualify_uri( $uri ) ); if ($func) { my ($v, $f, $w) = $func->( $self, $parsed_vars, $level, RDF::Query::Node::Literal->new($literal) ); $literal = $w->[0]; } else { $literal = qq("${literal}"); } } else { $literal = qq('${literal}'); } } $add_where->( $literal ); } elsif ($expr->isa('RDF::Query::Node::Blank')) { my $hash = $self->_mysql_node_hash( $expr ); $add_where->( $hash ); } elsif ($expr->isa('RDF::Query::Node::Resource')) { my $uri = $self->_mysql_node_hash( $expr ); $add_where->( $uri ); } elsif ($expr->isa('RDF::Query::Node::Variable')) { my $name = $expr->name; my $col = $vars->{ $name }; no warnings 'uninitialized'; $add_where->( qq(${col}) ); } } elsif ($blessed and $expr->isa('RDF::Query::Expression::Function')) { my $uri = $expr->uri->uri_value; my $func = $self->get_function( $uri ); if ($func) { my ($v, $f, $w) = $func->( $self, $parsed_vars, $level, $expr->arguments ); foreach my $key (keys %$v) { my $val = $v->{ $key }; $vars->{ $key } = $val unless (exists($vars->{ $key })); } foreach my $f (@$f) { $add_from->( @$f ); } foreach my $w (@$w) { $add_where->( $w ); } } else { throw RDF::Query::Error::CompilationError( -text => "Unknown custom function $uri in FILTER." ); } } elsif ($blessed and $expr->isa('RDF::Query::Expression')) { my $op = $expr->op; my @args = $expr->operands; if ($op eq '!') { if ($args[0]->isa('RDF::Query::Expression::Function')) { if ($args[0]->uri->uri_value eq 'sparql:isbound') { my $expr = RDF::Query::Expression::Function->new( RDF::Query::Node::Resource->new('rdfquery:isNotBound'), $args[0]->arguments ); $self->expr2sql( $expr, $level, %args ); } } } else { if ($op =~ m#^(=|==|!=|[<>]=?|[*]|/|[-+])$#) { $op = '<>' if ($op eq '!='); $op = '=' if ($op eq '=='); my ($a, $b) = @args; my $a_type = $a->type; my $b_type = $b->type; try { if ($op eq '=') { if ($a_type eq 'VAR' and $b_type eq 'VAR') { # comparing equality on two type-unknown variables. # could need rdf-term equality, so punt to the # catch block below. throw RDF::Query::Error::ComparisonError; } } foreach my $data ([$a_type, 'LHS'], [$b_type, 'RHS']) { my ($type, $side) = @$data; unless ($type =~ m/^(VAR|LITERAL|FUNCTION)$/) { if ($op =~ m/^!?=$/) { # throw to the catch block below. throw RDF::Query::Error::ComparisonError( -text => "Using comparison operator '${op}' on unknown node type requires RDF-Term semantics." ); } else { # throw error out of the compiler. throw RDF::Query::Error::CompilationError( -text => "Cannot use the comparison operator '${op}' on a ${side} ${type} node." ); } } } if ($a_type eq 'VAR') { ++$$level; my $var_name_a = $self->expr2sql( $a, $level, equality => $equality ); my $sql_a = "(SELECT value FROM Literals WHERE ${var_name_a} = ID LIMIT 1)"; if ($b_type eq 'VAR') { # ?var cmp ?var ++$$level; my $var_name_b = $self->expr2sql( $b, $level, equality => $equality ); my $sql_b = "(SELECT value FROM Literals WHERE ${var_name_b} = ID LIMIT 1)"; $add_where->( "${sql_a} ${op} ${sql_b}" ); } else { # ?var cmp NODE ++$$level; my $sql_b = $self->expr2sql( $b, $level, equality => $equality ); $add_where->( "${sql_a} ${op} ${sql_b}" ); } } else { ++$$level; my $sql_a = $self->expr2sql( $a, $level, equality => $equality ); if ($b->[0] eq 'VAR') { # ?var cmp NODE ++$$level; my $var_name = $self->expr2sql( $b, $level, equality => $equality ); my $sql_b = "(SELECT value FROM Literals WHERE ${var_name} = ID LIMIT 1)"; $add_where->( "${sql_a} ${op} ${sql_b}" ); } else { # NODE cmp NODE ++$$level; my $sql_b = $self->expr2sql( $b, $level, equality => $equality ); $add_where->( "${sql_a} ${op} ${sql_b}" ); } } } catch RDF::Query::Error::ComparisonError with { # we can't compare these terms using the XPath semantics (for literals), # so fall back on RDF-Term semantics. my $err = shift; my @w; my $where_hook = sub { my $w = shift; push(@w, $w); return; }; foreach my $expr (@args) { $self->expr2sql( $expr, $level, %args, %args, equality => 'rdf', where_hook => $where_hook ) } $add_where->("$w[0] ${op} $w[1]"); }; } elsif ($op eq '&&') { foreach my $expr (@args) { $self->expr2sql( $expr, $level, %args ) } } elsif ($op eq '||') { my @w; my $where_hook = sub { my $w = shift; push(@w, $w); return; }; foreach my $expr (@args) { $self->expr2sql( $expr, $level, %args, where_hook => $where_hook ) } my $where = '(' . join(' OR ', map { qq<($_)> } @w) . ')'; $add_where->( $where ); } else { throw RDF::Query::Error::CompilationError( -text => "SQL compilation of FILTER($op) queries not yet implemented." ); } } } return $sql; } =item C<< _mysql_hash ( $data ) >> Returns a hash value for the supplied C<$data> string. This value is computed using the same algorithm that Redland's mysql storage backend uses. =cut sub _mysql_hash { my $data = shift; my @data = unpack('C*', md5( $data )); my $sum = Math::BigInt->new('0'); # my $count = 0; foreach my $count (0 .. 7) { # while (@data) { my $data = Math::BigInt->new( $data[ $count ] ); #shift(@data); my $part = $data << (8 * $count); # warn "+ $part\n"; $sum += $part; } # continue { last if ++$count == 8 } # limit to 64 bits # warn "= $sum\n"; $sum =~ s/\D//; # get rid of the extraneous '+' that pops up under perl 5.6 return $sum; } =item C<< _mysql_node_hash ( $node ) >> Returns a hash value (computed by C<_mysql_hash> for the supplied C<$node>. The hash value is based on the string value of the node and the node type. =cut sub _mysql_node_hash { my $self = shift; my $node = shift; # my @node = @$node; # my ($type, $value) = splice(@node, 0, 2, ()); my $data; Carp::confess 'node a blessed node: ' . Dumper($node) unless blessed($node); if ($node->isa('RDF::Query::Node::Resource')) { my $value = $node->uri_value; if (ref($value)) { $value = $self->qualify_uri( $value ); } $data = 'R' . $value; } elsif ($node->isa('RDF::Query::Node::Blank')) { my $value = $node->blank_identifier; $data = 'B' . $value; } elsif ($node->isa('RDF::Query::Node::Literal')) { my $value = $node->literal_value; my $lang = $node->literal_value_language; my $dt = $node->literal_datatype; no warnings 'uninitialized'; $data = sprintf("L%s<%s>%s", $value, $lang, $dt); # warn "($data)"; } else { return undef; } my $hash = _mysql_hash( $data ); return $hash; } =item C<< qualify_uri ( $uri ) >> Returns a fully qualified URI from the supplied C<$uri>. C<$uri> may already be a qualified URI, or a parse tree for a qualified URI or QName. If C<$uri> is a QName, the namespaces defined in the query parse tree are used to fully qualify. =cut sub qualify_uri { my $self = shift; my $uri = shift; my $parsed = $self->{parsed}; if (ref($uri) and $uri->[0] eq 'URI') { $uri = $uri->[1]; } if (ref($uri)) { my ($abbr, $local) = @$uri; if (exists $parsed->{namespaces}{$abbr}) { my $ns = $parsed->{namespaces}{$abbr}; $uri = join('', $ns, $local); } else { throw RDF::Query::Error::ParseError ( -text => "Unknown namespace prefix: $abbr" ); } } return $uri; } =item C Associates the custom function C<$function> (a CODE reference) with the specified URI, allowing the function to be called by query FILTERs. =cut sub add_function { my $self = shift; my $uri = shift; my $code = shift; if (ref($self)) { $self->{'functions'}{$uri} = $code; } else { our %functions; $functions{ $uri } = $code; } } =item C If C<$uri> is associated with a query function, returns a CODE reference to the function. Otherwise returns C. =cut sub get_function { my $self = shift; my $uri = shift; our %functions; my $func = $self->{'functions'}{$uri} || $functions{ $uri }; return $func; } our %functions; BEGIN { $functions{ 'sparql:regex' } = sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my (@from, @where); my (@regex, @literal, @pattern); if (blessed($args[0]) and $args[0]->isa('RDF::Query::Node::Variable')) { my $name = $args[0]->name; push(@literal, "${name}_Value"); push(@literal, "${name}_URI"); push(@literal, "${name}_Name"); } else { push(@literal, $self->expr2sql( $args[0], $level )); } if ($args[1][0] eq 'VAR') { my $name = $args[0][1]; push(@pattern, "${name}_Value"); push(@pattern, "${name}_URI"); push(@pattern, "${name}_Name"); } else { push(@pattern, $self->expr2sql( $args[1], $level )); } foreach my $literal (@literal) { foreach my $pattern (@pattern) { push(@regex, sprintf(qq(%s REGEXP %s), $literal, $pattern)); } } push(@where, '(' . join(' OR ', @regex) . ')'); return ({}, \@from, \@where); }; $functions{ 'sparql:bound' } = sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my (@from, @where); my $literal = $self->expr2sql( $args[0], $level ); push(@where, sprintf(qq(%s IS NOT NULL), $literal)); return ({}, \@from, \@where); }; $functions{ 'rdfquery:isNotBound' } = sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my (@from, @where); my $literal = $self->expr2sql( $args[0], $level ); push(@where, sprintf(qq(%s IS NULL), $literal)); return ({}, \@from, \@where); }; $functions{ 'http://www.w3.org/2001/XMLSchema#integer' } = sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my (@from, @where); my $literal = $self->expr2sql( $args[0], $level ); push(@where, sprintf(qq((0 + %s)), $literal)); return ({}, \@from, \@where); }; $functions{ 'http://www.w3.org/2001/XMLSchema#double' } = $functions{ 'http://www.w3.org/2001/XMLSchema#decimal' } = sub { my $self = shift; my $parsed_vars = shift; my $level = shift || \do{ my $a = 0 }; my @args = @_; my (@from, @where); if ($args[0] eq 'FUNCTION') { Carp::confess; } my $literal = $self->expr2sql( $args[0], $level ); push(@where, sprintf(qq((0.0 + %s)), $literal)); return ({}, \@from, \@where); }; } 1; __END__ =back =head1 AUTHOR Gregory Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Aggregate.pm000644 000765 000024 00000013124 12173312155 021402 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Aggregate # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Aggregate - Algebra class for aggregate patterns =head1 VERSION This document describes RDF::Query::Algebra::Aggregate version 2.910. =cut package RDF::Query::Algebra::Aggregate; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Scalar::Util qw(blessed reftype); use Data::Dumper; use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(smap); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C [$op => $col] )> =item C [ $alias => [$op, \%options, @cols] ] )> Returns a new Aggregate structure. Groups by the named bindings in C<< @groupby >>, and returns new bindings for the named C<< $alias >> for the operation C<< $op >> on column C<< $col >>. C<< $op >> may be one of: COUNT, MIN, MAX, SUM. =cut sub new { my $class = shift; my $pattern = shift; my $groupby = shift; my @ops; if (scalar(@_) and ref($_[0]) and reftype($_[0]) eq 'HASH') { my $hash = shift; @ops = @{ $hash->{ 'expressions' } || [] }; } else { while (@_) { my ($alias, $data) = splice(@_,0,2,()); my $op = shift(@$data); my @data = ($op, {}, @$data); push(@ops, $alias, \@data); } @ops = @_; } return bless( [ $pattern, $groupby, \@ops ] ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my @ops = @{ $self->[2] }; return ($self->pattern, [ $self->groupby ], { expressions => \@ops }); } =item C<< pattern >> Returns the aggregates pattern. =cut sub pattern { my $self = shift; return $self->[0]; } =item C<< groupby >> Returns the aggregate's GROUP BY binding names. =cut sub groupby { my $self = shift; return @{ $self->[1] }; } =item C<< ops >> Returns a list of tuples as ARRAY refs containing C<< $alias, $op, @cols >>. =cut sub ops { my $self = shift; my @ops = @{ $self->[2] }; my @tuples; while (@ops) { my $alias = shift(@ops); my $data = shift(@ops); my ($op, $opts, @col) = @$data; push(@tuples, [$alias, $op, $opts, @col]); } return @tuples; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = ($context->{indent} ||= ' '); my @ops_sse; my @ops = $self->ops; foreach my $data (@ops) { my ($alias, $op, $opts, @cols) = @$data; my @col_strings = map { (not(blessed($_)) and $_ eq '*') ? '*' : $_->sse( $context, "${prefix}${indent}" ) } @cols; my $col_string = join(' ', @col_strings); if (@col_strings > 1) { $col_string = '(' . $col_string . ')'; } my %op_opts = %{ $opts || {} }; my @opts_keys = keys %op_opts; if (@opts_keys) { my $opt_string = '(' . join(' ', map { $_, qq["$op_opts{$_}"] } @opts_keys) . ')'; push(@ops_sse, sprintf('(alias "%s" (%s %s %s))', $alias, $op, $col_string, $opt_string)); } else { push(@ops_sse, sprintf('(alias "%s" (%s %s))', $alias, $op, $col_string)); } } my @group = $self->groupby; my $group = (@group) ? '(' . join(', ', map {$_->sse($context, $prefix)} @group) . ')' : ''; return sprintf( "(aggregate\n${prefix}${indent}%s\n${prefix}${indent}(%s)\n${prefix}${indent}%s)", $self->pattern->sse( $context, "${prefix}${indent}" ), join(', ', @ops_sse), $group, ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; return $self->pattern->as_sparql($context, $indent); } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, groupby => [ map { $_->as_hash } $self->groupby ], expressions => [ map { $_->as_hash } $self->ops ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'AGGREGATE'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @aliases = map { $_->[0] } $self->ops; return RDF::Query::_uniq( @aliases, $self->pattern->referenced_variables ); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @vars; # push(@vars, map { $_->[0] } $self->ops); foreach my $g ($self->groupby) { if (blessed($g)) { if ($g->isa('RDF::Query::Node::Variable')) { push(@vars, $g->name); } elsif ($g->isa('RDF::Query::Expression::Alias')) { push(@vars, $g->name); } } } return RDF::Query::_uniq(@vars); # return RDF::Query::_uniq( @aliases, $self->pattern->referenced_variables ); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; my @aliases = map { $_->[0] } $self->ops; return @aliases; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/BasicGraphPattern.pm000644 000765 000024 00000020270 12173312155 023055 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::BasicGraphPattern # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::BasicGraphPattern - Algebra class for BasicGraphPattern patterns =head1 VERSION This document describes RDF::Query::Algebra::BasicGraphPattern version 2.910. =cut package RDF::Query::Algebra::BasicGraphPattern; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(blessed refaddr reftype); use Carp qw(carp croak confess); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap swatch); ###################################################################### our ($VERSION); my %AS_SPARQL; BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new BasicGraphPattern structure. =cut sub new { my $class = shift; my @triples = @_; foreach my $t (@triples) { unless ($t->isa('RDF::Trine::Statement')) { throw RDF::Query::Error::QueryPatternError -text => "Patterns belonging to a BGP must be graph statements"; } } return bless( [ @triples ] ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->triples); } =item C<< triples >> Returns a list of triples belonging to this BGP. =cut sub triples { my $self = shift; return @$self; } =item C<< quads >> Returns a list of the (implicit) quads belonging to this BGP. =cut sub quads { my $self = shift; my @triples = $self->triples; my @quads; foreach my $t (@triples) { my @nodes = $t->nodes; foreach my $i (0 .. 3) { my $n = $nodes[ $i ]; if (not blessed($n)) { if ($i == 3) { $nodes[ $i ] = RDF::Trine::Node::Nil->new(); } else { $nodes[ $i ] = RDF::Query::Node::Variable->new(); } } } my $st = RDF::Trine::Statement::Quad->new( @nodes ); push(@quads, $st); } return @quads; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my @triples = sort map { $_->sse( $context ) } $self->triples; return sprintf( "(BGP\n${prefix}${indent}%s\n${prefix})", join("\n${prefix}${indent}", @triples) ); } =item C<< explain >> Returns a string serialization of the algebra appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}basic graph pattern\n"; foreach my $t ($self->triples) { $string .= "${indent}${s}" . $t->as_sparql . "\n"; } return $string; } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; if (exists $AS_SPARQL{ refaddr( $self ) }) { return $AS_SPARQL{ refaddr( $self ) }; } else { my $context = shift; # if (ref($context)) { # $context = { %$context }; # } my $indent = shift || ''; my @triples; foreach my $t ($self->triples) { push(@triples, $t->as_sparql( $context, $indent )); } my $string = join("\n${indent}", @triples); $AS_SPARQL{ refaddr( $self ) } = $string; return $string; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), patterns => [ map { $_->as_hash } $self->triples ], }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my @t = $self->triples; my @nodes = map { $_->as_spin( $model ) } @t; return @nodes; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'BGP'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq(map { $_->referenced_variables } $self->triples); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq(map { $_->potentially_bound } $self->triples); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return RDF::Query::_uniq(map { $_->definite_variables } $self->triples); } sub _referenced_blanks { my $self = shift; my %seen; foreach my $t ($self->triples) { my @blanks = $t->referenced_blanks; foreach my $b (@blanks) { $seen{ $b }++; } } return [keys %seen]; } =item C<< connected >> Returns true if the pattern is connected through shared variables, false otherwise. =cut sub connected { my $self = shift; my @triples = $self->triples; return 1 unless (scalar(@triples) > 1); my %index; my %variables; foreach my $i (0 .. $#triples) { my $t = $triples[ $i ]; $index{ $t->as_string } = $i; foreach my $n ($t->nodes) { next unless ($n->isa('RDF::Trine::Node::Variable')); push( @{ $variables{ $n->name } }, $t ); } } my @connected; foreach my $i (0 .. $#triples) { foreach my $j (0 .. $#triples) { $connected[ $i ][ $j ] = ($i == $j) ? 1 : 0; } } my %seen; my @queue = $triples[0]; while (my $t = shift(@queue)) { my $string = $t->as_string; next if ($seen{ $string }++); my @vars = map { $_->name } grep { $_->isa('RDF::Trine::Node::Variable') } $t->nodes; my @connected_to = map { @{ $variables{ $_ } } } @vars; foreach my $c (@connected_to) { my $cstring = $c->as_string; my $i = $index{$string}; my $k = $index{ $cstring }; my @conn = @{ $connected[$i] }; $conn[ $k ] = 1; foreach my $j (0 .. $#triples) { if ($conn[ $j ] == 1) { $connected[ $k ][ $j ] = 1; $connected[ $j ][ $k ] = 1; } } push(@queue, $c); } } foreach my $i (0 .. $#triples) { return 0 unless ($connected[0][$i] == 1); } return 1; } =item C<< subsumes ( $pattern ) >> Returns true if the bgp subsumes the pattern, false otherwise. =cut sub subsumes { my $self = shift; my $pattern = shift; if ($pattern->isa('RDF::Trine::Statement')) { foreach my $t ($self->triples) { return 1 if ($t->subsumes($pattern)); } return 0; } elsif ($pattern->isa('RDF::Query::Algebra::BasicGraphPattern')) { OUTER: foreach my $p ($pattern->triples) { foreach my $t ($self->triples) { next OUTER if ($t->subsumes($p)); } return 0; } return 1; } else { return 0; } } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my @bf; my %var_to_num; my %use_count; my $counter = 1; foreach my $t ($self->triples) { my $bf = $t->bf; if ($bf =~ /f/) { $bf = ''; foreach my $n ($t->nodes) { if ($n->isa('RDF::Query::Node::Variable')) { my $name = $n->name; my $num = ($var_to_num{ $name } ||= $counter++); $use_count{ $name }++; $bf .= "{${num}}"; } else { $bf .= 'b'; } } } push(@bf, $bf); } my $bf = join(',',@bf); if ($counter <= 10) { $bf =~ s/[{}]//g; } return $bf; } =item C<< clone >> =cut sub clone { my $self = shift; my $class = ref($self); return $class->new( map { $_->clone } $self->triples ); } =item C<< bind_variables ( \%bound ) >> Returns a new algebra pattern with variables named in %bound replaced by their corresponding bound values. =cut sub bind_variables { my $self = shift; my $class = ref($self); my $bound = shift; return $class->new( map { $_->bind_variables( $bound ) } $self->triples ); } sub DESTROY { my $self = shift; delete $AS_SPARQL{ refaddr( $self ) }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Clear.pm000644 000765 000024 00000006163 12173312155 020547 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Clear # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Clear - Algebra class for CLEAR operations =head1 VERSION This document describes RDF::Query::Algebra::Clear version 2.910. =cut package RDF::Query::Algebra::Clear; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new CLEAR structure. =cut sub new { my $class = shift; my $graph = shift; my $silent = shift; unless ($graph) { throw RDF::Query::Error::MethodInvocationError -text => "A graph argument is required in RDF::Query::Algebra::Clear->new"; $graph = RDF::Trine::Node::Nil->new; } return bless([$graph, $silent], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->graph, $self->silent); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $graph = $self->graph; my $string; if ($graph->is_nil) { $string = "CLEAR DEFAULT"; } elsif ($graph->uri_value =~ m'^tag:gwilliams@cpan[.]org,2010-01-01:RT:(NAMED|ALL)$') { $string = "CLEAR $1"; } else { $string = ($graph->is_nil) ? 'CLEAR GRAPH DEFAULT' : sprintf( "CLEAR GRAPH <%s>", $graph->uri_value ); } return $string; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $graph = $self->graph; my $string; if ($graph->is_nil) { $string = "(clear default)"; } elsif ($graph->uri_value =~ m'^tag:gwilliams@cpan[.]org,2010-01-01:RT:(NAMED|ALL)$') { $string = "(clear " . lc($1) . ")"; } else { $string = ($graph->is_nil) ? '(clear default)' : sprintf( "(clear <%s>)", $graph->uri_value ); } return $string; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< graph >> =cut sub graph { my $self = shift; return $self->[0]; } =item C<< silent >> =cut sub silent { my $self = shift; return $self->[1]; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Construct.pm000644 000765 000024 00000010317 12173312155 021501 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Construct # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Construct - Algebra class for construct query results =head1 VERSION This document describes RDF::Query::Algebra::Construct version 2.910. =cut package RDF::Query::Algebra::Construct; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $query_pattern, \@construct_triples ) >> Returns a new Sort structure. =cut sub new { my $class = shift; my $pattern = shift; my $triples = shift; return bless( [ $pattern, $triples ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my $triples = $self->triples; return ($pattern, $triples); } =item C<< pattern >> Returns the pattern used to produce the variable bindings used in graph construction. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< triples >> Returns an ARRAY ref of triples to be used in graph construction. =cut sub triples { my $self = shift; if (@_) { $self->[1] = shift; } return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my $triples = join("\n${prefix}${indent}${indent}", map { $_->sse( $context, "${prefix}${indent}${indent}" ) } @{$self->triples}); return sprintf( "(construct\n${prefix}${indent}(\n${prefix}${indent}${indent}%s\n${prefix}${indent})\n${prefix}${indent}%s)", $triples, $self->pattern->sse( $context, "${prefix}${indent}" ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; my $triples = $self->triples; my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @$triples ); my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $bgp ); my $force = $context->{ force_ggp_braces } || 0; my ($template, $pattern); { $context->{ force_ggp_braces } = $force + 1; $template = $ggp->as_sparql( $context, $indent ); } { $context->{ force_ggp_braces } = $force + 1; $pattern = $self->pattern->as_sparql( $context, $indent ); } my $string = sprintf( "CONSTRUCT %s\n${indent}WHERE %s", $template, $pattern, ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @{ $self->triples } ); return { type => lc($self->type), pattern => $self->pattern->as_hash, construct => $bgp->as_hash, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'CONSTRUCT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Copy.pm000644 000765 000024 00000005637 12173312155 020440 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Copy # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Copy - Algebra class for COPY operations =head1 VERSION This document describes RDF::Query::Algebra::Copy version 2.910. =cut package RDF::Query::Algebra::Copy; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new COPY structure. =cut sub new { my $class = shift; my $from = shift; my $to = shift; my $silent = shift || 0; return bless([$from, $to, $silent], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->from, $self->to, $self->silent); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $from = $self->from; my $to = $self->to; for ($from, $to) { if ($_->isa('RDF::Trine::Node::Nil')) { $_ = 'DEFAULT'; } else { $_ = '<' . $_->uri_value . '>'; } } my $string = sprintf( "COPY %s%s TO %s", ($self->silent ? 'SILENT ' : ''), $from, $to ); return $string; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $from = $self->from; my $to = $self->to; for ($from, $to) { if ($_->isa('RDF::Trine::Node::Nil')) { $_ = 'DEFAULT'; } else { $_ = '<' . $_->uri_value . '>'; } } my $string = sprintf( "(copy%s %s %s)", ($self->silent ? '-silent' : ''), $from, $to ); return $string; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< from >> =cut sub from { my $self = shift; return $self->[0]; } =item C<< to >> =cut sub to { my $self = shift; return $self->[1]; } =item C<< silent >> =cut sub silent { my $self = shift; return $self->[2]; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Create.pm000644 000765 000024 00000004621 12173312155 020721 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Create # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Create - Algebra class for CREATE GRAPH operations =head1 VERSION This document describes RDF::Query::Algebra::Create version 2.910. =cut package RDF::Query::Algebra::Create; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new CREATE GRAPH structure. =cut sub new { my $class = shift; my $graph = shift; unless ($graph) { $graph = RDF::Trine::Node::Nil->new; } return bless([$graph], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->graph); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $graph = $self->graph; my $string = sprintf( "CREATE GRAPH <%s>", $graph->uri_value ); return $string; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $graph = $self->graph; my $string = sprintf( "(create <%s>)", $graph->uri_value ); return $string; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< graph >> =cut sub graph { my $self = shift; return $self->[0]; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Distinct.pm000644 000765 000024 00000007365 12173312155 021307 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Distinct # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Distinct - Algebra class for distinct query results =head1 VERSION This document describes RDF::Query::Algebra::Distinct version 2.910. =cut package RDF::Query::Algebra::Distinct; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $pattern ) >> Returns a new Sort structure. =cut sub new { my $class = shift; my $pattern = shift; return bless( [ $pattern ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; return ($pattern); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; return sprintf( "(distinct\n${prefix}${indent}%s)", $self->pattern->sse( $context, "${prefix}${indent}" ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; return 'DISTINCT ' . $self->pattern->as_sparql( $context, $indent ); } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#'); my $q = $self->pattern->as_spin( $model ); $model->add_statement( RDF::Trine::Statement->new($q, $spin->distinct, RDF::Query::Node::Literal->new('true', undef, $xsd->boolean)) ); return $q; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'DISTINCT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Extend.pm000644 000765 000024 00000013064 12173312155 020746 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Extend # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Extend - Algebra class for extending the variable projection =head1 VERSION This document describes RDF::Query::Algebra::Extend version 2.910. =cut package RDF::Query::Algebra::Extend; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(reftype blessed); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $pattern, \@vars_and_exprs ) >> Returns a new Extend structure. =cut sub new { my $class = shift; my $pattern = shift; my $vars = shift; unless (reftype($vars) eq 'ARRAY' and not(blessed($vars))) { throw RDF::Query::Error::MethodInvocationError -text => "Variable list in RDF::Query::Algebra::Extend constructor must be an ARRAY reference"; } my @vars = grep { $_->isa('RDF::Query::Expression::Alias') } @$vars; return bless( [ $pattern, \@vars ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my $vars = $self->vars; return ($pattern, $vars); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< vars >> Returns the vars to be extended. =cut sub vars { my $self = shift; return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my $vars = join(' ', map { ($_->isa('RDF::Query::Node::Variable')) ? '?' . $_->name : $_->sse( $context ) } @{ $self->vars } ); return sprintf( "(extend (%s)\n${prefix}${indent}%s\n${prefix})", $vars, $self->pattern->sse( $context, "${prefix}${indent}" ), ); } sub _from_sse { my $class = shift; my $context = $_[1]; for ($_[0]) { if (m/^[(]extend\s+[(]\s*/) { my @nodes; s/^[(]extend\s+[(]\s*//; do { push(@nodes, RDF::Trine::Node->from_sse( $_[0], $context )); } until (m/\s*[)]/); if (m/^\s*[)]/) { s/^\s*[)]\s*//; } else { throw RDF::Trine::Error -text => "Cannot parse end-of-extend-vars from SSE string: >>$_<<"; } my ($pattern) = RDF::Query::Algebra->from_sse( $context, $_[0] ); if (m/^\s*[)]/) { s/^\s*[)]\s*//; return RDF::Query::Algebra::Extend->new( $pattern, \@nodes ); } else { throw RDF::Trine::Error -text => "Cannot parse end-of-extend from SSE string: >>$_<<"; } } else { throw RDF::Trine::Error -text => "Cannot parse extend from SSE string: >>$_<<"; } } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; if ($context->{ skip_extend }) { $context->{ skip_extend }--; return $self->pattern->as_sparql( $context, $indent ); } my $pattern = $self->pattern; my $vlist = $self->vars; my (@vars); foreach my $k (@$vlist) { if ($k->isa('RDF::Query::Expression')) { push(@vars, $k->as_sparql({}, '')); } elsif ($k->isa('RDF::Query::Node::Variable')) { push(@vars, '?' . $k->name); } else { push(@vars, $k); } } my $ggp = $pattern->as_sparql( $context, $indent ); my $sparql = $ggp; foreach my $v (@vars) { $sparql .= "\n${indent}BIND" . $v; } return $sparql; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, vars => [ map { $_->as_hash } @{ $self->vars } ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'PROJECT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @vars = $self->pattern->referenced_variables; foreach my $v (@{ $self->vars }) { if ($v->isa('RDF::Query::Node::Variable')) { push(@vars, $v->name); } else { push(@vars, $v->referenced_variables); } } return RDF::Query::_uniq(@vars); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @vars; push(@vars, $self->pattern->potentially_bound); foreach my $v (@{ $self->vars }) { if ($v->isa('RDF::Query::Node::Variable')) { push(@vars, $v->name); } else { push(@vars, $v->potentially_bound); } } return RDF::Query::_uniq(@vars); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Filter.pm000644 000765 000024 00000011647 12173312155 020751 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Filter # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Filter - Algebra class for Filter expressions =head1 VERSION This document describes RDF::Query::Algebra::Filter version 2.910. =cut package RDF::Query::Algebra::Filter; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype); use RDF::Query::Error qw(:try); use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### # function # operator # unary # binary =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Filter structure. =cut sub new { my $class = shift; my $expr = shift; my $pattern = shift; Carp::confess "Not an algebra pattern: " . Dumper($pattern) unless ($pattern->isa('RDF::Query::Algebra')); unless ($pattern->isa('RDF::Query::Algebra::GroupGraphPattern') or $pattern->isa('RDF::Query::Algebra::Filter')) { # for proper serialization, the pattern needs to be a GGP or another filter $pattern = RDF::Query::Algebra::GroupGraphPattern->new( $pattern ); } return bless( [ 'FILTER', $expr, $pattern ] ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->expr, $self->pattern); } =item C<< expr >> Returns the filter expression. =cut sub expr { my $self = shift; if (@_) { $self->[1] = shift; } return $self->[1]; } =item C<< pattern >> Returns the filter pattern. =cut sub pattern { my $self = shift; if (@_) { $self->[2] = shift; } return $self->[2]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; return sprintf( "(filter %s\n${prefix}${indent}%s)", $self->expr->sse( $context, "${prefix}${indent}" ), $self->pattern->sse( $context, "${prefix}${indent}" ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift || ''; if ($context->{ skip_filter }) { $context->{ skip_filter }--; return $self->pattern->as_sparql( $context, $indent ); } my $expr = $self->expr; my $filter_sparql = $expr->as_sparql( $context, $indent ); my $pattern_sparql = $self->pattern->as_sparql( $context, $indent ); if ($pattern_sparql =~ m#}\s*$#) { $pattern_sparql =~ s#}\s*$#${indent}\tFILTER( ${filter_sparql} ) .\n${indent}}#; } else { $pattern_sparql = "${pattern_sparql}\n${indent}FILTER( ${filter_sparql} )"; } return $pattern_sparql; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, expression => $self->expr->as_hash, }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; return $self->pattern->as_spin($model); } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'FILTER'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my $expr = $self->expr; my $pattern = $self->pattern; my @vars = $pattern->referenced_variables; if (blessed($expr) and $expr->isa('RDF::Query::Algebra')) { return RDF::Query::_uniq(@vars, $self->expr->referenced_variables); } elsif (blessed($expr) and $expr->isa('RDF::Query::Node::Variable')) { return RDF::Query::_uniq(@vars, $expr->name); } else { return (@vars); } } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; my $pattern = $self->pattern; return $pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 0; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/GroupGraphPattern.pm000644 000765 000024 00000014535 12173312155 023137 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::GroupGraphPattern # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::GroupGraphPattern - Algebra class for GroupGraphPattern patterns =head1 VERSION This document describes RDF::Query::Algebra::GroupGraphPattern version 2.910. =cut package RDF::Query::Algebra::GroupGraphPattern; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Log::Log4perl; use Scalar::Util qw(blessed refaddr); use Data::Dumper; use List::Util qw(first); use Carp qw(carp croak confess); use RDF::Query::Error qw(:try); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION, $debug); BEGIN { $debug = 0; $VERSION = '2.910'; our %SERVICE_BLOOM_IGNORE = ('http://dbpedia.org/sparql' => 1); # by default, assume dbpedia doesn't implement k:bloom(). } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new GroupGraphPattern structure. =cut sub new { my $class = shift; my @patterns = @_; my $self = bless( \@patterns, $class ); foreach my $p (@patterns) { unless (blessed($p)) { Carp::cluck; throw RDF::Query::Error::MethodInvocationError -text => "GroupGraphPattern constructor called with unblessed value"; } } return $self; } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->patterns); } =item C<< patterns >> Returns a list of the graph patterns in this GGP. =cut sub patterns { my $self = shift; return @{ $self }; } =item C<< add_pattern >> Appends a new child pattern to the GGP. =cut sub add_pattern { my $self = shift; my $pattern = shift; push( @{ $self }, $pattern ); } =item C<< quads >> Returns a list of the quads belonging to this GGP. =cut sub quads { my $self = shift; my @quads; my %bgps; foreach my $p ($self->subpatterns_of_type('RDF::Query::Algebra::NamedGraph')) { push(@quads, $p->quads); foreach my $bgp ($p->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern')) { $bgps{ refaddr($bgp) }++; } } foreach my $p ($self->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern')) { next if ($bgps{ refaddr($p) }); push(@quads, $p->quads); } return @quads; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = ($context->{indent} ||= "\t"); my @patterns = $self->patterns; if (scalar(@patterns) == 1) { return $patterns[0]->sse( $context, $prefix ); } else { return sprintf( "(join\n${prefix}${indent}%s)", join("\n${prefix}${indent}", map { $_->sse( $context, "${prefix}${indent}" ) } @patterns) ); } } =item C<< explain >> Returns a string serialization of the algebra appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}group graph pattern\n"; my @patterns = $self->patterns; if (scalar(@patterns) == 1) { $string .= $patterns[0]->explain( $s, $count+1 ); } else { foreach my $p (@patterns) { $string .= $p->explain( $s, $count+1 ); } } return $string; } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift || ''; my $force = $context->{force_ggp_braces}; $force = 0 unless (defined($force)); if ($force) { $context->{force_ggp_braces}--; } my @patterns; my @p = $self->patterns; if (scalar(@p) == 0) { return "{}"; } elsif (scalar(@p) == 1 and not($force)) { return $p[0]->as_sparql($context, $indent); } else { foreach my $p (@p) { push(@patterns, $p->as_sparql( $context, "$indent\t" )); } my $patterns = join("\n${indent}\t", @patterns); my $string = sprintf("{\n${indent}\t%s\n${indent}}", $patterns); return $string; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), patterns => [ map { $_->as_hash } $self->patterns ], }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; return map { $_->as_spin($model) } $self->patterns; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'GGP'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq(map { $_->referenced_variables } $self->patterns); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq(map { $_->potentially_bound } $self->patterns); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return RDF::Query::_uniq(map { $_->definite_variables } $self->patterns); } =item C<< check_duplicate_blanks >> Returns true if blank nodes respect the SPARQL rule of no blank-label re-use across BGPs, otherwise throws a RDF::Query::Error::QueryPatternError exception. =cut sub check_duplicate_blanks { my $self = shift; my @data; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@data, $arg->_referenced_blanks()); } } my %seen; foreach my $d (@data) { foreach my $b (@$d) { if ($seen{ $b }++) { throw RDF::Query::Error::QueryPatternError -text => "Same blank node identifier ($b) used in more than one BasicGraphPattern."; } } } return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Limit.pm000644 000765 000024 00000007335 12173312155 020601 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Limit # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Limit - Algebra class for limiting query results =head1 VERSION This document describes RDF::Query::Algebra::Limit version 2.910. =cut package RDF::Query::Algebra::Limit; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $pattern, $limit ) >> Returns a new Sort structure. =cut sub new { my $class = shift; my $pattern = shift; my $limit = shift; return bless( [ $pattern, $limit ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my $limit = $self->limit; return ($pattern, $limit); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< limit >> Returns the limit number of the pattern. =cut sub limit { my $self = shift; return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; if ($self->pattern->isa('RDF::Query::Algebra::Offset')) { my $l = $self->limit; my $o = $self->pattern->offset; return sprintf( "(slice %d %d\n${prefix}${indent}%s)", $o, $l, $self->pattern->pattern->sse( $context, "${prefix}${indent}" ), ); } else { return sprintf( "(limit %s\n${prefix}${indent}%s)", $self->limit, $self->pattern->sse( $context, "${prefix}${indent}" ), ); } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "%s\nLIMIT %d", $self->pattern->as_sparql( $context, $indent ), $self->limit, ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, limit => $self->limit, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'LIMIT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Load.pm000644 000765 000024 00000005743 12173312155 020403 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Load # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Load - Algebra class for LOAD operations =head1 VERSION This document describes RDF::Query::Algebra::Load version 2.910. =cut package RDF::Query::Algebra::Load; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new LOAD structure. =cut sub new { my $class = shift; my $url = shift; my $graph = shift; my $silent = shift; return bless([$url, $graph, $silent], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->url, $self->graph, $self->silent); } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $url = $self->url; my $graph = $self->graph; my $string; my $s = $self->silent ? "SILENT " : ''; if ($graph) { $string = sprintf( "(load %s<%s> <%s>)", $s, $url->uri_value, $graph->uri_value, ); } else { $string = sprintf( "(load %s<%s>)", $s, $url->uri_value, ); } return $string; } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $url = $self->url; my $graph = $self->graph; my $s = $self->silent ? "SILENT " : ''; my $string; if ($graph) { $string = sprintf( "LOAD %s<%s> INTO GRAPH <%s>", $s, $url->uri_value, $graph->uri_value, ); } else { $string = sprintf( "LOAD %s<%s>", $s, $url->uri_value, ); } return $string; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< url >> =cut sub url { my $self = shift; return $self->[0]; } =item C<< graph >> =cut sub graph { my $self = shift; return $self->[1]; } =item C<< silent >> =cut sub silent { my $self = shift; return $self->[2]; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Minus.pm000644 000765 000024 00000006564 12173312155 020621 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Minus # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Minus - Algebra class for Minus patterns =head1 VERSION This document describes RDF::Query::Algebra::Minus version 2.910. =cut package RDF::Query::Algebra::Minus; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Minus structure. =cut sub new { my $class = shift; my $pattern = shift; my $opt = shift; return bless( [ $pattern, $opt ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->pattern, $self->minus); } =item C<< pattern >> Returns the base pattern (LHS) onto which the minus pattern matches. =cut sub pattern { my $self = shift; return $self->[0]; } =item C<< minus >> Returns the minus pattern (RHS). =cut sub minus { my $self = shift; return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || "\t"; return sprintf( "(minus\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->pattern->sse( $context, "${prefix}${indent}" ), $self->minus->sse( $context, "${prefix}${indent}" ) ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "%s\n${indent}MINUS %s", $self->pattern->as_sparql( $context, $indent ), $self->minus->as_sparql( $context, $indent ), ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, minus => $self->minus->as_hash, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'MINUS'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables, $self->minus->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Move.pm000644 000765 000024 00000005637 12173312155 020434 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Move # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Move - Algebra class for MOVE operations =head1 VERSION This document describes RDF::Query::Algebra::Move version 2.910. =cut package RDF::Query::Algebra::Move; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new MOVE structure. =cut sub new { my $class = shift; my $from = shift; my $to = shift; my $silent = shift || 0; return bless([$from, $to, $silent], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->from, $self->to, $self->silent); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $from = $self->from; my $to = $self->to; for ($from, $to) { if ($_->isa('RDF::Trine::Node::Nil')) { $_ = 'DEFAULT'; } else { $_ = '<' . $_->uri_value . '>'; } } my $string = sprintf( "MOVE %s%s TO %s", ($self->silent ? 'SILENT ' : ''), $from, $to ); return $string; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $from = $self->from; my $to = $self->to; for ($from, $to) { if ($_->isa('RDF::Trine::Node::Nil')) { $_ = 'DEFAULT'; } else { $_ = '<' . $_->uri_value . '>'; } } my $string = sprintf( "(move%s %s %s)", ($self->silent ? '-silent' : ''), $from, $to ); return $string; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< from >> =cut sub from { my $self = shift; return $self->[0]; } =item C<< to >> =cut sub to { my $self = shift; return $self->[1]; } =item C<< silent >> =cut sub silent { my $self = shift; return $self->[2]; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/NamedGraph.pm000644 000765 000024 00000015615 12173312155 021531 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::NamedGraph # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::NamedGraph - Algebra class for NamedGraph patterns =head1 VERSION This document describes RDF::Query::Algebra::NamedGraph version 2.910. =cut package RDF::Query::Algebra::NamedGraph; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use RDF::Query::Error; use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype); use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new NamedGraph structure. =cut sub new { my $class = shift; my $graph = shift; my $pattern = shift; return bless( [ 'GRAPH', $graph, $pattern ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->graph, $self->pattern); } =item C<< graph >> Returns the graph node of the named graph expression. =cut sub graph { my $self = shift; if (@_) { my $graph = shift; $self->[1] = $graph; } my $graph = $self->[1]; return $graph; } =item C<< pattern >> Returns the graph pattern of the named graph expression. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< quads >> Returns a list of the quads belonging to this NamedGraph. =cut sub quads { my $self = shift; my @quads; foreach my $p ($self->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern')) { push(@quads, $p->quads); } my @graphquads; foreach my $q (@quads) { my $st = RDF::Trine::Statement::Quad->new( $q->subject, $q->predicate, $q->object, $self->graph, ); push(@graphquads, $st); } return @graphquads; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ''; return sprintf( "(namedgraph\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->graph->sse( $context, "${prefix}${indent}" ), $self->pattern->sse( $context, "${prefix}${indent}" ) ); } =item C<< explain >> Returns a string serialization of the algebra appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}named graph pattern\n" . "${indent}${s}graph: " . $self->graph->as_string . "\n" . $self->pattern->explain( $s, $count+1 ); return $string; } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; my $pcontext = { %$context, force_ggp_braces => 1 }; my $string = sprintf( "GRAPH %s %s", $self->graph->as_sparql( $context, $indent ), $self->pattern->as_sparql( $pcontext, $indent ), ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), graph => $self->graph, pattern => $self->pattern->as_hash, }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my @t = $self->pattern->as_spin( $model ); my $ng = RDF::Query::Node::Blank->new(); my $list = $model->add_list( @t ); $model->add_statement( RDF::Trine::Statement->new($ng, $rdf->type, $spin->NamedGraph) ); $model->add_statement( RDF::Trine::Statement->new($ng, $spin->elements, $list) ); return $ng; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'GRAPH'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @list = RDF::Query::_uniq( $self->pattern->referenced_variables, (map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph)), ); return @list; } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @list = RDF::Query::_uniq( $self->pattern->potentially_bound, (map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph)), ); return @list; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return RDF::Query::_uniq( map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph), $self->pattern->definite_variables, ); } =item C<< qualify_uris ( \%namespaces, $base_uri ) >> Returns a new algebra pattern where all referenced Resource nodes representing QNames (ns:local) are qualified using the supplied %namespaces. =cut sub qualify_uris { my $self = shift; my $class = ref($self); my $ns = shift; my $base_uri = shift; my $pattern = $self->pattern->qualify_uris( $ns, $base_uri ); my $graph = $self->graph; if (blessed($graph) and $graph->isa('RDF::Query::Node::Resource')) { my $uri = $graph->uri; if (ref($uri)) { my ($n,$l) = @$uri; unless (exists($ns->{ $n })) { throw RDF::Query::Error::QuerySyntaxError -text => "Namespace $n is not defined"; } my $resolved = join('', $ns->{ $n }, $l); $graph = RDF::Query::Node::Resource->new( $resolved, $base_uri ); } } return $class->new( $graph, $pattern ); } =item C<< check_duplicate_blanks >> Returns true if blank nodes respect the SPARQL rule of no blank-label re-use across BGPs, otherwise throws a RDF::Query::Error::QueryPatternError exception. =cut sub check_duplicate_blanks { my $self = shift; my @data; foreach my $arg ($self->construct_args) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push(@data, $arg->_referenced_blanks()); } } my %seen; foreach my $d (@data) { foreach my $b (@$d) { if ($seen{ $b }++) { throw RDF::Query::Error::QueryPatternError -text => "Same blank node identifier ($b) used in more than one BasicGraphPattern."; } } } return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Offset.pm000644 000765 000024 00000006734 12173312155 020753 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Offset # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Offset - Algebra class for offseting query results =head1 VERSION This document describes RDF::Query::Algebra::Offset version 2.910. =cut package RDF::Query::Algebra::Offset; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $pattern, $offset ) >> Returns a new Sort structure. =cut sub new { my $class = shift; my $pattern = shift; my $offset = shift; return bless( [ $pattern, $offset ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my $offset = $self->offset; return ($pattern, $offset); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< offset >> Returns the offset number of the pattern. =cut sub offset { my $self = shift; return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; return sprintf( "(offset %s\n${prefix}${indent}%s)", $self->offset, $self->pattern->sse( $context, "${prefix}${indent}" ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "%s\nOFFSET %d", $self->pattern->as_sparql( $context, $indent ), $self->offset, ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, offset => $self->offset, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'LIMIT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Optional.pm000644 000765 000024 00000010371 12173312155 021302 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Optional # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Optional - Algebra class for Optional patterns =head1 VERSION This document describes RDF::Query::Algebra::Optional version 2.910. =cut package RDF::Query::Algebra::Optional; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Optional structure. =cut sub new { my $class = shift; my $pattern = shift; my $opt = shift; return bless( [ 'OPTIONAL', $pattern, $opt ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->pattern, $self->optional); } =item C<< pattern >> Returns the base pattern (LHS) onto which the optional pattern joins. =cut sub pattern { my $self = shift; return $self->[1]; } =item C<< optional >> Returns the optional pattern (RHS). =cut sub optional { my $self = shift; return $self->[2]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; return sprintf( "(leftjoin\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->pattern->sse( $context, "${prefix}${indent}" ), $self->optional->sse( $context, "${prefix}${indent}" ) ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "%s\n${indent}OPTIONAL %s", $self->pattern->as_sparql( $context, $indent ), $self->optional->as_sparql( { %$context, force_ggp_braces => 1 }, $indent ), ); return $string; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my @lhs = $self->pattern->as_spin($model); my @rhs = $self->optional->as_spin($model); my $opt = RDF::Query::Node::Blank->new(); my $list = $model->add_list( @rhs ); $model->add_statement( RDF::Trine::Statement->new($opt, $rdf->type, $spin->Optional) ); $model->add_statement( RDF::Trine::Statement->new($opt, $spin->elements, $list) ); return (@lhs, $opt); } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, optional => $self->optional->as_hash, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'OPTIONAL'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables, $self->optional->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq($self->pattern->potentially_bound, $self->optional->potentially_bound); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Path.pm000644 000765 000024 00000014674 12173312155 020423 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Path # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Path - Algebra class for path patterns =head1 VERSION This document describes RDF::Query::Algebra::Path version 2.910. =cut package RDF::Query::Algebra::Path; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Set::Scalar; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION, $debug, $lang, $languri); BEGIN { $debug = 0; $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Path structure. =cut sub new { my $class = shift; my $start = shift; my $path = shift; my $end = shift; my $graph = shift; return bless( [ $start, $path, $end, $graph ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->start, $self->path, $self->end, $self->graph); } =item C<< path >> Returns the path description for this path expression. =cut sub path { my $self = shift; return $self->[1]; } =item C<< start >> Returns the path origin node. =cut sub start { my $self = shift; return $self->[0]; } =item C<< end >> Returns the path destination node. =cut sub end { my $self = shift; return $self->[2]; } =item C<< graph >> Returns the named graph. =cut sub graph { my $self = shift; return $self->[3]; } =item C<< distinguish_bnode_variables >> Returns a new Path object with blank nodes replaced by distinguished variables. =cut sub distinguish_bnode_variables { my $self = shift; my $class = ref($self); my @nodes = ($self->start, $self->end); foreach my $i (0 .. $#nodes) { if ($nodes[$i]->isa('RDF::Query::Node::Blank')) { $nodes[$i] = $nodes[$i]->make_distinguished_variable; } } return $class->new( $nodes[0], $self->path, $nodes[1] ); } =item C<< bounded_length >> Returns true if the path is of bounded length. =cut sub bounded_length { my $self = shift; return $self->_bounded_length( $self->path ); } sub _bounded_length { my $self = shift; my $array = shift; return 1 if blessed($array); my ($op, @nodes) = @$array; return 1 if ($op eq '?'); return 0 if ($op =~ /^[*+]$/); return 1 if ($op =~ /^\d+(-\d+)?$/); return 0 if ($op =~ /^\d+-$/); if ($op =~ m<^[/|^]$>) { my @fixed = map { $self->_bounded_length($_) } @nodes; foreach my $f (@fixed) { return 0 unless ($f); } return 1; } } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; my $start = $self->start->sse( $context, $prefix ); my $end = $self->end->sse( $context, $prefix ); my $path = $self->path; my $psse = $self->_expand_path( $path, 'sse' ); if ($self->graph) { my $graph = $self->graph->sse( $context, $prefix ); return sprintf( '(path %s %s %s %s)', $start, $psse, $end, $graph ); } else { return sprintf( '(path %s %s %s)', $start, $psse, $end ); } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; my $start = $self->start->as_sparql( $context, $prefix ); my $end = $self->end->as_sparql( $context, $prefix ); my $path = $self->path; my $psse = $self->_expand_path( $path, 'as_sparql' ); return sprintf( '%s %s %s .', $start, $psse, $end ); } sub _expand_path { my $self = shift; my $array = shift; my $method = shift; if (blessed($array)) { my $string = $array->$method({}, ''); if ($string eq '') { return 'a'; } else { return $string; } } else { my ($op, @nodes) = @$array; my @nodessse = map { $self->_expand_path($_, $method) } @nodes; my $psse; # if ($op eq 'DISTINCT') { # $psse = 'DISTINCT(' . join('/', @nodessse) . ')'; # } if ($op eq '+') { $psse = (scalar(@nodessse) == 1) ? $nodessse[0] . $op : '(' . join('/', @nodessse) . ')' . $op; } elsif ($op eq '*') { $psse = (scalar(@nodessse) == 1) ? $nodessse[0] . $op : '(' . join('/', @nodessse) . ')' . $op; } elsif ($op eq '?') { $psse = (scalar(@nodessse) == 1) ? $nodessse[0] . $op : '(' . join('/', @nodessse) . ')' . $op; } elsif ($op eq '!') { $psse = (scalar(@nodessse) == 1) ? '!' . $nodessse[0] : '!(' . join('|', @nodessse) . ')'; } elsif ($op eq '^') { $psse = (scalar(@nodessse) == 1) ? $op . $nodessse[0] : '(' . join('/', map { "${op}$_" } @nodessse) . ')'; } elsif ($op eq '/') { $psse = (scalar(@nodessse) == 1) ? $nodessse[0] : '(' . join('/', @nodessse) . ')'; } elsif ($op eq '|') { $psse = (scalar(@nodessse) == 1) ? $nodessse[0] : '(' . join('|', @nodessse) . ')'; } elsif ($op =~ /^(\d+)$/) { $psse = join('/', @nodessse) . '{' . $op . '}'; } elsif ($op =~ /^(\d+)-(\d+)$/) { $psse = join('/', @nodessse) . "{$1,$2}"; } elsif ($op =~ /^(\d+)-$/) { $psse = join('/', @nodessse) . "{$1,}"; } else { confess "Serialization of unknown path type $op"; } return $psse; } } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'PATH'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @vars = grep { $_->isa('RDF::Query::Node::Variable') } ($self->start, $self->end); return RDF::Query::_uniq(map { $_->name } @vars); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @vars = grep { $_->isa('RDF::Query::Node::Variable') } ($self->start, $self->end); return RDF::Query::_uniq(map { $_->name } @vars); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->referenced_variables; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Project.pm000644 000765 000024 00000024037 12173312155 021127 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Project # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Project - Algebra class for projection =head1 VERSION This document describes RDF::Query::Algebra::Project version 2.910. =cut package RDF::Query::Algebra::Project; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Scalar::Util qw(reftype blessed refaddr); use Carp qw(carp croak confess); use RDF::Trine::Iterator qw(sgrep); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( $pattern, \@vars_and_exprs ) >> Returns a new Project structure. =cut sub new { my $class = shift; my $pattern = shift; my $vars = shift; unless (blessed($pattern)) { throw RDF::Query::Error::MethodInvocationError -text => "Sub-pattern in RDF::Query::Algebra::Project constructor must be a valid algebra object"; } unless (reftype($vars) eq 'ARRAY' and not(blessed($vars))) { throw RDF::Query::Error::MethodInvocationError -text => "Variable list in RDF::Query::Algebra::Project constructor must be an ARRAY reference"; } my @vars; foreach my $v (@$vars) { if ($v->isa('RDF::Query::Node::Variable')) { push(@vars, $v); } else { push(@vars, $v->alias); } } return bless( [ $pattern, \@vars ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my $vars = $self->vars; return ($pattern, $vars); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< vars >> Returns the vars to be projected to. =cut sub vars { my $self = shift; return $self->[1]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my $vars = join(' ', map { ($_->isa('RDF::Query::Node::Variable')) ? '?' . $_->name : $_->sse( $context ) } @{ $self->vars } ); return sprintf( "(project (%s)\n${prefix}${indent}%s\n${prefix})", $vars, $self->pattern->sse( $context, "${prefix}${indent}" ), ); } sub _from_sse { my $class = shift; my $context = $_[1]; for ($_[0]) { if (m/^[(]project\s+[(]\s*/) { my @nodes; s/^[(]project\s+[(]\s*//; do { push(@nodes, RDF::Trine::Node->from_sse( $_[0], $context )); } until (m/\s*[)]/); if (m/^\s*[)]/) { s/^\s*[)]\s*//; } else { throw RDF::Trine::Error -text => "Cannot parse end-of-project-vars from SSE string: >>$_<<"; } my ($pattern) = RDF::Query::Algebra->from_sse( $context, $_[0] ); if (m/^\s*[)]/) { s/^\s*[)]\s*//; warn "project: " . Dumper(\@nodes); return RDF::Query::Algebra::Project->new( $pattern, \@nodes ); } else { throw RDF::Trine::Error -text => "Cannot parse end-of-project from SSE string: >>$_<<"; } } else { throw RDF::Trine::Error -text => "Cannot parse project from SSE string: >>$_<<"; } } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; my $pattern = $self->pattern; $context->{ force_ggp_braces }++; my ($vars, $_sparql); my $vlist = $self->vars; my (@vars); foreach my $k (@$vlist) { if ($k->isa('RDF::Query::Expression')) { push(@vars, $k->sse({}, '')); } elsif ($k->isa('RDF::Query::Node::Variable')) { push(@vars, '?' . $k->name); } else { push(@vars, $k); } } my $aggregate = 0; my $group = ''; my $having = ''; my $order = ''; my %agg_projections; my @aggs = $pattern->subpatterns_of_type( 'RDF::Query::Algebra::Aggregate' ); if (@aggs) { # aggregate check my $p = $pattern; if ($p->isa('RDF::Query::Algebra::Sort')) { $context->{ skip_sort }++; $order = $p->_as_sparql_order_exprs( $context, $indent ); $p = $p->pattern } if ($p->isa('RDF::Query::Algebra::Filter')) { $context->{ skip_filter }++; $having = $p->expr->as_sparql( $context, $indent ); $p = $p->pattern; } $p = ($p->patterns)[0] if ($p->isa('RDF::Query::Algebra::GroupGraphPattern') and scalar(@{[$p->patterns]}) == 1); if ($p->isa('RDF::Query::Algebra::Extend') and $p->pattern->isa('RDF::Query::Algebra::Aggregate')) { my $pp = $p->pattern; $context->{ skip_extend }++; my $vlist = $p->vars; foreach my $k (@$vlist) { if ($k->isa('RDF::Query::Expression::Alias')) { my $var = $k->name; my $expr = $k->expression; my $exprstr; if ($expr->isa('RDF::Query::Expression::Binary')) { $exprstr = $expr->as_sparql( $context, $indent ); } else { $exprstr = $k->expression->name; } my $str = "($exprstr AS ?$var)"; $agg_projections{ '?' . $var } = $str; } else { warn Dumper($k) . ' '; } } my @groups = $pp->groupby; if (@groups) { $group = join(' ', map { $_->as_sparql($context, $indent) } @groups); } } } if ($pattern->isa('RDF::Query::Algebra::Extend')) { my %seen; my $vlist = $pattern->vars; foreach my $k (@$vlist) { if ($k->isa('RDF::Query::Expression::Alias')) { $seen{ '?' . $k->name } = $k->as_sparql({}, ''); } elsif ($k->isa('RDF::Query::Expression')) { push(@vars, $k->as_sparql({}, '')); } elsif ($k->isa('RDF::Query::Node::Variable')) { push(@vars, '?' . $k->name); } else { push(@vars, $k); } } @vars = map { exists($seen{$_}) ? $seen{$_} : $_ } @vars; $vars = join(' ', @vars); my $pp = $pattern->pattern; if ($pp->isa('RDF::Query::Algebra::Aggregate')) { $_sparql = $pp->pattern->as_sparql( $context, $indent ); my @groups = $pp->groupby; if (@groups) { $group = join(' ', map { $_->as_sparql($context, $indent) } @groups); } } else { $_sparql = $pp->as_sparql( $context, $indent ); } } else { my $pvars = join(' ', map { my $agg = $agg_projections{ "?$_" }; defined($agg) ? $agg : "?$_" } sort $self->pattern->referenced_variables); my $svars = join(' ', map { my $agg = $agg_projections{ $_ }; defined($agg) ? $agg : $_ } sort @vars); $vars = ($pvars eq $svars) ? '*' : join(' ', map { my $agg = $agg_projections{ $_ }; defined($agg) ? $agg : $_ } @vars); $_sparql = $pattern->as_sparql( $context, $indent ); } my $sparql = sprintf("%s WHERE %s", $vars, $_sparql); if ($group) { $sparql .= "\n${indent}GROUP BY $group"; } if ($having) { $sparql .= "\n${indent}HAVING $having"; } if ($order) { $sparql .= "\n${indent}ORDER BY $order"; } return $sparql; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), variables => [ map { $_->as_hash } @{ $self->vars } ], pattern => $self->pattern->as_hash, }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my $q = RDF::Query::Node::Blank->new(); my @nodes = $self->pattern->as_spin( $model ); $model->add_statement( RDF::Trine::Statement->new($q, $rdf->type, $spin->Select) ); my @vars = map { RDF::Query::Node::Blank->new( "variable_" . $_->name ) } @{ $self->vars }; my $vlist = $model->add_list( @vars ); $model->add_statement( RDF::Trine::Statement->new($q, $spin->resultVariables, $vlist) ); my $list = $model->add_list( @nodes ); $model->add_statement( RDF::Trine::Statement->new($q, $spin->where, $list) ); return $q; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'PROJECT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @vars = $self->pattern->referenced_variables; foreach my $v (@{ $self->vars }) { if ($v->isa('RDF::Query::Node::Variable')) { push(@vars, $v->name); } else { push(@vars, $v->referenced_variables); } } return RDF::Query::_uniq(@vars); } =item C<< bind_variables ( \%bound ) >> Returns a new algebra pattern with variables named in %bound replaced by their corresponding bound values. =cut sub bind_variables { my $self = shift; my $class = ref($self); my $bound = shift; my $pattern = $self->pattern->bind_variables( $bound ); my $vars = $self->vars; my @vars; foreach my $v (@$vars) { if (blessed($v) and $v->isa('RDF::Query::Node::Variable') and exists $bound->{ $v->name }) { push(@vars, $bound->{ $v->name }); } else { push(@vars, $v); } } return $class->new( $pattern, \@vars ); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @vars; # push(@vars, $self->pattern->potentially_bound); foreach my $v (@{ $self->vars }) { if ($v->isa('RDF::Query::Node::Variable')) { push(@vars, $v->name); } else { push(@vars, $v->potentially_bound); } } return RDF::Query::_uniq(@vars); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Quad.pm000644 000765 000024 00000012427 12173312155 020413 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Quad # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Quad - Algebra class for Quad patterns =head1 VERSION This document describes RDF::Query::Algebra::Quad version 2.910. =cut package RDF::Query::Algebra::Quad; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra RDF::Trine::Statement::Quad); use Data::Dumper; use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### my %QUAD_LABELS; our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Quad structure. =cut sub new { my $class = shift; my @nodes = @_; unless (scalar(@nodes) == 4) { throw RDF::Query::Error::MethodInvocationError -text => "Quad constructor must have four node arguments"; } my @names = qw(subject predicate object context); foreach my $i (0 .. 3) { unless (defined($nodes[ $i ]) and blessed($nodes[ $i ])) { $nodes[ $i ] = RDF::Query::Node::Variable->new($names[ $i ]); } unless ($nodes[ $i ]->isa('RDF::Query::Node')) { $nodes[ $i ] = RDF::Query::Node->from_trine( $nodes[ $i ] ); } } return $class->SUPER::new( @nodes ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; my $pred = $self->predicate; if ($pred->isa('RDF::Trine::Node::Resource') and $pred->uri_value eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') { $pred = 'a'; } else { $pred = $pred->as_sparql( $context ); } my $string = sprintf( "%s %s %s .", $self->subject->as_sparql( $context ), $pred, $self->object->as_sparql( $context ), ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), nodes => [ map { $_->as_hash } $self->nodes ], }; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; my @nodes = $self->nodes; my @blanks = grep { $_->isa('RDF::Trine::Node::Blank') } @nodes; return map { $_->blank_identifier } @blanks; } =item C<< qualify_uris ( \%namespaces, $base_uri ) >> Returns a new algebra pattern where all referenced Resource nodes representing QNames (ns:local) are qualified using the supplied %namespaces. =cut sub qualify_uris { my $self = shift; my $class = ref($self); my $ns = shift; my $base_uri = shift; my @nodes; foreach my $n ($self->nodes) { my $blessed = blessed($n); if ($blessed and $n->isa('RDF::Query::Node::Resource')) { my $uri = $n->uri; if (ref($uri)) { my ($n,$l) = @$uri; unless (exists($ns->{ $n })) { throw RDF::Query::Error::QuerySyntaxError -text => "Namespace $n is not defined"; } my $resolved = RDF::Query::Node::Resource->new( join('', $ns->{ $n }, $l), $base_uri ); push(@nodes, $resolved); } else { push(@nodes, $n); } } elsif ($blessed and $n->isa('RDF::Query::Node::Literal')) { my $node = $n; my $dt = $node->literal_datatype; if (ref($dt)) { my ($n,$l) = @$dt; unless (exists($ns->{ $n })) { throw RDF::Query::Error::QuerySyntaxError -text => "Namespace $n is not defined"; } my $resolved = RDF::Query::Node::Resource->new( join('', $ns->{ $n }, $l), $base_uri ); my $lit = RDF::Query::Node::Literal->new( $node->literal_value, undef, $resolved->uri_value ); push(@nodes, $lit); } else { push(@nodes, $node); } } else { push(@nodes, $n); } } return $class->new( @nodes ); } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $bf = ''; foreach my $n ($self->nodes) { $bf .= ($n->isa('RDF::Query::Node::Variable')) ? 'f' : 'b'; } return $bf; } =item C<< distinguish_bnode_variables >> Returns a new Quad object with blank nodes replaced by distinguished variables. =cut sub distinguish_bnode_variables { my $self = shift; my $class = ref($self); my @nodes = $self->nodes; foreach my $i (0 .. $#nodes) { if ($nodes[$i]->isa('RDF::Query::Node::Blank')) { $nodes[$i] = $nodes[$i]->make_distinguished_variable; } } return $class->new( @nodes ); } =item C<< label ( $label => $value ) >> Sets the named C<< $label >> to C<< $value >> for this quad object. If no C<< $value >> is given, returns the current label value, or undef if none exists. =cut sub label { my $self = shift; my $addr = refaddr($self); my $label = shift; if (@_) { my $value = shift; $QUAD_LABELS{ $addr }{ $label } = $value; } if (exists $QUAD_LABELS{ $addr }) { return $QUAD_LABELS{ $addr }{ $label }; } else { return; } } sub DESTROY { my $self = shift; my $addr = refaddr( $self ); delete $QUAD_LABELS{ $addr }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Sequence.pm000644 000765 000024 00000012040 12173312155 021260 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Sequence # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Sequence - Algebra class for a sequence of algebra operations =head1 VERSION This document describes RDF::Query::Algebra::Sequence version 2.910. =cut package RDF::Query::Algebra::Sequence; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr reftype blessed); use Carp qw(carp croak confess); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap swatch); ###################################################################### our ($VERSION); my %AS_SPARQL; BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Sequence structure. =cut sub new { my $class = shift; my @patterns = @_; return bless( [ @patterns ] ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->patterns); } =item C<< patterns >> Returns a list of patterns belonging to this sequence. =cut sub patterns { my $self = shift; return @$self; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my @patterns = map { $_->sse( $context ) } $self->patterns; return sprintf( "(sequence\n${prefix}${indent}%s\n${prefix})", join("\n${prefix}${indent}", @patterns) ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; if (exists $AS_SPARQL{ refaddr( $self ) }) { return $AS_SPARQL{ refaddr( $self ) }; } else { my $context = shift; # if (ref($context)) { # $context = { %$context }; # } my $indent = shift || ''; my @patterns; foreach my $t ($self->patterns) { push(@patterns, $t->as_sparql( $context, $indent )); } my $string = join(" ;\n${indent}", @patterns); $AS_SPARQL{ refaddr( $self ) } = $string; return $string; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), patterns => [ map { $_->as_hash } $self->patterns ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'SEQUENCE'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq(map { $_->referenced_variables } $self->patterns); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @patterns = $self->patterns; return $patterns[ $#patterns ]->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; my @patterns = $self->patterns; return $patterns[ $#patterns ]->potentially_bound; } =item C<< clone >> =cut sub clone { my $self = shift; my $class = ref($self); return $class->new( map { $_->clone } $self->patterns ); } =item C<< bind_variables ( \%bound ) >> Returns a new algebra pattern with variables named in %bound replaced by their corresponding bound values. =cut sub bind_variables { my $self = shift; my $class = ref($self); my $bound = shift; return $class->new( map { $_->bind_variables( $bound ) } $self->patterns ); } =item C<< check_duplicate_blanks >> Returns true if blank nodes respect the SPARQL rule of no blank-label re-use across BGPs, otherwise throws a RDF::Query::Error::QueryPatternError exception. =cut sub check_duplicate_blanks { my $self = shift; my @data; foreach my $arg (grep { blessed($_) and $_->isa('RDF::Query::Algebra::Update') and $_->data_only } $self->construct_args) { push(@data, [$arg, $arg->_referenced_blanks()]); } my %seen; foreach my $d (@data) { my ($pat, $data) = @$d; foreach my $b (@$data) { if ($seen{ $b }) { throw RDF::Query::Error::QueryPatternError -text => "Same blank node identifier ($b) used in more than one BasicGraphPattern."; } $seen{ $b } = $pat; } } return 1; } sub _referenced_blanks { my $self = shift; my @data; foreach my $arg ($self->pattern) { if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) { push( @data, $arg->_referenced_blanks ); } } return @data; } sub DESTROY { my $self = shift; delete $AS_SPARQL{ refaddr( $self ) }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Service.pm000644 000765 000024 00000014010 12173312155 021107 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Service # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Service - Algebra class for SERVICE (federation) patterns =head1 VERSION This document describes RDF::Query::Algebra::Service version 2.910. =cut package RDF::Query::Algebra::Service; use strict; use warnings; use base qw(RDF::Query::Algebra); use Log::Log4perl; use URI::Escape; use MIME::Base64; use Data::Dumper; use RDF::Query::Error; use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype); use Storable qw(store_fd fd_retrieve); use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION, $BLOOM_FILTER_ERROR_RATE); BEGIN { $BLOOM_FILTER_ERROR_RATE = 0.1; $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Service structure. =cut sub new { my $class = shift; my $endpoint = shift; my $pattern = shift; my $silent = shift || 0; my $ggp = shift; return bless( [ 'SERVICE', $endpoint, $pattern, $silent, $ggp ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->endpoint, $self->pattern, $self->silent, $self->lhs); } =item C<< endpoint >> Returns the endpoint resource of the named graph expression. =cut sub endpoint { my $self = shift; if (@_) { my $endpoint = shift; $self->[1] = $endpoint; } my $endpoint = $self->[1]; return $endpoint; } =item C<< pattern >> Returns the graph pattern of the named graph expression. =cut sub pattern { my $self = shift; if (@_) { my $pattern = shift; $self->[2] = $pattern; } return $self->[2]; } =item C<< silent >> Returns true if the service operation is to ignore errors during execution. =cut sub silent { my $self = shift; return $self->[3]; } =item C<< lhs >> If the SERVCE operation uses a variable endpoint, then it is considered a binary operator, executing the left-hand-side pattern first, and using results from it to bind endpoint URL values to use in SERVICE evaluation. =cut sub lhs { my $self = shift; return $self->[4]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; if (my $ggp = $self->lhs) { return sprintf( "(service\n${prefix}${indent}%s\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->lhs->sse( $context, "${prefix}${indent}" ), $self->endpoint->sse( $context, "${prefix}${indent}" ), $self->pattern->sse( $context, "${prefix}${indent}" ) ); } else { return sprintf( "(service\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->endpoint->sse( $context, "${prefix}${indent}" ), $self->pattern->sse( $context, "${prefix}${indent}" ) ); } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $op = ($self->silent) ? 'SERVICE SILENT' : 'SERVICE'; if (my $ggp = $self->lhs) { local($context->{skip_filter}) = 0; my $string = sprintf( "%s\n${indent}%s %s %s", $ggp->as_sparql( $context, $indent ), $op, $self->endpoint->as_sparql( $context, $indent ), $self->pattern->as_sparql( { %$context, force_ggp_braces => 1 }, $indent ), ); return $string; } else { my $string = sprintf( "%s %s %s", $op, $self->endpoint->as_sparql( $context, $indent ), $self->pattern->as_sparql( { %$context, force_ggp_braces => 1 }, $indent ), ); return $string; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), endpoint => $self->endpoint, pattern => $self->pattern->as_hash, lhs => $self->lhs->as_hash, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'SERVICE'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @list = $self->pattern->referenced_variables; push(@list, $self->lhs->referenced_variables) if ($self->lhs); return RDF::Query::_uniq(@list); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; my @list = RDF::Query::_uniq($self->pattern->potentially_bound); if ($self->lhs) { push(@list, $self->lhs->potentially_bound); } return @list; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return RDF::Query::_uniq( map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph), $self->pattern->definite_variables, ($self->lhs ? $self->lhs->definite_variables : ()), ); } =item C<< qualify_uris ( \%namespaces, $base_uri ) >> Returns a new algebra pattern where all referenced Resource nodes representing QNames (ns:local) are qualified using the supplied %namespaces. =cut sub qualify_uris { my $self = shift; my $class = ref($self); my $ns = shift; my $base_uri = shift; my $pattern = $self->pattern->qualify_uris( $ns, $base_uri ); my $endpoint = $self->endpoint; my $silent = $self->silent; my $uri = $endpoint->uri; if (my $ggp = $self->lhs) { return $class->new( $endpoint, $pattern, $silent, $ggp->qualify_uris($ns, $base_uri) ); } else { return $class->new( $endpoint, $pattern, $silent ); } } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Sort.pm000644 000765 000024 00000010612 12173312155 020442 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Sort # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Sort - Algebra class for sorting =head1 VERSION This document describes RDF::Query::Algebra::Sort version 2.910. =cut package RDF::Query::Algebra::Sort; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Log::Log4perl; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); use Time::HiRes qw(gettimeofday tv_interval); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C $expr ] )> Returns a new Sort structure. =cut sub new { my $class = shift; my $pattern = shift; my @orderby = @_; return bless( [ $pattern, @orderby ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; my $pattern = $self->pattern; my @orderby = $self->orderby; return ($pattern, @orderby); } =item C<< pattern >> Returns the pattern to be sorted. =cut sub pattern { my $self = shift; if (@_) { $self->[0] = shift; } return $self->[0]; } =item C<< orderby >> Returns the array of ordering definitions. =cut sub orderby { my $self = shift; my @orderby = @{ $self }[ 1 .. $#{ $self } ]; return @orderby; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my @order_sse; my @orderby = $self->orderby; foreach my $o (@orderby) { my ($dir, $val) = @$o; push(@order_sse, sprintf("($dir %s)", $val->sse( $context, "${prefix}${indent}" ))); } return sprintf( "(sort\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->pattern->sse( $context, "${prefix}${indent}" ), join(' ', @order_sse), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift; if ($context->{ skip_sort }) { $context->{ skip_sort }--; return $self->pattern->as_sparql( $context, $indent ); } my $order_exprs = $self->_as_sparql_order_exprs($context, $indent); my $string = sprintf( "%s\nORDER BY %s", $self->pattern->as_sparql( $context, $indent ), $order_exprs, ); return $string; } sub _as_sparql_order_exprs { my $self = shift; my $context = shift; my $intent = shift; my @order_sparql; my @orderby = $self->orderby; foreach my $o (@orderby) { my ($dir, $val) = @$o; $dir = uc($dir); my $str = ($dir eq 'ASC') ? $val->as_sparql( $context ) : sprintf("%s(%s)", $dir, $val->as_sparql( $context )); push(@order_sparql, $str); } return join(' ', @order_sparql); } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->pattern->as_hash, order => [ map { $_->as_hash } $self->orderby ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'SORT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->pattern->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq($self->pattern->potentially_bound); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->pattern->definite_variables; } =item C<< is_solution_modifier >> Returns true if this node is a solution modifier. =cut sub is_solution_modifier { return 1; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/SubSelect.pm000644 000765 000024 00000006250 12173312155 021407 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::SubSelect # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::SubSelect - Algebra class for Subselects =head1 VERSION This document describes RDF::Query::Algebra::SubSelect version 2.910. =cut package RDF::Query::Algebra::SubSelect; use strict; use warnings; use base qw(RDF::Query::Algebra); use Log::Log4perl; use URI::Escape; use MIME::Base64; use Data::Dumper; use RDF::Query::Error; use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype); use Storable qw(store_fd fd_retrieve); use RDF::Trine::Iterator qw(sgrep smap swatch); ###################################################################### our ($VERSION, $BLOOM_FILTER_ERROR_RATE); BEGIN { $BLOOM_FILTER_ERROR_RATE = 0.1; $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new SubSelect structure. =cut sub new { my $class = shift; my $query = shift; return bless( [ $query ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->query); } =item C<< query >> Returns the sub-select query. =cut sub query { my $self = shift; if (@_) { my $query = shift; $self->[0] = $query; } my $query = $self->[0]; return $query; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; return $self->query->sse( $context ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "{ %s }", $self->query->as_sparql( $context, $indent ), ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), pattern => $self->query->as_hash, }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'SUBSELECT'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my @list = $self->query->pattern->referenced_variables; return @list; } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return $self->query->pattern->potentially_bound; } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return $self->query->pattern->definite_variables; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Table.pm000644 000765 000024 00000010210 12173312155 020534 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Table # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Table - Algebra class for constant table data =head1 VERSION This document describes RDF::Query::Algebra::Table version 2.910. =cut package RDF::Query::Algebra::Table; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(blessed refaddr reftype); use Carp qw(carp croak confess); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap swatch); ###################################################################### our ($VERSION); my %AS_SPARQL; BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C<< new ( \@variables, $row1, $row2, ... ) >> Returns a new Table structure. =cut sub new { my $class = shift; my $vars = shift; my @rows = @_; foreach my $t (@rows) { unless ($t->isa('RDF::Trine::VariableBindings')) { throw RDF::Query::Error::QueryPatternError -text => "Rows belonging to a table must be variable bindings"; } } return bless( [ $vars, \@rows ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ([$self->variables], $self->rows); } =item C<< variables >> Returns a list of variable names used in this data table. =cut sub variables { my $self = shift; return @{ $self->[0] }; } =item C<< rows >> Returns a list of variable bindings belonging to this data table. =cut sub rows { my $self = shift; return @{ $self->[1] }; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; my @rows = sort map { $_->sse( $context ) } $self->rows; return sprintf( "(table\n${prefix}${indent}%s\n${prefix})", join("\n${prefix}${indent}", @rows) ); } =item C<< explain >> Returns a string serialization of the algebra appropriate for display on the command line. =cut sub explain { my $self = shift; my $s = shift; my $count = shift; my $indent = $s x $count; my $string = "${indent}table\n"; foreach my $t ($self->rows) { $string .= $t->explain($s, $indent+1); } return $string; } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; if (exists $AS_SPARQL{ refaddr( $self ) }) { return $AS_SPARQL{ refaddr( $self ) }; } else { my $context = shift; # if (ref($context)) { # $context = { %$context }; # } my $indent = shift || ''; my @values; my @vars = $self->variables; foreach my $row ($self->rows) { my @row_values; foreach my $var (@vars) { my $node = $row->{$var}; my $value = ($node) ? $node->as_sparql($context, $indent) : 'UNDEF'; push(@row_values, $value); } push(@values, '(' . join(' ', @row_values) . ')'); } my $vars = join(' ', map { "?$_" } @vars); my $string = "VALUES ($vars) {\n${indent}" . join("\n${indent}", @values) . "\n}\n"; $AS_SPARQL{ refaddr( $self ) } = $string; return $string; } } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), patterns => [ map { $_->as_hash } $self->rows ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'TABLE'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; my %vars; foreach my $r ($self->rows) { $vars{ $_ }++ foreach (keys %$r); } return keys %vars; } sub DESTROY { my $self = shift; delete $AS_SPARQL{ refaddr( $self ) }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/TimeGraph.pm000644 000765 000024 00000007550 12173312155 021402 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::TimeGraph # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::TimeGraph - Algebra class for temporal patterns =head1 VERSION This document describes RDF::Query::Algebra::TimeGraph version 2.910. =cut package RDF::Query::Algebra::TimeGraph; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new TimeGraph structure. =cut sub new { my $class = shift; my @data = @_; # $interval, $pattern, $triples return bless( [ 'TIME', @data ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->interval, $self->pattern, $self->time_triples); } =item C<< interval >> Returns the time interval node of the temporal graph expression. =cut sub interval { my $self = shift; if (@_) { my $interval = shift; $self->[1] = $interval; } return $self->[1]; } =item C<< pattern >> Returns the graph pattern of the temporal graph expression. =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< time_triples >> Returns the triples describing the time interval of the temporal graph. =cut sub time_triples { my $self = shift; return $self->[3]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent}; return sprintf( '(time\n${prefix}${indent}%s\n${prefix}${indent}%s\n${prefix}${indent}%s)', $self->interval->sse( $context, "${prefix}${indent}" ), $self->pattern->sse( $context, "${prefix}${indent}" ), $self->time_triples->sse( $context, "${prefix}${indent}" ), ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $nindent = $indent . "\t"; my $string = sprintf( "TIME %s %s", $self->interval->as_sparql( $context, $indent ), $self->pattern->as_sparql( $context, $indent ), ); return $string; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'TIME'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq( map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph), $self->pattern->referenced_variables, $self->time_triples->referenced_variables, ); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq( map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph), $self->pattern->potentially_bound, $self->time_triples->potentially_bound, ); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; return RDF::Query::_uniq( map { $_->name } grep { $_->isa('RDF::Query::Node::Variable') } ($self->graph), $self->pattern->definite_variables, $self->time_triples->definite_variables, ); } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Triple.pm000644 000765 000024 00000012445 12173312155 020760 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Triple # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Triple - Algebra class for Triple patterns =head1 VERSION This document describes RDF::Query::Algebra::Triple version 2.910. =cut package RDF::Query::Algebra::Triple; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra RDF::Trine::Statement); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Triple structure. =cut sub new { my $class = shift; my @nodes = @_; foreach my $i (0 .. 2) { unless (defined($nodes[ $i ])) { $nodes[ $i ] = RDF::Query::Node::Variable->new($node_methods[ $i ]); } if (blessed($nodes[ $i ]) and not($nodes[ $i ]->isa('RDF::Query::Node'))) { $nodes[ $i ] = RDF::Query::Node->from_trine( $nodes[ $i ] ); } } return $class->_new( @nodes ); } sub _new { my $class = shift; return $class->SUPER::new( @_ ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $pred = $self->predicate; if ($pred->isa('RDF::Trine::Node::Resource') and $pred->uri_value eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') { $pred = 'a'; } else { $pred = $pred->as_sparql( $context ); } my $subj = $self->subject->as_sparql( $context ); my $obj = $self->object->as_sparql( $context ); my $string = sprintf( "%s %s %s .", $subj, $pred, $obj, ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), nodes => [ map { $_->as_hash } $self->nodes ], }; } =item C<< as_spin ( $model ) >> Adds statements to the given model to represent this algebra object in the SPARQL Inferencing Notation (L). =cut sub as_spin { my $self = shift; my $model = shift; my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $t = RDF::Query::Node::Blank->new(); my @nodes = $self->nodes; foreach (@nodes) { if (blessed($_) and $_->isa('RDF::Trine::Node::Variable')) { $_ = RDF::Query::Node::Blank->new( "variable_" . $_->name ); } } $model->add_statement( RDF::Trine::Statement->new($t, $spin->subject, $nodes[0]) ); $model->add_statement( RDF::Trine::Statement->new($t, $spin->predicate, $nodes[1]) ); $model->add_statement( RDF::Trine::Statement->new($t, $spin->object, $nodes[2]) ); return $t; } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; my @nodes = $self->nodes; my @blanks = grep { Carp::confess Dumper($_) unless blessed($_); $_->isa('RDF::Trine::Node::Blank') } @nodes; return map { $_->blank_identifier } @blanks; } =item C<< subsumes ( $pattern ) >> Returns true if the triple subsumes the pattern, false otherwise. =cut sub subsumes { my $self = shift; my $pattern = shift; return 0 unless ($pattern->isa('RDF::Trine::Statement')); foreach my $method (@node_methods) { my $snode = $self->$method(); next if ($snode->isa('RDF::Trine::Node::Variable')); my $pnode = $pattern->$method(); next if ($snode->equal( $pnode )); return 0; } return 1; } =item C<< bf () >> Returns a string representing the state of the nodes of the triple (bound or free). =cut sub bf { my $self = shift; my $bf = ''; foreach my $n ($self->nodes) { $bf .= ($n->isa('RDF::Query::Node::Variable')) ? 'f' : 'b'; } return $bf; } =item C<< distinguish_bnode_variables >> Returns a new Quad object with blank nodes replaced by distinguished variables. =cut sub distinguish_bnode_variables { my $self = shift; my $class = ref($self); my @nodes = $self->nodes; foreach my $i (0 .. $#nodes) { if ($nodes[$i]->isa('RDF::Query::Node::Blank')) { $nodes[$i] = $nodes[$i]->make_distinguished_variable; } } return $class->new( @nodes ); } sub _from_sse { my $class = shift; return RDF::Trine::Statement->from_sse( @_ ); } =item C<< label ( $label => $value ) >> Sets the named C<< $label >> to C<< $value >> for this triple object. If no C<< $value >> is given, returns the current label value, or undef if none exists. =cut sub label { my $self = shift; my $addr = refaddr($self); my $label = shift; if (@_) { my $value = shift; $TRIPLE_LABELS{ $addr }{ $label } = $value; } if (exists $TRIPLE_LABELS{ $addr }) { return $TRIPLE_LABELS{ $addr }{ $label }; } else { return; } } sub DESTROY { my $self = shift; my $addr = refaddr( $self ); delete $TRIPLE_LABELS{ $addr }; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Union.pm000644 000765 000024 00000007402 12173312155 020606 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Union # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Union - Algebra class for Union patterns =head1 VERSION This document describes RDF::Query::Algebra::Union version 2.910. =cut package RDF::Query::Algebra::Union; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Set::Scalar; use Log::Log4perl; use Scalar::Util qw(blessed); use Carp qw(carp croak confess); ###################################################################### our ($VERSION); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new Union structure. =cut sub new { my $class = shift; my $left = shift; my $right = shift; return bless( [ 'UNION', $left, $right ], $class ); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->first, $self->second); } =item C<< first >> Returns the first pattern (LHS) of the union. =cut sub first { my $self = shift; return $self->[1]; } =item C<< second >> Returns the second pattern (RHS) of the union. =cut =item C<< patterns >> Returns the two patterns belonging to the UNION pattern. =cut sub patterns { my $self = shift; return ($self->first, $self->second); } sub second { my $self = shift; return $self->[2]; } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $prefix = shift || ''; my $indent = $context->{indent} || ' '; return sprintf( "(union\n${prefix}${indent}%s\n${prefix}${indent}%s)", $self->first->sse( $context, "${prefix}${indent}" ), $self->second->sse( $context, "${prefix}${indent}" ) ); } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift; my $indent = shift; my $string = sprintf( "%s\n${indent}UNION\n${indent}%s", $self->first->as_sparql( { %$context, force_ggp_braces => 1 }, $indent ), $self->second->as_sparql( { %$context, force_ggp_braces => 1 }, $indent ), ); return $string; } =item C<< as_hash >> Returns the query as a nested set of plain data structures (no objects). =cut sub as_hash { my $self = shift; my $context = shift; return { type => lc($self->type), patterns => [ map { $_->as_hash } $self->patterns ], }; } =item C<< type >> Returns the type of this algebra expression. =cut sub type { return 'UNION'; } =item C<< referenced_variables >> Returns a list of the variable names used in this algebra expression. =cut sub referenced_variables { my $self = shift; return RDF::Query::_uniq($self->first->referenced_variables, $self->second->referenced_variables); } =item C<< potentially_bound >> Returns a list of the variable names used in this algebra expression that will bind values during execution. =cut sub potentially_bound { my $self = shift; return RDF::Query::_uniq($self->first->potentially_bound, $self->second->potentially_bound); } =item C<< definite_variables >> Returns a list of the variable names that will be bound after evaluating this algebra expression. =cut sub definite_variables { my $self = shift; my $seta = Set::Scalar->new( $self->first->definite_variables ); my $setb = Set::Scalar->new( $self->second->definite_variables ); return $seta->intersection( $setb )->members; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/lib/RDF/Query/Algebra/Update.pm000644 000765 000024 00000015256 12173312155 020746 0ustar00gregstaff000000 000000 # RDF::Query::Algebra::Update # ----------------------------------------------------------------------------- =head1 NAME RDF::Query::Algebra::Update - Algebra class for UPDATE operations =head1 VERSION This document describes RDF::Query::Algebra::Update version 2.910. =cut package RDF::Query::Algebra::Update; use strict; use warnings; no warnings 'redefine'; use base qw(RDF::Query::Algebra); use Data::Dumper; use Log::Log4perl; use Scalar::Util qw(refaddr); use Carp qw(carp croak confess); use Scalar::Util qw(blessed reftype refaddr); use Time::HiRes qw(gettimeofday tv_interval); use RDF::Trine::Iterator qw(smap sgrep swatch); ###################################################################### our ($VERSION); my %TRIPLE_LABELS; my @node_methods = qw(subject predicate object); BEGIN { $VERSION = '2.910'; } ###################################################################### =head1 METHODS Beyond the methods documented below, this class inherits methods from the L class. =over 4 =cut =item C Returns a new UPDATE structure. =cut sub new { my $class = shift; my $delete = shift; my $insert = shift; my $pat = shift; my $dataset = shift; my $data = shift; return bless([$delete, $insert, $pat, $dataset, $data], $class); } =item C<< construct_args >> Returns a list of arguments that, passed to this class' constructor, will produce a clone of this algebra pattern. =cut sub construct_args { my $self = shift; return ($self->delete_template, $self->insert_template, $self->pattern); } =item C<< sse >> Returns the SSE string for this algebra expression. =cut sub sse { my $self = shift; my $context = shift; my $indent = shift; my $string; my $delete = $self->delete_template; my $insert = $self->insert_template; my $dataset = $self->dataset; my @ds_keys = keys %{ $dataset || {} }; if (@ds_keys) { my @defaults = sort map { $_->sse } @{ $dataset->{default} || [] }; my @named = sort map { $_->sse } values %{ $dataset->{named} || {} }; my @strings; push(@strings, (@defaults) ? '(defaults ' . join(' ', @defaults) . ')' : ()); push(@strings, (@named) ? '(named ' . join(' ', @named) . ')' : ()); my $ds_string = '(dataset ' . join(' ', @strings) . ')'; return sprintf( "(update (delete %s) (insert %s) (where %s) %s)", ($delete ? $delete->sse( $context, $indent ) : ''), ($insert ? $insert->sse( $context, $indent ) : ''), $self->pattern->sse( $context, $indent ), $ds_string, ); } else { return sprintf( "(update (delete %s) (insert %s) (where %s))", ($delete ? $delete->sse( $context, $indent ) : ''), ($insert ? $insert->sse( $context, $indent ) : ''), $self->pattern->sse( $context, $indent ), ); } } =item C<< as_sparql >> Returns the SPARQL string for this algebra expression. =cut sub as_sparql { my $self = shift; my $context = shift || {}; my $indent = shift || ''; my $delete = $self->delete_template; my $insert = $self->insert_template; my $ggp = $self->pattern; my $dataset = $self->dataset; my @ds_keys = keys %{ $dataset || {} }; my $ds_string = ''; if (@ds_keys) { my @defaults = @{ $dataset->{default} || [] }; my %named = %{ $dataset->{named} || {} }; my @strings; push(@strings, sprintf("USING <%s>", $_->uri_value)) foreach (@defaults); push(@strings, sprintf("USING NAMED <%s>", $named{$_}->uri_value)) foreach (keys %named); $ds_string = join("\n${indent}", @strings); } if ($insert or $delete) { # TODO: $(delete|insert)->as_sparql here isn't properly serializing GRAPH blocks, because even though they contain Quad objects inside of BGPs, there's no containing NamedGraph object... if ($ds_string) { $ds_string = "\n${indent}$ds_string"; } if ($insert and $delete) { return sprintf( "DELETE {\n${indent} %s\n${indent}}\n${indent}INSERT {\n${indent} %s\n${indent}}\n${indent}%s\n${indent}WHERE %s", $delete->as_sparql( $context, "${indent} " ), $insert->as_sparql( $context, "${indent} " ), $ds_string, $ggp->as_sparql( { %$context, force_ggp_braces => 1 }, ${indent} ), ); } elsif ($insert) { return sprintf( "INSERT {\n${indent} %s\n${indent}}\n${indent}%s\n${indent}WHERE %s", $insert->as_sparql( $context, "${indent} " ), $ds_string, $ggp->as_sparql( { %$context, force_ggp_braces => 1 }, ${indent} ), ); } else { return sprintf( "DELETE {\n${indent} %s\n${indent}}\n${indent}%s\n${indent}WHERE %s", $delete->as_sparql( $context, "${indent} " ), $ds_string, $ggp->as_sparql( { %$context, force_ggp_braces => 1 }, ${indent} ), ); } } else { my @pats = $ggp->patterns; my $op = ($delete) ? 'DELETE' : 'INSERT'; my $temp = ($delete) ? $delete : $insert; my $temps = ($temp->isa('RDF::Query::Algebra::GroupGraphPattern')) ? $temp->as_sparql( $context, "${indent} " ) : "{\n${indent} " . $temp->as_sparql( $context, "${indent} " ) . "\n${indent}}"; if (scalar(@pats) == 0) { return sprintf( "${op} DATA %s", $temps ); } else { if ($ds_string) { $ds_string = "\n${indent}$ds_string\n${indent}"; } else { $ds_string = ' '; } return sprintf( "${op} %s%sWHERE %s", $temps, $ds_string, $ggp->as_sparql( { %$context, force_ggp_braces => 1 }, "${indent}" ), ); } } } =item C<< referenced_blanks >> Returns a list of the blank node names used in this algebra expression. =cut sub referenced_blanks { my $self = shift; return; } =item C<< referenced_variables >> =cut sub referenced_variables { my $self = shift; return; } =item C<< delete_template >> =cut sub delete_template { my $self = shift; return $self->[0]; } =item C<< insert_template >> =cut sub insert_template { my $self = shift; return $self->[1]; } =item C<< pattern >> =cut sub pattern { my $self = shift; return $self->[2]; } =item C<< dataset >> =cut sub dataset { my $self = shift; return $self->[3]; } =item C<< data_only >> =cut sub data_only { my $self = shift; return $self->[4]; } =item C<< check_duplicate_blanks >> Returns true if blank nodes respect the SPARQL rule of no blank-label re-use across BGPs, otherwise throws a RDF::Query::Error::QueryPatternError exception. =cut sub check_duplicate_blanks { my $self = shift; unless ($self->data_only) { # if self isn't an INSERT/DELETE DATA operation, then we need to check the template patterns, too if ($self->delete_template) { $self->delete_template->check_duplicate_blanks; } if ($self->insert_tempalte) { $self->insert_tempalte->check_duplicate_blanks; } } return $self->pattern->check_duplicate_blanks; } 1; __END__ =back =head1 AUTHOR Gregory Todd Williams =cut RDF-Query-2.910/inc/Module/000755 000765 000024 00000000000 12173312223 015324 5ustar00gregstaff000000 000000 RDF-Query-2.910/inc/Module/Install/000755 000765 000024 00000000000 12173312223 016732 5ustar00gregstaff000000 000000 RDF-Query-2.910/inc/Module/Install.pm000644 000765 000024 00000030135 12173312215 017273 0ustar00gregstaff000000 000000 #line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.06'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[1]) <=> _version($_[2]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2012 Adam Kennedy. RDF-Query-2.910/inc/Module/Install/AuthorTests.pm000644 000765 000024 00000002215 12173312215 021556 0ustar00gregstaff000000 000000 #line 1 package Module::Install::AuthorTests; use 5.005; use strict; use Module::Install::Base; use Carp (); #line 16 use vars qw{$VERSION $ISCORE @ISA}; BEGIN { $VERSION = '0.002'; $ISCORE = 1; @ISA = qw{Module::Install::Base}; } #line 42 sub author_tests { my ($self, @dirs) = @_; _add_author_tests($self, \@dirs, 0); } #line 56 sub recursive_author_tests { my ($self, @dirs) = @_; _add_author_tests($self, \@dirs, 1); } sub _wanted { my $href = shift; sub { /\.t$/ and -f $_ and $href->{$File::Find::dir} = 1 } } sub _add_author_tests { my ($self, $dirs, $recurse) = @_; return unless $Module::Install::AUTHOR; my @tests = $self->tests ? (split / /, $self->tests) : 't/*.t'; # XXX: pick a default, later -- rjbs, 2008-02-24 my @dirs = @$dirs ? @$dirs : Carp::confess "no dirs given to author_tests"; @dirs = grep { -d } @dirs; if ($recurse) { require File::Find; my %test_dir; File::Find::find(_wanted(\%test_dir), @dirs); $self->tests( join ' ', @tests, map { "$_/*.t" } sort keys %test_dir ); } else { $self->tests( join ' ', @tests, map { "$_/*.t" } sort @dirs ); } } #line 107 1; RDF-Query-2.910/inc/Module/Install/Base.pm000644 000765 000024 00000002147 12173312215 020147 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.06'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 RDF-Query-2.910/inc/Module/Install/Can.pm000644 000765 000024 00000006157 12173312215 020003 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Can; use strict; use Config (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # Check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; require File::Spec; my $abs = File::Spec->catfile($dir, $cmd); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # Can our C compiler environment build XS files sub can_xs { my $self = shift; # Ensure we have the CBuilder module $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 ); # Do we have the configure_requires checker? local $@; eval "require ExtUtils::CBuilder;"; if ( $@ ) { # They don't obey configure_requires, so it is # someone old and delicate. Try to avoid hurting # them by falling back to an older simpler test. return $self->can_cc(); } # Do we have a working C compiler my $builder = ExtUtils::CBuilder->new( quiet => 1, ); unless ( $builder->have_compiler ) { # No working C compiler return 0; } # Write a C file representative of what XS becomes require File::Temp; my ( $FH, $tmpfile ) = File::Temp::tempfile( "compilexs-XXXXX", SUFFIX => '.c', ); binmode $FH; print $FH <<'END_C'; #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int main(int argc, char **argv) { return 0; } int boot_sanexs() { return 1; } END_C close $FH; # Can the C compiler access the same headers XS does my @libs = (); my $object = undef; eval { local $^W = 0; $object = $builder->compile( source => $tmpfile, ); @libs = $builder->link( objects => $object, module_name => 'sanexs', ); }; my $result = $@ ? 0 : 1; # Clean up all the build files foreach ( $tmpfile, $object, @libs ) { next unless defined $_; 1 while unlink; } return $result; } # Can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 236 RDF-Query-2.910/inc/Module/Install/Fetch.pm000644 000765 000024 00000004627 12173312215 020333 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; RDF-Query-2.910/inc/Module/Install/Makefile.pm000644 000765 000024 00000027437 12173312215 021023 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # This previous attempted to inherit the version of # ExtUtils::MakeMaker in use by the module author, but this # was found to be untenable as some authors build releases # using future dev versions of EU:MM that nobody else has. # Instead, #toolchain suggests we use 6.59 which is the most # stable version on CPAN at time of writing and is, to quote # ribasushi, "not terminally fucked, > and tested enough". # TODO: We will now need to maintain this over time to push # the version up as new versions are released. $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 544 RDF-Query-2.910/inc/Module/Install/Metadata.pm000644 000765 000024 00000043277 12173312215 021026 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; my $value = @_ ? shift : 1; if ( $self->{values}->{dynamic_config} ) { # Once dynamic we never change to static, for safety return 0; } $self->{values}->{dynamic_config} = $value ? 1 : 0; return 1; } # Convenience command sub static_config { shift->dynamic_config(0); } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the really old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; RDF-Query-2.910/inc/Module/Install/Scripts.pm000644 000765 000024 00000001011 12173312215 020711 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Scripts; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub install_script { my $self = shift; my $args = $self->makemaker_args; my $exe = $args->{EXE_FILES} ||= []; foreach ( @_ ) { if ( -f $_ ) { push @$exe, $_; } elsif ( -d 'script' and -f "script/$_" ) { push @$exe, "script/$_"; } else { die("Cannot find script '$_'"); } } } 1; RDF-Query-2.910/inc/Module/Install/Win32.pm000644 000765 000024 00000003403 12173312215 020173 0ustar00gregstaff000000 000000 #line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; RDF-Query-2.910/inc/Module/Install/WriteAll.pm000644 000765 000024 00000002376 12173312215 021024 0ustar00gregstaff000000 000000 #line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; RDF-Query-2.910/examples/create_query_api.pl000755 000765 000024 00000003407 11707542733 021046 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use utf8; use Data::Dumper; use Scalar::Util qw(reftype blessed); use RDF::Query; my $la = RDF::Query::Node::Literal->new( 'a' ); my $l3 = RDF::Query::Node::Literal->new( '3', undef, 'http://www.w3.org/2001/XMLSchema#integer' ); my $ra = RDF::Query::Node::Resource->new( 'http://example.org/a' ); my $rb = RDF::Query::Node::Resource->new( 'http://example.org/b' ); my $va = RDF::Query::Node::Variable->new( 'a' ); my $vb = RDF::Query::Node::Variable->new( 'b' ); # construct a BGP with two triples { ?a :b "a" ; :b ?b } my $triplea = RDF::Query::Algebra::Triple->new( $va, $ra, $la ); my $tripleb = RDF::Query::Algebra::Triple->new( $va, $rb, $vb ); my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( $triplea, $tripleb ); # now add a filter, and wrap it in a GGP { ?a :b "a" ; :b ?b . FILTER(?b < 3) } my $expr = RDF::Query::Expression::Binary->new( '<', $vb, $l3 ); my $filter = RDF::Query::Algebra::Filter->new( $expr, $bgp ); my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( $filter ); # add a LIMIT clause my $limit = RDF::Query::Algebra::Limit->new( $filter, 5 ); # and a PROJECT to get the variable list my $proj = RDF::Query::Algebra::Project->new( $limit, [map {RDF::Query::Node::Variable->new($_) } qw(a b)] ); # you still need to add the method (SELECT) yourself. at some point, the code # from RDF::Query::describe, RDF::Query::construct and RDF::Query::ask will be # moved into ::Algebra subclasses, and then we won't need to do this manually. # until then, just tag on 'SELECT ' onto the front of the query string. print 'SELECT ' . $proj->as_sparql; # should print: # SELECT * WHERE { # ?a "a" . # ?a ?b . # FILTER (?b < 3) . # } # LIMIT 5 RDF-Query-2.910/examples/livejournal.pl000755 000765 000024 00000002346 11707542733 020060 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(../lib); use RDF::Query; use Data::Dumper; my $nick = scalar(@ARGV) ? shift : do { print "Nickname: "; my $n = ; chomp($n); $n }; my $url = "http://${nick}.livejournal.com/data/foaf.rdf"; my $sparql = <<"END"; PREFIX foaf: SELECT ?nick FROM <${url}> WHERE { ?person foaf:nick "${nick}"\@en ; foaf:knows ?friend . ?friend foaf:nick ?nick . } ORDER BY ?nick END my $query = RDF::Query->new( $sparql, undef, undef, 'sparql' ); warn RDF::Query->error unless ($query); my $stream = $query->execute; while (my $row = $stream->()) { my $friend = $row->{nick}; if ($friend->is_literal) { my $name = $friend->literal_value; print "$nick knows $name\n"; } } # ### If we wanted to see the RDF that was loaded from the URL # ### before the query is executed, we could add a # ### post-creatae-model hook function like this: # # RDF::Query->add_hook( # 'http://kasei.us/code/rdf-query/hooks/post-create-model', # sub { # my $query = shift; # my $bridge = shift; # my $stream = $bridge->get_statements(); # while (my $st = $stream->()) { # warn 'added statement: ' . $bridge->as_string( $st ) . "\n"; # } # } # ); RDF-Query-2.910/examples/queries/000755 000765 000024 00000000000 12173312223 016621 5ustar00gregstaff000000 000000 RDF-Query-2.910/examples/query.pl000755 000765 000024 00000003431 11707542733 016667 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use lib qw(lib); use File::Spec; use URI::file; use RDF::Query; unless (@ARGV) { print STDERR <<"END"; USAGE: $0 query.rq data.rdf [ data2.rdf ... ] Reads in a SPARQL query from query.rq, and RDF/XML data from the data.rdf files. The SPARQL query is executed against a triplestore containing data from the data files, and query results are printed to standard output. END exit; } # get the query file from the arguments array my $query_file = shift(@ARGV); #open the query file and read in the query my $sparql = do { local($/) = undef; open(my $fh, '<:utf8', $query_file); <$fh> }; # construct the query object my $query = RDF::Query->new( $sparql ); unless ($query) { # the query couldn't be constructed. print out the reason why. warn RDF::Query->error; exit; } # read in the list of files with RDF/XML content for querying my @files = map { File::Spec->rel2abs( $_ ) } @ARGV; # create a temporary triplestore, and wrap it into a model my $store = RDF::Trine::Store::DBI->temporary_store(); my $model = RDF::Trine::Model->new( $store ); # create a rdf/xml parser object that we'll use to read in the rdf data my $parser = RDF::Trine::Parser->new('rdfxml'); # loop over all the files foreach my $i (0 .. $#files) { my $file = $files[ $i ]; # $uri is the URI object used as the base uri for parsing my $uri = URI::file->new_abs( $file ); my $content = do { open( my $fh, '<', $file ); local($/) = undef; <$fh> }; $parser->parse_into_model( $uri, $content, $model ); } # execute the query against data contained in the model my $iter = $query->execute( $model ); # print the results as a string to standard output print $iter->as_string; ### or, if you want to iterator over each result row: # while (my $s = $iter->next) { # print $s . "\n"; # } RDF-Query-2.910/examples/query_url.pl000755 000765 000024 00000002637 11760736733 017564 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use RDF::Query; unless (@ARGV) { print STDERR <<"END"; USAGE: $0 query.rq http://path/to/data.rdf Reads in a SPARQL query from query.rq, and loads RDF data from the supplied URL. The SPARQL query is executed against a triplestore containing the loaded RDF, and query results are printed to standard output. END exit; } # get the query file from the arguments array my $query_file = shift(@ARGV); #open the query file and read in the query my $sparql = do { local($/) = undef; open(my $fh, '<:utf8', $query_file); <$fh> }; # construct the query object my $query = RDF::Query->new( $sparql ); unless ($query) { # the query couldn't be constructed. print out the reason why. warn RDF::Query->error; exit; } # create a temporary triplestore (in memory), and wrap it into a model my $store = RDF::Trine::Store::DBI->temporary_store(); my $model = RDF::Trine::Model->new( $store ); # load the RDF data from the url into the model. # the parse_url_into_model method will attempt to guess the appropriate format for parsing. my $url = shift(@ARGV); RDF::Trine::Parser->parse_url_into_model( $url, $model ); # execute the query against data contained in the model my $iter = $query->execute( $model ); # print the results as a string to standard output print $iter->as_string; ### or, if you want to iterator over each result row: # while (my $s = $iter->next) { # print $s . "\n"; # } RDF-Query-2.910/examples/service_descriptions/000755 000765 000024 00000000000 12173312223 021372 5ustar00gregstaff000000 000000 RDF-Query-2.910/examples/service_descriptions/dbpedia_org.ttl000644 000765 000024 00000002423 11707542733 024374 0ustar00gregstaff000000 000000 @prefix rdfs: . @prefix rdf: . @prefix xsd: . @prefix sd: . @prefix foaf: . @prefix sparql: . @prefix p: . # definition of an endpoint [] a sd:Service ; rdfs:label "DBpedia" ; rdfs:comment "Service for DBpedia data" ; # the endpoint url sd:url ; # capabilities of the endpoint sd:capability [ # the endpoint stores triples with predicate rdf:type sd:predicate rdf:type ; sd:triples 9417778 ; ]; sd:capability [ # the endpoint stores triples with predicate foaf:name sd:predicate foaf:name ; sd:triples 700012 ; # if the object in the triple pattern is bound # (e.g. ?s foaf:name 'Bastian Quilitz") the result size will be # reduced by factor 0.02. (on average) sd:objectSelectivity 1.4e-06 ; ] ; # whether the service is definitive or not # sd:isDefinitive (default=false) sd:isDefinitive false ; # total number of triples in the store sd:totalTriples 58787090 ; sparql:pattern [ a foaf:Person ; p:name [] ; foaf:name [] ; p:field [] ; p:wordnet_type [] ] ; . RDF-Query-2.910/examples/service_descriptions/kasei_us.ttl000644 000765 000024 00000010247 11707542733 023743 0ustar00gregstaff000000 000000 @prefix rdfs: . @prefix rdf: . @prefix xsd: . @prefix sd: . @prefix foaf: . @prefix saddle: . @prefix sparql: . @prefix geo: . @prefix exif: . @prefix dc: . @prefix dcterms: . # definition of an endpoint [] a sd:Service ; rdfs:label "SPARQL Endpoint for kasei.us" ; foaf:maker [ a foaf:Person ; foaf:name "Gregory Todd Williams" ; foaf:mbox_sha1sum "f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8" ; rdfs:seeAlso ] ; # the endpoint url sd:url ; sd:totalTriples 3846 ; sd:isDefinitive false ; saddle:humanInterface ; saddle:queryLanguage [ saddle:spec ] ; saddle:queryLanguage [ saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/sparql-results+xml"; saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/rdf+xml"; saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/sparql-results+json"; saddle:spec ] ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; # capabilities of the endpoint sd:capability [ # the endpoint stores triples with predicate rdf:type sd:predicate rdf:type ; sd:triples 12729 ; ]; sd:capability [ sd:predicate foaf:name ] ; sd:capability [ sd:predicate foaf:mbox_sha1sum ] ; sd:capability [ sd:predicate foaf:mbox ] ; sd:capability [ sd:predicate foaf:depicts ] ; sd:capability [ sd:predicate foaf:openid ] ; sd:capability [ sd:predicate foaf:made ] ; sd:capability [ sd:predicate ] ; sd:capability [ sd:predicate ] ; sd:capability [ sd:predicate ] ; sparql:pattern [ a foaf:Person ; foaf:name [] ; foaf:mbox_sha1sum [] ; foaf:made [] ] ; sparql:pattern [ a foaf:Image ; exif:exposureTime [] ; exif:fNumber [] ; dc:date [] ; foaf:maker [] ; dcterms:spatial [ geo:lat [] ; geo:long [] ] ] ; . RDF-Query-2.910/examples/queries/sparql-ask-people.rq000644 000765 000024 00000000106 11707542733 022537 0ustar00gregstaff000000 000000 PREFIX foaf: ASK { [] a foaf:Person . } RDF-Query-2.910/examples/queries/sparql-bgp-people-knows.rq000644 000765 000024 00000000231 11707542733 023667 0ustar00gregstaff000000 000000 PREFIX foaf: SELECT ?name ?knows WHERE { ?p a foaf:Person ; foaf:name ?name ; foaf:knows ?q . ?q foaf:name ?knows . } RDF-Query-2.910/examples/queries/sparql-bgp-people.rq000644 000765 000024 00000000154 11707542733 022534 0ustar00gregstaff000000 000000 PREFIX foaf: SELECT ?p ?name WHERE { ?p a foaf:Person ; foaf:name ?name . } RDF-Query-2.910/examples/queries/sparql-ggp-person-opt-knows.rq000644 000765 000024 00000000312 11707542733 024516 0ustar00gregstaff000000 000000 PREFIX foaf: SELECT ?person ?name ?email ?knows WHERE { ?person foaf:mbox_sha1sum ?email ; foaf:name ?name ; OPTIONAL { ?person foaf:knows [ foaf:name ?knows ] } } RDF-Query-2.910/examples/queries/sparqlp-service-people-names.rq000644 000765 000024 00000000216 11707542733 024704 0ustar00gregstaff000000 000000 PREFIX foaf: SELECT * WHERE { SERVICE { [] a foaf:Person ; foaf:name ?name . } } RDF-Query-2.910/data/about.xrdf000644 000765 000024 00000012551 11707542733 016257 0ustar00gregstaff000000 000000 52.972770 Cliffs of Moher, Ireland -9.430733 NIKON CORPORATION Greg Williams 11 NIKON D70 1/500 2004-09-06T15:19:20+01:00 41.849756 Providence, RI -71.392400 NIKON CORPORATION 19fc9d0234848371668cf10a1b71ac9bd4236806 f3c455a88761f83ba243b2653e6042de71fdd149 25c5f4f21afaedf113d92ac7c8591178ad9c03fa 9d179d12b032b4689cf5dd1ffaf237c3e007c919 f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8 The Samo Fool kasei Gregory Todd Williams Greg Williams 4.5 Nikon D70 1/60 2005-04-07T18:27:37-04:00 NIKON CORPORATION Greg Williams 4.5 Nikon D70 1/60 2005-04-07T18:27:50-04:00 NIKON CORPORATION Greg Williams 4.5 Nikon D70 1/80 2005-04-07T18:27:56-04:00 RDF-Query-2.910/data/bnode-person.rdf000644 000765 000024 00000001716 11707542733 017351 0ustar00gregstaff000000 000000 Adam Pisoni 26fb6400147dcccfda59717ff861db9cb97ac5ec Gregory Todd Williams RDF-Query-2.910/data/federation_data/000755 000765 000024 00000000000 12173312223 017350 5ustar00gregstaff000000 000000 RDF-Query-2.910/data/Flower-2.rdf000644 000765 000024 00000011277 11707542733 016356 0ustar00gregstaff000000 000000 reproductive organ of angiosperm plants especially one having showy or colorful parts reproductive organ of angiosperm plants especially one having showy or colorful parts a diminutive flower (especially one that is part of a composite flower) a diminutive flower (especially one that is part of a composite flower) flower having no petals flower having no petals the flowering part of a plant or arrangement of flowers on a stalk the flowering part of a plant or arrangement of flowers on a stalk small flower with a flat strap-shaped corolla usually occupying the peripheral rings of a composite flower small flower with a flat strap-shaped corolla usually occupying the peripheral rings of a composite flower small flower with a flat strap-shaped corolla usually occupying the peripheral rings of a composite flower small flower with a flat strap-shaped corolla usually occupying the peripheral rings of a composite flower a partially opened flower a partially opened flower the flower of a chrysanthemum plant the flower of a chrysanthemum plant RDF-Query-2.910/data/foaf.xrdf000644 000765 000024 00000010577 11707542733 016066 0ustar00gregstaff000000 000000 FOAF Description for Gregory Williams Friend of a Friend (FOAF) description, containing personal metadata for Gregory Williams Sat, 4 Oct 2003 20:02:22 PDT-0700 Gregory Todd Williams Gregory Williams kasei f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8 samofool Gary P Gary P tylix 2057969209f1dfdad832de387cf13e6ff8c93b12 Lauren B Lauren B f272c39785d651d0776093f3d28aadfd0719e787 Liz F Elizabeth F f8677979059b73385c9d14cadf7d1e3652b205a8 1 2 3 type The subject is an instance of a class. RDF-Query-2.910/data/greenwich.rdf000644 000765 000024 00000001374 11707542733 016731 0ustar00gregstaff000000 000000 51.477222 0.000000 Royal Observatory Greenwich RDF-Query-2.910/data/named_graphs/000755 000765 000024 00000000000 12173312223 016667 5ustar00gregstaff000000 000000 RDF-Query-2.910/data/rdfa-test.xhtml000644 000765 000024 00000000640 11707542733 017223 0ustar00gregstaff000000 000000 Test 0001

This photo was taken by Mark Birbeck.

RDF-Query-2.910/data/service-kasei.ttl000644 000765 000024 00000010246 11707542733 017536 0ustar00gregstaff000000 000000 @prefix rdfs: . @prefix rdf: . @prefix xsd: . @prefix sd: . @prefix foaf: . @prefix saddle: . @prefix sparql: . @prefix geo: . @prefix exif: . @prefix dc: . @prefix dcterms: . # definition of an endpoint [] a sd:Service ; rdfs:label "SPARQL Endpoint for kasei.us" ; foaf:maker [ a foaf:Person ; foaf:name "Gregory Todd Williams" ; foaf:mbox_sha1sum "f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8" ; rdfs:seeAlso ] ; # the endpoint url sd:url ; sd:totalTriples 3846 ; sd:isDefinitive false ; saddle:humanInterface ; saddle:queryLanguage [ saddle:spec ] ; saddle:queryLanguage [ saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/sparql-results+xml"; saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/rdf+xml"; saddle:spec ] ; saddle:resultFormat [ saddle:mediaType "application/sparql-results+json"; saddle:spec ] ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:extensionFunction ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; sparql:sparqlExtension ; # capabilities of the endpoint sd:capability [ # the endpoint stores triples with predicate rdf:type sd:predicate rdf:type ; # sd:triples 3683409 ; ]; sd:capability [ sd:predicate foaf:name ] ; sd:capability [ sd:predicate foaf:mbox_sha1sum ] ; sd:capability [ sd:predicate foaf:mbox ] ; sd:capability [ sd:predicate foaf:depicts ] ; sd:capability [ sd:predicate foaf:openid ] ; sd:capability [ sd:predicate foaf:made ] ; sd:capability [ sd:predicate ] ; sd:capability [ sd:predicate ] ; sd:capability [ sd:predicate ] ; sparql:pattern [ a foaf:Person ; foaf:name [] ; foaf:mbox_sha1sum [] ; foaf:made [] ] ; sparql:pattern [ a foaf:Image ; exif:exposureTime [] ; exif:fNumber [] ; dc:date [] ; foaf:maker [] ; dcterms:spatial [ geo:lat [] ; geo:long [] ] ] ; . RDF-Query-2.910/data/service.ttl000644 000765 000024 00000005203 11707542733 016441 0ustar00gregstaff000000 000000 @prefix rdfs: . @prefix rdf: . @prefix xsd: . @prefix sd: . @prefix foaf: . @prefix sparql: . @prefix p: . # definition of an endpoint [] a sd:Service ; rdfs:label "DBpedia" ; rdfs:comment "Service for DBpedia data" ; # the endpoint url sd:url ; # capabilities of the endpoint sd:capability [ # the endpoint stores triples with predicate rdf:type sd:predicate rdf:type ; # Restriction on the subject/object # Every legal SPARQL filter expression is allowed. # only queries for the type http://xmlns.com/foaf/0.1/Person # are allowed sd:sofilter "REGEX(STR(?object),'http://xmlns.com/foaf/0.1/Person')" ; # could also use ?subject # statistical information # number of triples that will be returned by # a "?s a foaf:Person ; ?p ?o" query sd:triples 3683409 ; # other propeties are: # Selectivity of a triple pattern, when object/subject is bound # sd:objectSelectivity (default=1) # sd:subjectSelectivity (default=1/x, # where x is the value given by sd:triples) ]; sd:capability [ # the endpoint stores triples with predicate foaf:name sd:predicate foaf:name ; # no filter on subject or object sd:sofilter "" ; # statistical information # there are 18000 triples with predicate foaf:name sd:triples 18000 ; # if the object in the triple pattern is bound # (e.g. ?s foaf:name 'Bastian Quilitz") the result size will be # reduced by factor 0.02. (on average) sd:objectSelectivity "0.02"^^xsd:double ; ] ; sd:capability [ sd:predicate foaf:mbox ; sd:sofilter "" ; sd:triples 18000 ; sd:objectSelectivity 5.5E-5 ] ; sd:capability [ sd:predicate ] ; sd:capability [ a sparql:any_triple ] ; # whether the service is definitive or not # sd:isDefinitive (default=false) sd:isDefinitive "true"^^xsd:boolean ; # limitations on access patterns # the query for this service must either contain a triple pattern # with predicate foaf:name and a bound object or # a pattern with predicate foaf:mbox and a bound object. # not shown here: sd:subjectBinding -> subject must be bound sd:requiredBindings [ sd:objectBinding foaf:name ] ; sd:requiredBindings [ sd:objectBinding foaf:mbox ] ; # total number of triples in the store sd:totalTriples 58787090 ; sparql:pattern [ a foaf:Person ; p:name [] ; foaf:name [] ; p:field [] ; p:wordnet_type [] ] ; . RDF-Query-2.910/data/t-sparql11-aggregates-1.rdf000644 000765 000024 00000003327 11707542733 021130 0ustar00gregstaff000000 000000 9 5 7 7 RDF-Query-2.910/data/temporal.rdf000644 000765 000024 00000004536 11707542733 016604 0ustar00gregstaff000000 000000 1975-01-01 1999-12-31 Alice 2000-01-01 Eve 2000-01-01 2001-01-01 2000-01-01 2003-01-01 RDF-Query-2.910/data/named_graphs/alice.rdf000644 000765 000024 00000000503 11707542733 020454 0ustar00gregstaff000000 000000 Alice RDF-Query-2.910/data/named_graphs/alice.ttl000644 000765 000024 00000000162 11707542733 020505 0ustar00gregstaff000000 000000 @prefix foaf: . _:a foaf:name "Alice" . _:a foaf:mbox . RDF-Query-2.910/data/named_graphs/bob.rdf000644 000765 000024 00000000506 11707542733 020144 0ustar00gregstaff000000 000000 Bob RDF-Query-2.910/data/named_graphs/bob.ttl000644 000765 000024 00000000165 11707542733 020175 0ustar00gregstaff000000 000000 @prefix foaf: . _:a foaf:name "Bob" . _:a foaf:mbox . RDF-Query-2.910/data/named_graphs/meta.rdf000644 000765 000024 00000001260 11707542733 020326 0ustar00gregstaff000000 000000 Test data for Alice Alice Test data for Bob Bob RDF-Query-2.910/data/named_graphs/meta.ttl000644 000765 000024 00000000333 11707542733 020356 0ustar00gregstaff000000 000000 @prefix foaf: . @prefix dc: . dc:title "Test data for Alice"; foaf:topic "Alice" . dc:title "Test data for Bob"; foaf:topic "Bob" . RDF-Query-2.910/data/named_graphs/repeats1.rdf000644 000765 000024 00000000371 11707542733 021126 0ustar00gregstaff000000 000000 RDF-Query-2.910/data/named_graphs/repeats2.rdf000644 000765 000024 00000000444 11707542733 021130 0ustar00gregstaff000000 000000 Label RDF-Query-2.910/data/federation_data/alice.rdf000644 000765 000024 00000000643 11707542733 021142 0ustar00gregstaff000000 000000 Alice RDF-Query-2.910/data/federation_data/alice_bob.rdf000644 000765 000024 00000001226 11707542733 021762 0ustar00gregstaff000000 000000 Alice Bob RDF-Query-2.910/data/federation_data/bob.rdf000644 000765 000024 00000000627 11707542733 020631 0ustar00gregstaff000000 000000 Bob RDF-Query-2.910/bin/dawg11-status.pl000755 000765 000024 00000006510 11707542733 017062 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'uninitialized'; use RDF::Trine; use RDF::Query; use RDF::Trine::Error qw(:try); use Scalar::Util qw(blessed); my @manifests = glob( "xt/dawg11/*/manifest.ttl" ); my @files = scalar(@ARGV) ? @ARGV : glob('earl*11.ttl'); my $model = RDF::Trine::Model->temporary_model; my $parser = RDF::Trine::Parser->new('turtle'); foreach my $f (@files, @manifests) { try { my $base = "file://"; if ($f =~ /manifest/) { my ($dir) = ($f =~ m{xt/dawg11/([^/]+)/manifest.ttl}); $base = "http://www.w3.org/2009/sparql/docs/tests/data-sparql11/${dir}/manifest#"; } $parser->parse_file_into_model( $base, $f, $model ); } catch Error with { my $e = shift; }; } my $query = RDF::Query->new(<<"END"); PREFIX rdfs: PREFIX earl: PREFIX dt: SELECT ?test ?outcome ?approval ?comment WHERE { [] earl:test ?test ; earl:result ?result . ?result earl:outcome ?outcome . OPTIONAL { ?result rdfs:comment ?comment } OPTIONAL { ?test dt:approval ?approval } } ORDER BY ?test END my $iter = $query->execute( $model ); open( my $git, 'git log|' ); my $rev; if ($git) { my $line; do { $line = <$git>; } until ($line =~ /^commit (.{7})/); if (length($1)) { $rev = "$1"; } close($git); } my $date = scalar(gmtime) . ' GMT'; print <<"END"; SPARQL 1.1 Evaluation Test Results

SPARQL 1.1 Test Results for RDF::Query

END my $total = 0; my $pass = 0; while (my $row = $iter->next) { my ($t, $o, $a) = map { (blessed($_) and $_->can('uri_value')) ? $_->uri_value : $_ } @{ $row }{ qw(test outcome approval comment) }; my $c = $row->{comment}; if ($c) { $c = $c->literal_value; } $t =~ s{http://www.w3.org/2001/sw/DataAccess/tests/data-r2/}{}; $t =~ s{http://www.w3.org/2009/sparql/docs/tests/data-sparql11/}{}; $t =~ s{file:///Users/samofool/data/prog/git/perlrdf/RDF-Query/xt/dawg11/}{}; $a =~ s{http://www.w3.org/2001/sw/DataAccess/tests/test-dawg#}{}; $o =~ s{http://www.w3.org/ns/earl#}{}; $total++; $pass++ if ($o eq 'pass'); print <<"END"; END } my $perc = sprintf('%.1f', (100*$pass/$total)); print <<"END";
Test Status Result
$t $a $o

Passed $pass of $total tests (${perc}%).

END RDF-Query-2.910/bin/deparse.pl000755 000765 000024 00000004533 12065422135 016072 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(../lib lib); use Data::Dumper; use RDF::Query; use RDF::Query::Error qw(:try); use RDF::Query::Util; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my $sparql = 0; my $algebra = 0; my $plan = 0; my $explain = 0; my $spin = 0; my $canon = 0; my $endpoint; while ($ARGV[0] =~ /^-([cEapPsS])$/) { $algebra = 1 if ($1 eq 'a'); $plan = 1 if ($1 eq 'p'); $explain = 1 if ($1 eq 'P'); $sparql = 1 if ($1 eq 's'); $spin = 1 if ($1 eq 'S'); $canon = 1 if ($1 eq 'c'); shift(@ARGV); if ($1 eq 'E') { $endpoint = shift(@ARGV); } } $sparql = 1 unless ($algebra || $plan || $sparql || $spin || $explain); unshift(@ARGV, '-w'); my $query; try { local($Error::Debug) = 1; $query = &RDF::Query::Util::cli_make_query( $canon ? (canonicalize => 1) : () ) or die RDF::Query->error; } catch RDF::Query::Error with { my $e = shift; warn $e->stacktrace; exit; }; if ($query) { if ($sparql) { print "\n# SPARQL:\n"; print $query->as_sparql . "\n"; } if ($algebra) { print "\n# Algebra:\n"; print $query->pattern->sse . "\n"; } if ($spin) { print "\n# SPIN:\n"; my $model = RDF::Trine::Model->temporary_model; $query->pattern->as_spin( $model ); my $spin = RDF::Trine::Namespace->new('http://spinrdf.org/spin#'); my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); my $s = RDF::Trine::Serializer::Turtle->new( namespaces => { spin => $spin, rdf => $rdf } ); $s->serialize_model_to_file( \*STDOUT, $model ); } my $model; if ($endpoint) { my $store = RDF::Trine::Store::SPARQL->new( $endpoint ); $model = RDF::Trine::Model->new( $store ); } else { $model = RDF::Trine::Model->temporary_model; } if ($explain) { print "\n# Plan:\n"; my ($plan, $ctx) = $query->prepare( $model ); print $plan->explain(" ", 0); } if ($plan) { print "\n# Plan:\n"; my ($plan, $ctx) = $query->prepare( $model ); print $plan->sse . "\n"; } print "\n"; } RDF-Query-2.910/bin/failing_earl_tests.sh000755 000765 000024 00000000412 12054225575 020303 0ustar00gregstaff000000 000000 #!/bin/tcsh foreach i (earl*.ttl) if (-e $i) then roqet -q -e "PREFIX earl: SELECT ?test FROM <${i}> WHERE { [ earl:test ?test ; earl:result [ earl:outcome earl:failed ] ] } ORDER BY ?test" | cut -d '<' -f 2 | cut -d '>' -f 1 endif end RDF-Query-2.910/bin/fedoptimize.pl000755 000765 000024 00000003123 11707542733 016771 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t lib .. ../t ../lib); use RDF::Query::Util; use RDF::Query::Federate; use List::Util qw(first); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.util = DEBUG, Screen # log4perl.category.rdf.query.federate = TRACE, Screen # log4perl.category.rdf.query.plan.service = DEBUG, Screen # log4perl.category.rdf.query.plan.triple = TRACE, Screen # log4perl.category.rdf.query.model = DEBUG, Screen # log4perl.category.rdf.query.servicedescription = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ unless (@ARGV) { print <<"END"; USAGE: perl $0 [-F path/to/service_description.ttl] query.rq Attempts to optimize a federated SPARQL query using the endpoints described in the specified service descriptions. Each optimized query plan is printed as output. END exit; } my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; my $model = &RDF::Query::Util::cli_make_model; my $context = RDF::Query::ExecutionContext->new( bound => {}, model => $model, query => $query, optimize => 1, ); my @plans = $query->query_plan( $context ); my %plans; foreach my $i (0 .. $#plans) { my $name = "plan $i"; print "$name: " . $plans[$i]->sse( {}, ' 'x8 ) . "\n"; } RDF-Query-2.910/bin/fedquery.pl000755 000765 000024 00000002741 11707542733 016303 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t lib .. ../t ../lib); use RDF::Query::Util; use RDF::Query::Federate; use List::Util qw(first); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.util = DEBUG, Screen # log4perl.category.rdf.query.federate = TRACE, Screen # log4perl.category.rdf.query.plan.service = DEBUG, Screen # log4perl.category.rdf.query.plan.triple = TRACE, Screen # log4perl.category.rdf.query.model = DEBUG, Screen # log4perl.category.rdf.query.servicedescription = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ unless (@ARGV) { print <<"END"; USAGE: perl $0 query.rq [ service.rdf, ... ] Executes a SPARQL query against the endpoints described in the specified service descriptions. END exit; } my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; my @files = @ARGV; foreach my $file (@files) { my $uri = URI::file->new_abs( $file ); my $sd = RDF::Query::ServiceDescription->new_from_uri( $uri ); $query->add_service( $sd ); } my ($plan, $ctx) = $query->prepare(); my $iter = $query->execute_plan( $plan, $ctx ); while (my $row = $iter->next) { print "$row\n"; } RDF-Query-2.910/bin/graph-bgp.pl000755 000765 000024 00000003435 11707542733 016327 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t lib .. ../t ../lib); use RDF::Query; use RDF::Query::Util; unless (@ARGV) { print <<"END"; USAGE: perl $0 query.rq Graph a BasicGraphPattern's variable connectivity. END exit; } use GraphViz; use List::Util qw(first); use Time::HiRes qw(tv_interval gettimeofday); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.federate = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; my ($bgp) = $query->pattern->subpatterns_of_type('RDF::Query::Algebra::BasicGraphPattern'); my @triples = $bgp->triples; my %vars; my $g = new GraphViz (directed => 0, layout => 'circo'); foreach my $i (0 .. $#triples) { my $t = $triples[ $i ]; $g->add_node( $i, label => "$i" ); warn "$i\t" . $t->as_sparql() . "\n"; my @nodes = $t->nodes; foreach my $n (@nodes) { if ($n->isa('RDF::Query::Node::Variable')) { push( @{ $vars{ $n->name } }, $i ); } elsif ($n->isa('RDF::Query::Node::Blank')) { push( @{ $vars{ '_:' . $n->blank_identifier } }, $i ); } } } foreach my $v (keys %vars) { my @triples = @{ $vars{ $v } }; while (scalar(@triples) > 1) { my $t = shift(@triples); foreach my $j (0 .. $#triples) { my $u = $triples[ $j ]; $g->add_edge( $t => $u, label => "$v" ); } } } open( my $fh, '>', "bgp.png" ) or die $!; print {$fh} $g->as_png; close($fh); if (my $opener = $ENV{PNG_VIEWER}) { system($opener, 'bgp.png'); } RDF-Query-2.910/bin/graph-qeps.pl000755 000765 000024 00000004104 11707542733 016521 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t lib .. ../t ../lib); require "t/models.pl"; unless (@ARGV) { print <<"END"; USAGE: perl $0 data.rdf [ \$MBOX_SHA ] Benchmarks a simple 2-triple BGP execution, trying all possible query plans. To run this benchmark with your own data, pass \$MBOX_SHA as a valid foaf:mbox_sha1sum value that is present in data.rdf. END exit; } our $MBOX_SHA = 'f80a0f19d2a0897b89f48647b2fb5ca1f0bc1cb8'; my @files = @ARGV; my @models = test_models( @files ); use RDF::Query; use GraphViz; use List::Util qw(first); use Time::HiRes qw(tv_interval gettimeofday); use Benchmark qw(cmpthese); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.algebra = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my ($model) = first { $_->isa('RDF::Trine::Model') } @models; my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; my $context = RDF::Query::ExecutionContext->new( bound => {}, model => $model, query => $query, optimize => 1, ); my @plans = $query->query_plan( $context ); my %plans; foreach my $i (0 .. $#plans) { my $name = "plan $i"; warn "$name: " . $plans[ $i ]->sse( {}, ' 'x8 ) . "\n"; my $g = new GraphViz; my $plan = $plans[ $i ]; $plan->graph( $g ); open( my $fh, '>', "qep-${i}.png" ) or die $!; print {$fh} $g->as_png; close($fh); $plans{ $name } = sub { local($query->{plan_index}) = $i; my $stream = $query->execute( $model ); my @res = $stream->get_all; }; } cmpthese( 15, \%plans ); package RDF::Query::Benchmark; use strict; use warnings; use base qw(RDF::Query); sub prune_plans { my $self = shift; my $context = shift; my @plans = @_; my $index = $self->{ plan_index }; my $plan = $plans[ $index ]; return $plan; } RDF-Query-2.910/bin/graph-query.pl000755 000765 000024 00000002635 11707542733 016725 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(. t lib .. ../t ../lib); require "t/models.pl"; use RDF::Query::Util; use RDF::Query::Federate; use GraphViz; use List::Util qw(first); use Time::HiRes qw(tv_interval gettimeofday); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.federate = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ unless (@ARGV) { print <<"END"; USAGE: perl $0 query.rq [ service.rdf, ... ] Graph the QEP produced for a query. END exit; } my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; my @files = @ARGV; my @models = test_models(); my ($model) = first { $_->isa('RDF::Trine::Model') } @models; foreach my $file (@files) { my $uri = URI::file->new_abs( $file ); my $sd = RDF::Query::ServiceDescription->new_from_uri( $uri ); $query->add_service( $sd ); } my ($plan, $ctx) = $query->prepare( $model ); my $g = new GraphViz; $plan->graph( $g ); open( my $fh, '>', "qep.png" ) or die $!; print {$fh} $g->as_png; close($fh); warn $plan->sse({}, ''); if (my $opener = $ENV{PNG_VIEWER}) { system($opener, 'qep.png'); } RDF-Query-2.910/bin/hexastore_query.pl000755 000765 000024 00000002311 11707542733 017677 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use URI::file; use lib qw(. t lib .. ../t ../lib); unless (@ARGV) { print <<"END"; USAGE: perl $0 data.hxs query.rq END exit; } use RDF::Query; use RDF::Trine::Store::Hexastore; use List::Util qw(first); use Time::HiRes qw(tv_interval gettimeofday); use Benchmark; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.algebra = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ my $file = shift; my $sparql = do { local($/) = undef; <> }; my $store = RDF::Trine::Store::Hexastore->load( $file ); my $model = RDF::Trine::Model->new( $store ); #DB::enable_profile(); my $query = new RDF::Query ( $sparql, undef, undef, 'sparql', optimize => 1 ); my ($p,$c) = $query->prepare( $model ); my $stream = $query->execute_plan( $p, $c ); while (my $r = $stream->next) { print "$r\n"; } #DB::disable_profile(); RDF-Query-2.910/bin/json.pl000755 000765 000024 00000001074 11707542733 015426 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(../lib lib); use JSON; use Data::Dumper; use RDF::Query; use RDF::Query::Util; my $json = new JSON; my $query = &RDF::Query::Util::cli_make_query or die RDF::Query->error; unless ($query) { warn "Failed to construct query object: " . RDF::Query->error; exit; } # my $pattern = $query->pattern; my ($pattern) = $query->pattern->subpatterns_of_type( 'RDF::Query::Algebra::GroupGraphPattern' ); my $hash = $pattern->as_hash; # print $json->pretty->encode($hash); print $json->encode($hash); RDF-Query-2.910/bin/open-test-files.pl000755 000765 000024 00000002706 11707542733 017476 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use lib "$ENV{HOME}/data/prog/git/perlrdf/RDF-Trine/lib"; use lib "$ENV{HOME}/data/prog/git/perlrdf/RDF-Query/lib"; use RDF::Trine; use RDF::Query; use File::Spec; use File::Slurp; use Data::Dumper; my $editor = $ENV{EDITOR} || '/usr/bin/bbedit'; while (my $test = shift) { my $file = $test; $file =~ s/#.*$/.ttl/; $file =~ s#^#xt/dawg/data-r2/#; my $parse = RDF::Trine::Parser->new('turtle'); my $store = RDF::Trine::Store->temporary_store(); my $model = RDF::Trine::Model->new( $store ); my $parser = RDF::Trine::Parser->new('turtle'); my $rdf = read_file( $file ); my $uri = 'file://' . File::Spec->rel2abs( $file ); $parser->parse_into_model ( $uri, $rdf, $model ); my $sparql = <<"END"; PREFIX mf: PREFIX qt: PREFIX earl: SELECT ?data ?query ?result WHERE { ?test mf:name ?name ; mf:action [ qt:data ?data ; qt:query ?query ] ; mf:result ?result . FILTER( REGEX(STR(?test), "${test}") ) } END my $query = RDF::Query->new( $sparql ) or warn RDF::Query->error; my $iter = $query->execute( $model ); while (my $row = $iter->next) { my @nodes = map { $_->uri_value } grep { $_->isa('RDF::Trine::Node::Resource') } values %$row; for (@nodes) { substr($_, 0, index($_, "data-r2/")+8) = "xt/dawg/data-r2/"; } system( $editor, @nodes ); } } RDF-Query-2.910/bin/parse.pl000755 000765 000024 00000000606 11707542733 015567 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use File::Spec; use Data::Dumper; use lib qw(../lib lib); use RDF::Query; use RDF::Query::Util; unless (@ARGV) { print STDERR <<"END"; USAGE: $0 -e 'SELECT * ...' USAGE: $0 query.rq END exit; } my $query = &RDF::Query::Util::cli_make_query; if ($query) { print $query->sse . "\n"; } else { warn RDF::Query->error; } RDF-Query-2.910/bin/parse_profile.pl000755 000765 000024 00000001245 11707542733 017307 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; my %times; my %elapsed; while (<>) { next unless m#([<>]) (\S+) ([.0-9]+)#; my $enter = ($1 eq '>'); my $sub = $2; my $time = $3; if ($enter) { push( @{ $times{ $sub } }, $time ); } else { my $start = pop( @{ $times{ $sub } } ); my $elapsed = $time - $start; $elapsed{ $sub }[0]++; $elapsed{ $sub }[1] += $elapsed; } } print "Calls\tS\t\tms/C\t\tSub\n"; foreach my $sub (sort { $elapsed{$b}[1] <=> $elapsed{$a}[1] } (keys %elapsed)) { my $calls = $elapsed{$sub}[0]; my $cum = $elapsed{$sub}[1]; my $cumc = 1000 * $cum / $calls; printf("%d\t%f\t%f\t%s\n", $calls, $cum, $cumc, $sub); } RDF-Query-2.910/bin/passing_earl_tests.sh000755 000765 000024 00000000412 12054225575 020336 0ustar00gregstaff000000 000000 #!/bin/tcsh foreach i (earl*.ttl) if (-e $i) then roqet -q -e "PREFIX earl: SELECT ?test FROM <${i}> WHERE { [ earl:test ?test ; earl:result [ earl:outcome earl:passed ] ] } ORDER BY ?test" | cut -d '<' -f 2 | cut -d '>' -f 1 endif end RDF-Query-2.910/bin/query.pl000755 000765 000024 00000003240 11711564475 015621 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; use lib qw(lib); use File::Spec; use URI::file; use RDF::Query; use RDF::Query::Util; binmode( \*STDOUT, ':utf8' ); unless (@ARGV) { print STDERR <<"END"; USAGE: $0 -e 'SELECT * ...' data.rdf [ ... ] $0 -E http://service.example/sparql -e 'SELECT * ...' $0 -F path/to/service_description.ttl -e 'SELECT * ...' $0 query.rq data.rdf Reads in a SPARQL query from query.rq (or specified with the -e flag), and RDF/XML data from the data.rdf files. The SPARQL query is executed against a triplestore containing data from the data files, and query results are printed to standard output. END exit; } ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.util = DEBUG, Screen # log4perl.category.rdf.query.plan.thresholdunion = TRACE, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ # construct a query from the command line arguments my ($query, $model) = &RDF::Query::Util::cli_make_query_and_model; unless ($query and $model) { die RDF::Query->error; } # execute the query against data contained in the model my $iter = $query->execute( $model ); # print the results as a string to standard output print $iter->as_string; ### this will allow the results to be printed in a streaming fashion: ### or, if you want to iterate over each result row: # while (my $s = $iter->next) { # print $s . "\n"; # } RDF-Query-2.910/bin/rqsh000755 000765 000024 00000026254 12054225575 015025 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(../lib lib); use Cwd; use Benchmark; use Scalar::Util qw(blessed); use Data::Dumper; use RDF::Query; use RDF::Query::Error qw(:try); use RDF::Query::Util; use Term::ReadLine; use Term::ReadKey; use LWP::Simple; use LWP::MediaTypes qw(add_type); ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ add_type( 'application/rdf+xml' => qw(rdf xrdf rdfx) ); add_type( 'text/turtle' => qw(ttl) ); add_type( 'text/plain' => qw(nt) ); add_type( 'text/x-nquads' => qw(nq) ); add_type( 'text/json' => qw(json) ); add_type( 'text/html' => qw(html xhtml htm) ); $| = 1; my $model; if (scalar(@ARGV) and $ARGV[ $#ARGV ] =~ /.sqlite/) { my $file = pop(@ARGV); my $dsn = "DBI:SQLite:dbname=" . $file; my $store = RDF::Trine::Store::DBI->new($model, $dsn, '', ''); $model = RDF::Trine::Model->new( $store ); } else { $model = memory_model(); } my %args = &RDF::Query::Util::cli_parse_args(); unless (exists $args{update}) { $args{update} = 1; } $args{ base } = 'file://' . getcwd . '/'; my $NAMESPACES = $RDF::Query::Util::PREFIXES; my $vb_format = 'table'; my $def_format = 'ntriples'; my $serializer = RDF::Trine::Serializer->new($def_format); my %serializer = ($def_format => $serializer); my $class = delete $args{ class } || 'RDF::Query'; my $term = Term::ReadLine->new('rqsh', \*STDIN, \*STDOUT); print "rqsh v1.0, RDF::Query v${RDF::Query::VERSION}\n\n"; while ( defined ($_ = $term->readline('rqsh> ')) ) { my $line = $_; next unless (length($line)); handle_cmd( $line ); } sub handle_cmd { my $line = shift; if ($line =~ /help/i) { help(); } elsif ($line =~ /^set (prefix (\w+): <([^>]+)>)/i) { my $query = RDF::Query->new( "$1 SELECT * WHERE {}" ); if ($query) { $NAMESPACES .= "PREFIX $2: <$3>\n"; } else { warn RDF::Query->error; } } elsif ($line =~ /^time (.*)/i) { timethis( 1, sub { handle_cmd( $1 ); } ); } elsif ($line =~ /^explain (.*)$/i) { explain($model, $term, $1); } elsif ($line =~ /^parse (.*)$/i) { parse($model, $term, $1); } elsif ($line =~ /^use (\w+)\s*;?\s*$/i) { my $name = $1; my $nmodel = model( $name ); if ($nmodel) { $model = $nmodel; } } elsif ($line =~ /init/i) { init( $model, $term, $line ); } elsif ($line =~ m/^results (table|srx)$/i) { $vb_format = lc($1); } elsif ($line =~ m/^serializer (\w+)$/i) { if (exists($serializer{ $1 })) { $serializer = $serializer{ $1 }; } else { my $ser; try { $ser = RDF::Trine::Serializer->new( $1 ); } catch RDF::Trine::Error::SerializationError with {}; if ($ser) { $serializer{ $1 } = $ser; $serializer = $ser; } else { print "Unrecognized serializer name '$1'\n"; print "Valid serializers are:\n"; foreach my $name (RDF::Trine::Serializer->serializer_names) { print " $name\n"; } print "\n"; } } } elsif ($line =~ /debug/i) { debug( $model, $term, $line ); } elsif ($line =~ /^execute <([^>]+)>$/) { my $url = URI->new_abs( $1, $args{ base } ); my $query = get( $url ); query( $model, $term, $query ); } else { query( $model, $term, $line ); } } sub help { print <<"END"; Commands: help Show this help information. use [backend] Switch the storage backend (e.g. "use mysql"). init Initialize the storage backend (creating necessary indexes, etc.). set prefix [ns]: [uri] Set a namespace for use in subsequent queries. results (table|srx) Set the serializer used for tabular variable binding results. serializer [format] Set the serializer used for RDF results (e.g. "serializer turtle"). debug Print all the quads in the storage backend. parse [sparql] Print the parsed algebra for the SPARQL 1.1 query/update. explain [sparql] Explain the execution plan for the SPARQL 1.1 query/update. time [command] Time the execution of the command. execute Execute the SPARQL update/query obtained by dereferencing URI. SELECT ... Execute the SPARQL 1.1 query. ASK ... Execute the SPARQL 1.1 query. CONSTRUCT ... Execute the SPARQL 1.1 query. DESCRIBE ... Execute the SPARQL 1.1 query. INSERT ... Execute the SPARQL 1.1 update. DELETE ... Execute the SPARQL 1.1 update. LOAD Execute the SPARQL 1.1 update. CLEAR ... Execute the SPARQL 1.1 update. COPY ... Execute the SPARQL 1.1 update. MOVE ... Execute the SPARQL 1.1 update. END } sub init { my $model = shift; my $term = shift; my $line = shift; if (my $store = $model->_store) { $store->init; } } sub model { my $name = shift; my $sclass = RDF::Trine::Store->class_by_name( $name ); if ($sclass) { if ($sclass eq 'RDF::Trine::Store::Memory') { $model = memory_model(); return; } else { if ($sclass->can('_config_meta')) { my $meta = $sclass->_config_meta; my $keys = $meta->{required_keys}; my $config = { storeclass => $sclass }; foreach my $k (@$keys) { get_value( $term, $meta, $k, $config ); } my $store = eval { $sclass->new_with_config( $config ) }; if ($store) { my $m = RDF::Trine::Model->new( $store ); if ($m) { return $m; } } print "Failed to construct '$name'-backed model. $@\n"; return; } else { print "Cannot construct model from '$name' storage class.\n"; } } } else { print "No storage class named '$name' found\n"; return; } } sub explain { my $model = shift; my $term = shift; my $sparql = shift; my $psparql = join("\n", $NAMESPACES, $sparql); my $query = $class->new( $psparql, \%args ); unless ($query) { print "Error: " . RDF::Query->error . "\n"; return; } my ($plan, $ctx) = $query->prepare( $model ); print $plan->explain(' ', 0); } sub parse { my $model = shift; my $term = shift; my $sparql = shift; my $psparql = join("\n", $NAMESPACES, $sparql); my $query = $class->new( $psparql, \%args ); unless ($query) { print "Error: " . RDF::Query->error . "\n"; return; } my $pattern = $query->pattern; print $pattern->sse . "\n"; } sub query { my $model = shift; my $term = shift; my $sparql = shift; my $psparql; if ($sparql =~ /^BASE <([^>]+)>/i) { $sparql =~ s/^BASE <([^>]+)>/BASE <$1>\n$NAMESPACES/; $psparql = $sparql; } else { $psparql = join("\n", $NAMESPACES, $sparql); } my $query = $class->new( $psparql, \%args ); unless ($query) { print "Error: " . RDF::Query->error . "\n"; return; } $term->addhistory($sparql); try { my ($plan, $ctx) = $query->prepare($model); my $iter = $query->execute_plan( $plan, $ctx ); my $count = -1; if (blessed($iter)) { if ($iter->isa('RDF::Trine::Iterator::Graph')) { $serializer->serialize_iterator_to_file( $term->OUT, $iter ); } else { if ($vb_format eq 'srx') { print $iter->as_xml( 0 ); } else { print $iter->as_string( 0, \$count ); } } } if ($plan->is_update) { my $size = $model->size; print "$size statements\n"; } elsif ($count >= 0) { print "$count results\n"; } } catch RDF::Query::Error with { my $e = shift; print "Error: $e\n"; } otherwise { warn "died: " . Dumper(\@_); }; } sub debug { my $model = shift; my $term = shift; my $line = shift; print "# model = $model\n"; if (my $store = $model->_store) { print "# store = $store\n"; } my $iter = $model->get_statements( undef, undef, undef, undef ); my @rows; my @names = qw[subject predicate object context]; while (my $row = $iter->next) { push(@rows, [map {$row->$_()->as_string} @names]); } my @rule = qw(- +); my @headers = (\q"| "); push(@headers, map { $_ => \q" | " } @names); pop @headers; push @headers => (\q" |"); my $table = Text::Table->new(@names); $table->rule(@rule); $table->body_rule(@rule); $table->load(@rows); print join('', $table->rule(@rule), $table->title, $table->rule(@rule), map({ $table->body($_) } 0 .. @rows), $table->rule(@rule) ); my $size = scalar(@rows); print "$size statements\n"; } sub get_value { my $term = shift; my $meta = shift; my $k = shift; my $config = shift; if (my $v = $config->{$k}) { return; } elsif (defined($meta->{fields}{$k}{'value'})) { $config->{ $k } = $meta->{fields}{$k}{'value'}; } elsif (defined($meta->{fields}{$k}{'template'})) { my $template = $meta->{fields}{$k}{'template'}; my @subkeys = ($template =~ m/\[%(\w+)%\]/g); foreach my $sk (@subkeys) { get_value( $term, $meta, $sk, $config ); } while ($template =~ m/\[%(\w+)%\]/) { my $key = $1; my $v = $config->{$key}; $template =~ s/\[%$key%\]/$v/e; } $config->{ $k } = $template; } else { my $desc = $meta->{fields}{$k}{description}; my $type = $meta->{fields}{$k}{type}; my $value; if ($type eq 'password') { print "$desc: "; ReadMode('noecho'); $value = ReadLine(0, $term->IN); chomp($value); } elsif ($type eq 'filename') { my $attribs = $term->Attribs; local($attribs->{completion_entry_function}) = $attribs->{filename_completion_function}; $value = $term->readline("$desc: "); } else { $value = $term->readline("$desc: ") } $config->{ $k } = $value; } } { my $memory_model; sub memory_model { if (defined($memory_model)) { return $memory_model; } else { my $model = RDF::Trine::Model->temporary_model; $memory_model = $model; return $model; } }} __END__ =head1 NAME rqsh - SPARQL database shell =head1 DESCRIPTION rqsh provides a command-line interface to the SPARQL 1.1 implementation of RDF::Query. It defaults to using an in-memory database, but can be configured to use any database implemented as an L. =head1 COMMANDS =over 4 =item help Show help information on available commands. =item use [backend] Switch the storage backend (e.g. "use mysql"). You will be prompted to enter any necessary connection/configuration data. =item init Initialize the storage backend (creating necessary indexes, etc.). =item set prefix [ns]: [uri] Set a namespace for use in subsequent queries. =item results (table|srx) Set the serializer used for tabular variable binding results. =item serializer [format] Set the serializer used for RDF results (e.g. "serializer turtle"). =item debug Print all the quads in the storage backend. =item parse [sparql] Print the parsed algebra for the SPARQL 1.1 query/update. =item explain [sparql] Print the execution plan for the SPARQL 1.1 query/update. =item time [command] Time the execution of the command. =item execute Execute the SPARQL update/query obtained by dereferencing URI. =item SELECT ... =item ASK ... =item CONSTRUCT ... =item DESCRIBE ... Execute the SPARQL 1.1 query. =item INSERT ... =item DELETE ... =item LOAD =item CLEAR ... =item COPY ... =item MOVE ... Execute the SPARQL 1.1 update. =back =cut RDF-Query-2.910/bin/rqsh-server.pl000755 000765 000024 00000017722 11707542733 016745 0ustar00gregstaff000000 000000 #!/usr/bin/perl use strict; use warnings; no warnings 'redefine'; use lib qw(../lib lib); use Cwd; use Net::hostent; use IO::Socket::INET; use Scalar::Util qw(blessed); use Data::Dumper; use RDF::Query; use RDF::Query::Error qw(:try); use RDF::Query::Util; use Term::ReadLine; use Term::ReadKey; ################################################################################ # Log::Log4perl::init( \q[ # log4perl.category.rdf.query.plan = DEBUG, Screen # log4perl.appender.Screen = Log::Log4perl::Appender::Screen # log4perl.appender.Screen.stderr = 0 # log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout # ] ); ################################################################################ $| = 1; my $model = memory_model(); my %args = ( update => 1, base => 'http://myrdf.us/rqsh/' ); my $def_format = 'ntriples'; my $serializer = RDF::Trine::Serializer->new($def_format); my %serializer = ($def_format => $serializer); my $class = delete $args{ class } || 'RDF::Query'; $SIG{CHLD} = 'IGNORE'; my $port = 8082; my $server = IO::Socket::INET->new( LocalPort => $port, Type => SOCK_STREAM, Reuse => 1, Listen => 10, ) or die "Couln't start server: $!\n"; print "rqsh server started on port $port\n"; my $client; while ($client = $server->accept) { my $hostinfo = gethostbyaddr($client->peeraddr); printf "[Connect from %s]\n", $hostinfo ? $hostinfo->name : $client->peerhost; unless (my $pid = fork) { open STDERR, '>&STDOUT'; handle( $client ); $client->shutdown(2); close($client); exit; } } exit; sub handle { my $handle = shift; my $term = Term::ReadLine->new('rqsh', $handle, $handle); select($handle); print {$handle} "rqsh v1.0\n\n"; while ( defined ($_ = $term->readline('rqsh> ')) ) { my $line = $_; next unless (length($line)); if ($line =~ /exit|close/i) { return; } elsif ($line =~ /help/i) { help(); } elsif ($line =~ /^explain (.*)$/i) { explain($model, $term, $1); # } elsif ($line =~ /^use (\w+)\s*;?\s*$/i) { # my $name = $1; # my $nmodel = model( $name ); # if ($nmodel) { # $model = $nmodel; # } # } elsif ($line =~ /init/i) { # init( $model, $term, $line ); } elsif ($line =~ m/^serializer (\w+)$/i) { if (exists($serializer{ $1 })) { $serializer = $serializer{ $1 }; } else { my $ser; try { $ser = RDF::Trine::Serializer->new( $1 ); } catch RDF::Trine::Error::SerializationError with {}; if ($ser) { $serializer{ $1 } = $ser; $serializer = $ser; } else { print {$handle} "Unrecognized serializer name '$1'\n"; print {$handle} "Valid serializers are:\n"; foreach my $name (RDF::Trine::Serializer->serializer_names) { print {$handle} " $name\n"; } print {$handle} "\n"; } } } elsif ($line =~ /debug/i) { debug( $model, $term, $line ); } else { query( $model, $term, $line ); } } } sub help { print <<"END"; Commands: help Show this help information. serializer [format] Set the serializer used for RDF results (e.g. "serializer turtle"). debug Print all the quads in the storage backend. explain [sparql] Explain the execution plan for the SPARQL 1.1 query/update. SELECT ... Execute the SPARQL 1.1 query. ASK ... Execute the SPARQL 1.1 query. CONSTRUCT ... Execute the SPARQL 1.1 query. DESCRIBE ... Execute the SPARQL 1.1 query. INSERT ... Execute the SPARQL 1.1 update. DELETE ... Execute the SPARQL 1.1 update. LOAD Execute the SPARQL 1.1 update. CLEAR ... Execute the SPARQL 1.1 update. END } sub init { my $model = shift; my $term = shift; my $line = shift; if (my $store = $model->_store) { $store->init; } } # sub model { # my $term = shift; # my $name = shift; # my $sclass = RDF::Trine::Store->class_by_name( $name ); # if ($sclass) { # if ($sclass eq 'RDF::Trine::Store::Memory') { # $model = memory_model(); # return; # } else { # if ($sclass->can('_config_meta')) { # my $meta = $sclass->_config_meta; # my $keys = $meta->{required_keys}; # my $config = {}; # foreach my $k (@$keys) { # get_value( $term, $meta, $k, $config ); # } # my $store = eval { $sclass->new_with_config( $config ) }; # if ($store) { # my $m = RDF::Trine::Model->new( $store ); # if ($m) { # return $m; # } # } # print "Failed to construct '$name'-backed model.\n"; # return; # } else { # print "Cannot construct model from '$name' storage class.\n"; # } # } # } else { # print "No storage class named '$name' found\n"; # return; # } # } sub explain { my $model = shift; my $term = shift; my $sparql = shift; my $psparql = join("\n", $RDF::Query::Util::PREFIXES, $sparql); my $query = $class->new( $psparql, \%args ); unless ($query) { print "Error: " . RDF::Query->error . "\n"; return; } my ($plan, $ctx) = $query->prepare( $model ); print $plan->sse . "\n"; } sub query { my $model = shift; my $term = shift; my $sparql = shift; my $psparql = join("\n", $RDF::Query::Util::PREFIXES, $sparql); my $query = $class->new( $psparql, \%args ); unless ($query) { print "Error: " . RDF::Query->error . "\n"; return; } $term->addhistory($sparql); try { my ($plan, $ctx) = $query->prepare($model); my $iter = $query->execute_plan( $plan, $ctx ); my $count = -1; if (blessed($iter)) { if ($iter->isa('RDF::Trine::Iterator::Graph')) { $serializer->serialize_iterator_to_file( $term->OUT, $iter ); } else { print $iter->as_string( 0, \$count ); } } if ($plan->is_update) { my $size = $model->size; print "$size statements\n"; } elsif ($count >= 0) { print "$count results\n"; } } catch RDF::Query::Error with { my $e = shift; print "Error: $e\n"; } otherwise { warn "died: " . Dumper(\@_); }; } sub debug { my $model = shift; my $term = shift; my $line = shift; print "# model = $model\n"; if (my $store = $model->_store) { print "# store = $store\n"; } my $iter = $model->get_statements( undef, undef, undef, undef ); my @rows; my @names = qw[subject predicate object context]; while (my $row = $iter->next) { push(@rows, [map {$row->$_()->as_string} @names]); } my @rule = qw(- +); my @headers = (\q"| "); push(@headers, map { $_ => \q" | " } @names); pop @headers; push @headers => (\q" |"); my $table = Text::Table->new(@names); $table->rule(@rule); $table->body_rule(@rule); $table->load(@rows); print join('', $table->rule(@rule), $table->title, $table->rule(@rule), map({ $table->body($_) } 0 .. @rows), $table->rule(@rule) ); my $size = scalar(@rows); print "$size statements\n"; } sub get_value { my $term = shift; my $meta = shift; my $k = shift; my $config = shift; if (my $v = $config->{$k}) { return; } elsif (defined($meta->{fields}{$k}{'value'})) { $config->{ $k } = $meta->{fields}{$k}{'value'}; } elsif (defined($meta->{fields}{$k}{'template'})) { my $template = $meta->{fields}{$k}{'template'}; my @subkeys = ($template =~ m/\[%(\w+)%\]/g); foreach my $sk (@subkeys) { get_value( $term, $meta, $sk, $config ); } while ($template =~ m/\[%(\w+)%\]/) { my $key = $1; my $v = $config->{$key}; $template =~ s/\[%$key%\]/$v/e; } $config->{ $k } = $template; } else { my $desc = $meta->{fields}{$k}{description}; my $type = $meta->{fields}{$k}{type}; my $value; if ($type eq 'password') { print "$desc: "; $value = ReadLine(0, $term->IN); chomp($value); } elsif ($type eq 'filename') { my $attribs = $term->Attribs; local($attribs->{completion_entry_function}) = $attribs->{filename_completion_function}; $value = $term->readline("$desc: "); } else { $value = $term->readline("$desc: ") } $config->{ $k } = $value; } } { my $memory_model; sub memory_model { if (defined($memory_model)) { return $memory_model; } else { my $model = RDF::Trine::Model->temporary_model; $memory_model = $model; return $model; } }}

As of $date, running with commit $rev (HEAD of master branch at github)