Skip to content

Commit

Permalink
Finish 3.2.4
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Jun 7, 2022
2 parents 6a04611 + 20a6750 commit 4687fc5
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 3 deletions.
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
3.2.3
3.2.4
2 changes: 1 addition & 1 deletion lib/sparql/algebra/expression.rb
Expand Up @@ -140,7 +140,7 @@ def self.new(sse, parent_operator: nil, **options)
logger = options[:logger]
options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
begin
# Due to confusiong over (triple) and special-case for (qtriple)
# Due to confusion over (triple) and special-case for (qtriple)
if operator == RDF::Query::Pattern
options = options.merge(quoted: true) if sse.first == :qtriple
elsif operator == Operator::Triple && PATTERN_PARENTS.include?(parent_operator)
Expand Down
2 changes: 2 additions & 0 deletions lib/sparql/algebra/operator.rb
Expand Up @@ -134,6 +134,7 @@ class Operator
autoload :Prefix, 'sparql/algebra/operator/prefix'
autoload :Project, 'sparql/algebra/operator/project'
autoload :Reduced, 'sparql/algebra/operator/reduced'
autoload :Service, 'sparql/algebra/operator/service'
autoload :Slice, 'sparql/algebra/operator/slice'
autoload :Table, 'sparql/algebra/operator/table'
autoload :Union, 'sparql/algebra/operator/union'
Expand Down Expand Up @@ -236,6 +237,7 @@ def self.for(name, arity = nil)
when :seconds then Seconds
when :seq then Seq
when :sequence then Sequence
when :service then Service
when :sha1 then SHA1
when :sha256 then SHA256
when :sha384 then SHA384
Expand Down
5 changes: 5 additions & 0 deletions lib/sparql/algebra/operator/extend.rb
Expand Up @@ -84,6 +84,11 @@ def execute(queryable, **options, &block)
debug(options) {"Extend"}
@solutions = operand(1).execute(queryable, depth: options[:depth].to_i + 1, **options)
@solutions.each do |solution|
# Re-bind to bindings, if defined, as they might not be found in solution
options[:bindings].each_binding do |name, value|
solution[name] = value if operands.first.variables.include?(name)
end if options[:bindings] && operands.first.respond_to?(:variables)

debug(options) {"===> soln #{solution.to_h.inspect}"}
operand(0).each do |(var, expr)|
begin
Expand Down
5 changes: 5 additions & 0 deletions lib/sparql/algebra/operator/filter.rb
Expand Up @@ -49,6 +49,11 @@ def execute(queryable, **options, &block)
opts = options.merge(queryable: queryable, depth: options[:depth].to_i + 1)
@solutions = RDF::Query::Solutions()
queryable.query(operands.last, depth: options[:depth].to_i + 1, **options) do |solution|
# Re-bind to bindings, if defined, as they might not be found in solution
options[:bindings].each_binding do |name, value|
solution[name] ||= value if operands.first.variables.include?(name)
end if options[:bindings] && operands.first.respond_to?(:variables)

begin
pass = boolean(operands.first.evaluate(solution, **opts)).true?
debug(options) {"(filter) #{pass.inspect} #{solution.to_h.inspect}"}
Expand Down
5 changes: 5 additions & 0 deletions lib/sparql/algebra/operator/left_join.rb
Expand Up @@ -66,6 +66,11 @@ def execute(queryable, **options, &block)
load_left = true
right.each do |s2|
s = s2.merge(s1)
# Re-bind to bindings, if defined, as they might not be found in solution
options[:bindings].each_binding do |name, value|
s[name] = value if filter.variables.include?(name)
end if options[:bindings] && filter.respond_to?(:variables)

expr = filter ? boolean(filter.evaluate(s)).true? : true rescue false
debug(options) {"===>(evaluate) #{s.inspect}"} if filter

Expand Down
86 changes: 86 additions & 0 deletions lib/sparql/algebra/operator/service.rb
@@ -0,0 +1,86 @@
module SPARQL; module Algebra
class Operator
##
# The SPARQL Service operator.
#
# [59] ServiceGraphPattern ::= 'SERVICE' 'SILENT'? VarOrIri GroupGraphPattern
#
# @example SPARQL Grammar
# PREFIX : <http://example.org/>
#
# SELECT ?s ?o1 ?o2 {
# ?s ?p1 ?o1 .
# SERVICE <http://example.org/sparql> {
# ?s ?p2 ?o2
# }
# }
#
# @example SSE
# (prefix ((: <http://example.org/>))
# (project (?s ?o1 ?o2)
# (join
# (bgp (triple ?s ?p1 ?o1))
# (service :sparql
# (bgp (triple ?s ?p2 ?o2))))))
#
# @see https://www.w3.org/TR/sparql11-query/#QSynIRI
class Service < Operator
include Query

NAME = [:service]

##
# Executes this query on the given `queryable` graph or repository.
# Really a pass-through, as this is a syntactic object used for providing
# graph_name for URIs.
#
# @param [RDF::Queryable] queryable
# the graph or repository to query
# @param [Hash{Symbol => Object}] options
# any additional keyword options
# @yield [solution]
# each matching solution, statement or boolean
# @yieldparam [RDF::Statement, RDF::Query::Solution, Boolean] solution
# @yieldreturn [void] ignored
# @return [RDF::Query::Solutions]
# the resulting solution sequence
# @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
def execute(queryable, **options, &block)
debug(options) {"Service"}
silent = operands.first == :silent
location, query = operands
query_sparql = query.to_sparql
debug(options) {"query: #{query_sparql}"}
raise NotImplementedError, "SERVICE operator not implemented"
end

##
# Returns an optimized version of this query.
#
# Replace with the query with URIs having their lexical shortcut removed
#
# @return [Prefix] a copy of `self`
# @see SPARQL::Algebra::Expression#optimize
def optimize(**options)
operands.last.optimize(**options)
end

##
#
# Returns a partial SPARQL grammar for this term.
#
# @return [String]
def to_sparql(**options)
silent = operands.first == :silent
ops = silent ? operands[1..-1] : operands
location, query = ops


str = "SERVICE "
str << "SILENT " if silent
str << location.to_sparql(**options) + " {" + query.to_sparql(**options) + "}"
str
end
end # Service
end # Operator
end; end # SPARQL::Algebra
13 changes: 13 additions & 0 deletions lib/sparql/grammar/parser11.rb
Expand Up @@ -916,6 +916,19 @@ class Parser
end
end

# [59] ServiceGraphPattern ::= 'SERVICE' 'SILENT'? VarOrIri GroupGraphPattern
#
# Input from `data` is TODO.
# Output to prod_data is TODO.
production(:ServiceGraphPattern) do |input, data, callback|
args = []
args << :silent if data[:silent]
args << (data[:VarOrIri]).last
args << data.fetch(:query, [SPARQL::Algebra::Operator::BGP.new]).first
service = SPARQL::Algebra::Expression.for(:service, *args)
add_prod_data(:query, service)
end

# [60] Bind ::= 'BIND' '(' Expression 'AS' Var ')'
#
# Input from `data` is TODO.
Expand Down
2 changes: 1 addition & 1 deletion sparql.gemspec
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |gem|

gem.required_ruby_version = '>= 2.6'
gem.requirements = []
gem.add_runtime_dependency 'rdf', '~> 3.2', '>= 3.2.7'
gem.add_runtime_dependency 'rdf', '~> 3.2', '>= 3.2.8'
gem.add_runtime_dependency 'rdf-aggregate-repo', '~> 3.2'
gem.add_runtime_dependency 'ebnf', '~> 2.2', '>= 2.3.1'
gem.add_runtime_dependency 'builder', '~> 3.2'
Expand Down
32 changes: 32 additions & 0 deletions spec/algebra/query_spec.rb
Expand Up @@ -503,6 +503,38 @@
end
end

context "with variable pre-binding" do
# Only test exceptions, as pre-binding is handled in RDF::Query.execute.
let(:graph) {RDF::Graph.new}

{
"extend unbound": {
query: %{(project (?z) (extend ((?z (+ ?o 1))) (bgp)))},
bindings: {o: RDF::Literal(1)},
result: [{z: RDF::Literal(2)}]
},
"filter unbound": {
query: %{(project (?this) (filter (= ?this <http://example.org/this>) (bgp)))},
bindings: {this: RDF::URI("http://example.org/this")},
result: [{this: RDF::URI("http://example.org/this")}]
},
"left_join unbound": {
query: %{
(project (?this)
(leftjoin (bgp) (bgp) (= ?this <http://example.org/this>)))
},
bindings: {this: RDF::URI("http://example.org/this")},
result: [{this: RDF::URI("http://example.org/this")}]
},
}.each do |name, params|
it name do
query = SPARQL::Algebra::Expression.parse(params[:query])
bindings = RDF::Query::Solution.new(params[:bindings])
expect(query.execute(graph, bindings: bindings)).to have_result_set params[:result]
end
end
end

context "aggregates" do
let(:graph) {
RDF::Graph.new do |g|
Expand Down
2 changes: 2 additions & 0 deletions spec/suite_spec.rb
Expand Up @@ -175,6 +175,8 @@
skip "Equivalent form"
when 'sq09.rq', 'sq14.rq'
pending("SubSelect")
when 'service03.rq', 'service06.rq', 'syntax-service-01.rq'
pending("Service")
when 'sparql-star-order-by.rq'
pending("OFFSET/LIMIT in sub-select")
when 'compare_time-01.rq',
Expand Down

0 comments on commit 4687fc5

Please sign in to comment.