RDF-Query-2.916/README.html 000644 000765 000024 00000272435 12610210253 015170 0 ustar 00greg staff 000000 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:
Installing RDF::Query
To install, run:
perl Makefile.PL
make
make test
make install
Version History
Version 2.916 (2015-10-16)
- Enhancements
- Updated RDF::Query::Node::Resource->as_sparql to allow SPARQL 1.0 PN_LOCAL prefix names (GitHub issue #132).
- Other
- Add default indentation to RDF::Query::Algebra::Limit->sse.
Version 2.915 (2015-08-18)
- New Features
- Add RDF::Query::Node->from_attean method (PR #128 from Kjetil Kjernsmo).
- Other
- Add default indentation to RDF::Query::Algebra::Limit->sse.
Version 2.914 (2015-05-17)
- Bug Fixes
- Fixed bug in evaluation of SPARQL CONCAT function.
Version 2.913 (2015-02-14)
- Bug Fixes
- Fix SPARQL 1.1 parsing bug that disallowed whitespace between an aggregate name and the following open-parenthesis.
- Fix bug in RDF::Query::Algebra::NamedGraph->definite_variables.
- Fix rqsh to use a pure memory model when requested (instead of a temporary model).
- Fixed whitespace handling bug during parsing of VALUES clauses (github issue #120).
- Enhancements
- Merge adjacent service blocks if their endpoints and silent flags match (github issue #124).
- Allow RDF::Query->new to accept an algebra object instead of a query string.
- Updated node classes to allow overloaded comparisions with RDF::Trine::Node::Nil objects.
- Improve coverage of as_hash function for variable bindings and expressions (github issue #121).
- Other
- Documentation fixes (from Kjetil Kjernsmo).
Version 2.912 (2014-10-24)
- Bug Fixes
- Fix bug in handling of SUM aggregates with non-literal data.
- Enhancements
- Improved performance of regular expression use in SPARQL parser (from Dorian Taylor; github pull request #111).
- Other
- Added IRC resource to Makefile.PL.
Version 2.911 (2014-07-24)
- Bug Fixes
- Fixed bug in RDF::Query::Plan::Join::PushDownNestedLoop that didn't allow 'aggregate' to appear in the RHS serialized text (github issue 101).
- Fixed SPARQL 1.1 GGP parsing bug (github issue 100).
- Fixed RDF::Query::Algebra->subpatterns_of_type to support descent into sub-queries.
- Disabled pushing full updates down into the store object (only meant to be full queries at this point).
- Fixed as_hash method in RDF::Query::Algebra::Aggregate, RDF::Query::Algebra::Sort, and RDF::Query::Expression::Alias.
- Updated RDF::Query::Compiler::SQL to respect node class encapsulation.
- Enhancements
- Modularized implementation of explain method in algebra and plan classes.
- Updated rqsh script to use env perl instead of hardcoded bin path.
- Added JSON output to bin/parse.pl script.
- Added RDF::Query::ExecutionContext->bind_variable method.
- Other
- Added :all export tag to RDF::Trine::Node.
- Remove Crypt::GPG and Bloom::Filter from list of recommended modules (no longer used in code).
Version 2.910 (2013-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–2015 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.916/SIGNATURE 000644 000765 000024 00000037432 12610210407 014626 0 ustar 00greg staff 000000 000000 This file contains message digests of all files listed in MANIFEST,
signed via the Module::Signature module, version 0.73.
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 0d27cafb2abb4494da228e7f40db5655a6b36486 Changes.ttl
SHA1 8963ea8452cff037bfc791e300c4df1bce9424af MANIFEST
SHA1 c323ac500ea88a9227e1181a5eebb7afa1a7f11a META.yml
SHA1 a16995de339fdf353650c786bc5d1024ded8208c Makefile.PL
SHA1 815ae19f29a96135f79939724a3c9f46c3de30bb README
SHA1 016da679f3bbece58939c691704bb1de3d92ee9d README.html
SHA1 1c422fda233ab5813719189e2a3cb3fa158b3c9e bin/dawg11-status.pl
SHA1 ccee1bfc884ba786e05cf19dae510724d61e344b 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 6af97114e44b89b9b2cb4e986776c8ad39c3d4f8 bin/parse.pl
SHA1 0b7d2af82ec39047f6a43d23f18daa45fd00b56e bin/parse_profile.pl
SHA1 1d08a6cfc90665744d5e6000584f1b5da796e46b bin/passing_earl_tests.sh
SHA1 0eaedd5c43ae75f997158027bd6d223c00609d2b bin/query.pl
SHA1 57b48843a2f1fea4b81b50b65b489358066605f9 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 fb69c7e4bc494e95f4d4d38c5b7c79b8cf6f26a7 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 9b5001bfa9cf8607b3b3935284d9253e0391c9f1 inc/Module/Install.pm
SHA1 d001b4b9a48395a8c4134b234a0e1789138427c5 inc/Module/Install/AuthorTests.pm
SHA1 cab0e564f9bdf658535f683aa197157e06d0dcea inc/Module/Install/Base.pm
SHA1 a1559b5b3b40f68efbbd256f4fef85970891b3ae inc/Module/Install/Can.pm
SHA1 f15c1ba85f6d52e70c48c64bf0752c90a4ad66f9 inc/Module/Install/Fetch.pm
SHA1 eb48df8bafd07c6a862126d9b274df42b4395742 inc/Module/Install/Makefile.pm
SHA1 95c73873c6c3cb7024614c225c53863e1e90c134 inc/Module/Install/Metadata.pm
SHA1 a1820d820772de5eb9c190425babf2c29a2caaf7 inc/Module/Install/Scripts.pm
SHA1 f8b2ae3386f6ba26c33408968a953d450842eade inc/Module/Install/Win32.pm
SHA1 f302bc703d76299cff243e5b44cecd61aac27b76 inc/Module/Install/WriteAll.pm
SHA1 635d056395f60f6a82f3b2bb272d9e6c12fe7b03 lib/RDF/Query.pm
SHA1 41b8fa76a5a044ba558edf34deba9ff22afc7aff lib/RDF/Query/Algebra.pm
SHA1 d74914aaadea2f728da169cbb0ff5bcd9b29d3e5 lib/RDF/Query/Algebra/Aggregate.pm
SHA1 2985a70ccca161b601302f0080503e86a2594e75 lib/RDF/Query/Algebra/BasicGraphPattern.pm
SHA1 6c6fda9a5a99e6f54ee605618ea172f1e853374c lib/RDF/Query/Algebra/Clear.pm
SHA1 3760f6b1a2652ca2ad882970089feb2dc23fbbf4 lib/RDF/Query/Algebra/Construct.pm
SHA1 c32c477801f9eab47fe8010816cca6adb967de9b lib/RDF/Query/Algebra/Copy.pm
SHA1 ae94cecb38430c82e3d7d81f2be6926d1637cf20 lib/RDF/Query/Algebra/Create.pm
SHA1 6fc45ce9e46927235b4b6c82b1ca960ad3b83673 lib/RDF/Query/Algebra/Distinct.pm
SHA1 8afebfe80a083010c0f6594086ea3c28034b5a5c lib/RDF/Query/Algebra/Extend.pm
SHA1 91393b6cd010a8eca3dc0df0d4787f7c7e26f5a9 lib/RDF/Query/Algebra/Filter.pm
SHA1 435c33265364c1e8544fecf271017e454a31e25b lib/RDF/Query/Algebra/GroupGraphPattern.pm
SHA1 1d899fa25f4aaf557f60c95c86aad8ba7344c077 lib/RDF/Query/Algebra/Limit.pm
SHA1 c4bc7b1b3a6ad4cf123a5251bd3fee87e45842bd lib/RDF/Query/Algebra/Load.pm
SHA1 0cf61d6290de4f490bba6a28bdd1c9ead677135c lib/RDF/Query/Algebra/Minus.pm
SHA1 6a0e0f505db33398e4976e23e0c6e0da849d87de lib/RDF/Query/Algebra/Move.pm
SHA1 356b27464392f4e5e1adeec5a410b0360f32c184 lib/RDF/Query/Algebra/NamedGraph.pm
SHA1 9377f25f2ee608f779c6de4e3a073f9ffdfd9d56 lib/RDF/Query/Algebra/Offset.pm
SHA1 76ecad8f0852846181674b0e3398d1719879e9c1 lib/RDF/Query/Algebra/Optional.pm
SHA1 706bc2f84709409c388c6525322cbe81a95a3f68 lib/RDF/Query/Algebra/Path.pm
SHA1 5324604f5d492ffe9eb3f49919a5ca0c05135630 lib/RDF/Query/Algebra/Project.pm
SHA1 55c68bc08398426840fe4b2e1e94c2fe103e2d8b lib/RDF/Query/Algebra/Quad.pm
SHA1 91198226a703b1b133e679bfc20b300e8f69ff35 lib/RDF/Query/Algebra/Sequence.pm
SHA1 2c87a54ad7000917485d9873fb7e59ecbbdbf8ea lib/RDF/Query/Algebra/Service.pm
SHA1 33f2669aa9819f36c2be58fca697784285cee9df lib/RDF/Query/Algebra/Sort.pm
SHA1 0f06b68208182608f83b9f552cdd36426e867d58 lib/RDF/Query/Algebra/SubSelect.pm
SHA1 a36c4d911b00bfeb39249bb12069c483dc4b1c5a lib/RDF/Query/Algebra/Table.pm
SHA1 35455b6b0594aee37e010f54e4a0d202959ca334 lib/RDF/Query/Algebra/TimeGraph.pm
SHA1 cdbfad13b844f6d482698a36cf178460c9682f19 lib/RDF/Query/Algebra/Triple.pm
SHA1 f58163c87105cf09884f514c7061d1f5691204bf lib/RDF/Query/Algebra/Union.pm
SHA1 599cf70c7187efaa6062c87abe749e5c75efd32e lib/RDF/Query/Algebra/Update.pm
SHA1 00ab39ce27f64b011c9420a9899234945cec5710 lib/RDF/Query/BGPOptimizer.pm
SHA1 ca6b2cf818d1dbd88a3f7cdfc63d826f4a290dce lib/RDF/Query/Compiler/SQL.pm
SHA1 b47c5b1c411bf3b4c53f5c983790395de7d88158 lib/RDF/Query/Error.pm
SHA1 897ee8c5f880246827067f3bb01e0fc329ad84df lib/RDF/Query/ExecutionContext.pm
SHA1 611e1f58b6e2c95efbc8f2e5cedb3c3d4f2bb464 lib/RDF/Query/Expression.pm
SHA1 5962d3c0780506cff6b268d04fe2063c2f425038 lib/RDF/Query/Expression/Alias.pm
SHA1 7e0df340b1ce0331b5e269a6fdd527810d93d1ad lib/RDF/Query/Expression/Binary.pm
SHA1 0dba194a0fd3f69b03a1e2b1eb504530f28639fc lib/RDF/Query/Expression/Function.pm
SHA1 776f38641b34889432f1e58dd1dd9fb372586f19 lib/RDF/Query/Expression/Nary.pm
SHA1 95f350982d26401c5c7a46fb78403e285d9eea50 lib/RDF/Query/Expression/Unary.pm
SHA1 b5d1075c6aeb884984419d85fc6a1c16c57fd43e lib/RDF/Query/Federate.pm
SHA1 f65be8c2fa0b2ceec4a624e97021527d6d9fbb08 lib/RDF/Query/Federate/Plan.pm
SHA1 90898f50a3120297a6b87cc6c9035096ad675699 lib/RDF/Query/Functions.pm
SHA1 9b4f453c67dd7d9576e6ad574c925f5b2176bd9b lib/RDF/Query/Functions/Geo.pm
SHA1 59cf4fbd3f4b6a604902ae962f53298287b1db29 lib/RDF/Query/Functions/Jena.pm
SHA1 42d0976cd474c0029f17b4691a381df8938aa79b lib/RDF/Query/Functions/Kasei.pm
SHA1 3fe0fe8e6f4463981e480cbaa401da395c9dcf4a lib/RDF/Query/Functions/SPARQL.pm
SHA1 412c60dbb34575e2618b79fe49418947ada3bb58 lib/RDF/Query/Functions/Xpath.pm
SHA1 2315aeb328601440ff47d22d17a428bc56d66822 lib/RDF/Query/Node.pm
SHA1 c4afc42fe907c6b5dbddb5f18aa6920c5fc852c4 lib/RDF/Query/Node/Blank.pm
SHA1 5a14c14ea491575b2ab086db9a82bfa454e1eba5 lib/RDF/Query/Node/Literal.pm
SHA1 686cd5b0c596de9e710717d109df028ea645318d lib/RDF/Query/Node/Resource.pm
SHA1 91ace2c4c34cdf6b9fcf0975e8d27d94b6ee5f56 lib/RDF/Query/Node/Variable.pm
SHA1 71b340032152c797981a6dbf7699057f46dfbc38 lib/RDF/Query/Parser.pm
SHA1 ec25ca2e661b26c15edfd17b074c9305e9d38b21 lib/RDF/Query/Parser/RDQL.pm
SHA1 5494b47c96b32e0c9d37e1876f08d0777d2a74ff lib/RDF/Query/Parser/SPARQL.pm
SHA1 30eb35a8433c5e5b4435def6172e8146929acb55 lib/RDF/Query/Parser/SPARQL11.pm
SHA1 67e9ee97644a188c8a9b7276c0488a5172401755 lib/RDF/Query/Plan.pm
SHA1 d1523be5454cb8b548856cd0875b98fa3e4fd077 lib/RDF/Query/Plan/Aggregate.pm
SHA1 2f1420b641074e78586ba95ed9f1a68ce5b935b3 lib/RDF/Query/Plan/BasicGraphPattern.pm
SHA1 61b662528424d03ebd35649071c39ecf809c4701 lib/RDF/Query/Plan/Clear.pm
SHA1 a7fa9239c7a3c4a87782f624d9c370de6aae36cc lib/RDF/Query/Plan/ComputedStatement.pm
SHA1 cd012936b5c57fba45a6dd7b56fa137253a602ce lib/RDF/Query/Plan/Constant.pm
SHA1 01097706a327a3803d9fdb2843ce5acca2a91c5e lib/RDF/Query/Plan/Construct.pm
SHA1 debcc43c65ea15ac6c54a2527f8a1eb73a40599a lib/RDF/Query/Plan/Copy.pm
SHA1 62c75f7ab5f647895cbefc23c682db3658b57f77 lib/RDF/Query/Plan/Distinct.pm
SHA1 a429adbe36d56535bdc8f39802bcd2d7b76dfee3 lib/RDF/Query/Plan/Extend.pm
SHA1 aefdaa80501976ad0368d56d47ee22461d4ef04e lib/RDF/Query/Plan/Filter.pm
SHA1 9777c8ff17f13401a9f61ba724ab4727acc30cf4 lib/RDF/Query/Plan/Iterator.pm
SHA1 dfaa8dc29475646f40df4f7f455ba906122d5cfd lib/RDF/Query/Plan/Join.pm
SHA1 4440cbe54971b16e452ffe8b328effe63e8c24e9 lib/RDF/Query/Plan/Join/NestedLoop.pm
SHA1 7cfff26bc27d5b60de34db11d0f7eea28a9b8a86 lib/RDF/Query/Plan/Join/PushDownNestedLoop.pm
SHA1 309f138882774af6540c7206170fb058b2b5fd72 lib/RDF/Query/Plan/Limit.pm
SHA1 dc3f837b9a82aa39b17fa4b430a81456805db11d lib/RDF/Query/Plan/Load.pm
SHA1 2d6d06f4a51d37431c003cbf4ba156dbc4f19b6d lib/RDF/Query/Plan/Minus.pm
SHA1 78a9ec61fb1f6d6213ce8d8e6a2fe5fdb92d50d0 lib/RDF/Query/Plan/Move.pm
SHA1 29f4072c4f992f22a165a53c49c726df7bda42a5 lib/RDF/Query/Plan/NamedGraph.pm
SHA1 e2365abf857729bb170d6868d55269fcca7f6f47 lib/RDF/Query/Plan/Offset.pm
SHA1 0228a5459fa851168fa066ac2d395c110cb6f4c6 lib/RDF/Query/Plan/Path.pm
SHA1 54aa33eb6e1416323703574653183bb7b91ebbab lib/RDF/Query/Plan/Project.pm
SHA1 afa241c0985ea9e0ebca78585bcaf09102186783 lib/RDF/Query/Plan/Quad.pm
SHA1 91c872aeb9d4e62fcfc8ea003c5e11a8ef624437 lib/RDF/Query/Plan/Sequence.pm
SHA1 ae90be33a698e2a9f5b54064d5c571ab2ee002a8 lib/RDF/Query/Plan/Service.pm
SHA1 515ee20b1264d6305d4978f7aab9e64e51fec87e lib/RDF/Query/Plan/Sort.pm
SHA1 f0adee50423e49bc579ee2eae8d6ba543437cecb lib/RDF/Query/Plan/SubSelect.pm
SHA1 61f7a42ec1b28c81304d6a6f80a92fa9f4390334 lib/RDF/Query/Plan/ThresholdUnion.pm
SHA1 ff251d0756eb1a7a8ceb04185969cd0fbe3a9e60 lib/RDF/Query/Plan/Triple.pm
SHA1 53d99135600e6a8d389d4bd47da646b2cda1a9c7 lib/RDF/Query/Plan/Union.pm
SHA1 d59e0bdb3f2b3fa105939c5d6db8de435d8f9810 lib/RDF/Query/Plan/Update.pm
SHA1 e33b1c690791207981e56b89ca2307543e8383bd lib/RDF/Query/ServiceDescription.pm
SHA1 ce37679470f12faaef3c5d83676e87660b043170 lib/RDF/Query/Temporal.pm
SHA1 91d59e6dd004f861844a51648a3856cba5f14bb6 lib/RDF/Query/Util.pm
SHA1 efaca29df069c83c1cebcf8910405bc1177a5b3a 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 e414bd93bfe8df35b28d8e2142a367641e87c775 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
Comment: GPGTools - http://gpgtools.org
iEYEARECAAYFAlYhEQUACgkQhPK6VMqoyC2nFgCgpJyilLLfhAQPtQilc2wJ2Sqt
RJAAni4KnKqFwjkgi4q00xg0n4VMMal8
=LdSw
-----END PGP SIGNATURE-----
RDF-Query-2.916/t/ 000755 000765 000024 00000000000 12610210404 013571 5 ustar 00greg staff 000000 000000 RDF-Query-2.916/xt/ 000755 000765 000024 00000000000 12610210404 013761 5 ustar 00greg staff 000000 000000 RDF-Query-2.916/xt/dawg-eval10.t 000755 000765 000024 00000066111 12370032672 016202 0 ustar 00greg staff 000000 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.916/xt/dawg-eval11.t 000755 000765 000024 00000066062 12370032672 016210 0 ustar 00greg staff 000000 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.916/xt/dawg-syntax11.t 000755 000765 000024 00000021605 11760736733 016614 0 ustar 00greg staff 000000 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.916/xt/dev-service-description.t 000644 000765 000024 00000012002 11707542737 020725 0 ustar 00greg staff 000000 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.916/xt/dev-sql-compiler.t 000644 000765 000024 00000103202 11707542737 017356 0 ustar 00greg staff 000000 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.916/xt/dev-time-intervals.t 000644 000765 000024 00000006523 11707542737 017722 0 ustar 00greg staff 000000 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.916/xt/federate.t 000644 000765 000024 00000033107 11707542737 015760 0 ustar 00greg staff 000000 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.916/xt/parser-rdql.t 000644 000765 000024 00000021152 12233723503 016415 0 ustar 00greg staff 000000 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.916/xt/parser-sparql.t 000644 000765 000024 00000531225 12233723503 016764 0 ustar 00greg staff 000000 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: