Class: Graffiti::SquishQuery
- Inherits:
-
Object
- Object
- Graffiti::SquishQuery
- Includes:
- Debug
- Defined in:
- lib/graffiti/squish.rb
Overview
parse Squish query and translate triples to relational conditions
provides access to internal representation of the parsed query and utility functions to deal with Squish syntax
Direct Known Subclasses
Constant Summary collapse
- INTERNAL =
regexp for internal resource reference
Regexp.new(/\A([[:digit:]]+)\z/).freeze
- BN =
regexp for blank node mark and name
Regexp.new(/\A\?([[:alnum:]_]+)\z/).freeze
- BN_SCAN =
regexp for scanning blank nodes inside a string
Regexp.new(/\?[[:alnum:]_]+?\b/).freeze
- PARAMETER =
regexp for parametrized value
Regexp.new(/\A:([[:alnum:]_]+)\z/).freeze
- LITERAL =
regexp for replaced string literal
Regexp.new(/\A'(\d+)'\z/).freeze
- LITERAL_SCAN =
regexp for scanning replaced string literals in a string
Regexp.new(/'(\d+)'/).freeze
- PARAMETER_AND_LITERAL_SCAN =
regexp for scanning query parameters inside a string
Regexp.new(/\B:([[:alnum:]_]+)|'(\d+)'/).freeze
- NUMBER =
regexp for number
Regexp.new(/\A-?[[:digit:]]+(\.[[:digit:]]+)?\z/).freeze
- OPERATOR =
regexp for operator
Regexp.new(/\A(\+|-|\*|\/|\|\||<|<=|>|>=|=|!=|@@|to_tsvector|to_tsquery|I?LIKE|NOT|AND|OR|IN|IS|NULL)\z/i).freeze
- AGGREGATE =
regexp for aggregate function
Regexp.new(/\A(avg|count|max|min|sum)\z/i).freeze
- QUERY =
Regexp.new(/\A\s*(SELECT|INSERT|UPDATE)\b\s*(.*?)\s* \bWHERE\b\s*(.*?)\s* (?:\bEXCEPT\b\s*(.*?))?\s* (?:\bOPTIONAL\b\s*(.*?))?\s* (?:\bLITERAL\b\s*(.*?))?\s* (?:\bGROUP\s+BY\b\s*(.*?))?\s* (?:\bORDER\s+BY\b\s*(.*?)\s*(ASC|DESC)?)?\s* (?:\bUSING\b\s*(.*?))?\s*\z/mix).freeze
Instance Attribute Summary collapse
-
#group ⇒ Object
readonly
SQL GROUP BY expression.
-
#literal ⇒ Object
readonly
literal SQL expression.
-
#nodes ⇒ Object
readonly
blank variables control section.
-
#ns ⇒ Object
readonly
query namespaces mapping.
-
#order ⇒ Object
readonly
SQL order expression.
-
#order_dir ⇒ Object
readonly
direction of order, ASC or DESC.
-
#pattern ⇒ Object
readonly
query pattern graph as array of triples [ [p, s, o], … ].
-
#variables ⇒ Object
readonly
list of variables defined in the query.
Class Method Summary collapse
-
.ns_shrink(uriref, namespaces) ⇒ Object
replace schema uri with a prefix from a supplied namespaces hash.
-
.uri_shrink!(uriref, prefix, uri) ⇒ Object
replace schema uri with namespace prefix.
Instance Method Summary collapse
-
#initialize(config, query) ⇒ SquishQuery
constructor
extract common Squish query sections, perform namespace substitution, generate query pattern graph, call transform_pattern, determine query type and parse nodes section accordingly.
-
#ns_shrink(uriref) ⇒ Object
replace schema uri with a prefix from query namespaces.
-
#substitute_literals(s) ⇒ Object
replace ‘n’ substitutions with query string literals (see #new, #LITERAL).
-
#to_s ⇒ Object
returns original string passed in for parsing.
-
#validate_expression(string) ⇒ Object
validate expression.
Constructor Details
#initialize(config, query) ⇒ SquishQuery
extract common Squish query sections, perform namespace substitution, generate query pattern graph, call transform_pattern, determine query type and parse nodes section accordingly
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/graffiti/squish.rb', line 71 def initialize(config, query) query.nil? and raise ProgrammingError, "SquishQuery: query can't be nil" if query.kind_of? Hash # pre-parsed query (used by SquishAssert) @nodes = query[:nodes] @pattern = query[:pattern] @negative = query[:negative] @optional = query[:optional] @strings = query[:strings] @literal = @group = @order = '' @sql_mapper = SqlMapper.new(config, @pattern) return self elsif not query.kind_of? String raise ProgrammingError, "Bad query initialization parameter class: #{query.class}" end debug { 'SquishQuery ' + query } @query = query # keep original string query = query.dup # replace string literals with 'n' placeholders (also see #substitute_literals) @strings = [] query.gsub!(/'((?:''|[^'])*)'/m) do @strings.push $1.gsub("''", "'") # keep unescaped string "'" + (@strings.size - 1).to_s + "'" end match = QUERY.match(query) or raise ProgrammingError, "Malformed query: are keywords SELECT, INSERT, UPDATE or WHERE missing?" match, @key, @nodes, @pattern, @negative, @optional, @literal, @group, @order, @order_dir, @ns = match.to_a.collect {|m| m.to_s } match = nil @key.upcase! @order_dir.upcase! # namespaces # todo: validate ns @ns = (@ns.empty? or /\APRESET\s+NS\z/ =~ @ns) ? config.ns : Hash[*@ns.gsub(/\b(FOR|AS|AND)\b/i, '').scan(/\S+/)] @pattern = parse_pattern(@pattern) @optional = parse_pattern(@optional) @negative = parse_pattern(@negative) # validate SQL expressions validate_expression(@literal) @group.split(/\s*,\s*/).each {|group| validate_expression(group) } validate_expression(@order) @sql_mapper = SqlMapper.new( config, @pattern, @negative, @optional, @literal) # check that all variables can be bound @variables = query.scan(BN_SCAN) @variables.each {|node| @sql_mapper.bind(node) } return self end |
Instance Attribute Details
#group ⇒ Object (readonly)
SQL GROUP BY expression
139 140 141 |
# File 'lib/graffiti/squish.rb', line 139 def group @group end |
#literal ⇒ Object (readonly)
literal SQL expression
136 137 138 |
# File 'lib/graffiti/squish.rb', line 136 def literal @literal end |
#nodes ⇒ Object (readonly)
blank variables control section
130 131 132 |
# File 'lib/graffiti/squish.rb', line 130 def nodes @nodes end |
#ns ⇒ Object (readonly)
query namespaces mapping
148 149 150 |
# File 'lib/graffiti/squish.rb', line 148 def ns @ns end |
#order ⇒ Object (readonly)
SQL order expression
142 143 144 |
# File 'lib/graffiti/squish.rb', line 142 def order @order end |
#order_dir ⇒ Object (readonly)
direction of order, ASC or DESC
145 146 147 |
# File 'lib/graffiti/squish.rb', line 145 def order_dir @order_dir end |
#pattern ⇒ Object (readonly)
query pattern graph as array of triples [ [p, s, o], … ]
133 134 135 |
# File 'lib/graffiti/squish.rb', line 133 def pattern @pattern end |
#variables ⇒ Object (readonly)
list of variables defined in the query
151 152 153 |
# File 'lib/graffiti/squish.rb', line 151 def variables @variables end |
Class Method Details
.ns_shrink(uriref, namespaces) ⇒ Object
replace schema uri with a prefix from a supplied namespaces hash
176 177 178 179 180 |
# File 'lib/graffiti/squish.rb', line 176 def SquishQuery.ns_shrink(uriref, namespaces) u = uriref.dup or return nil namespaces.each {|p, uri| SquishQuery.uri_shrink!(u, p, uri) and break } return u end |
.uri_shrink!(uriref, prefix, uri) ⇒ Object
replace schema uri with namespace prefix
170 171 172 |
# File 'lib/graffiti/squish.rb', line 170 def SquishQuery.uri_shrink!(uriref, prefix, uri) uriref.gsub!(/\A#{uri}([^\/#]+)\z/) {"#{prefix}::#{$1}"} end |
Instance Method Details
#ns_shrink(uriref) ⇒ Object
replace schema uri with a prefix from query namespaces
184 185 186 |
# File 'lib/graffiti/squish.rb', line 184 def ns_shrink(uriref) SquishQuery.ns_shrink(uriref, @ns) end |
#substitute_literals(s) ⇒ Object
replace ‘n’ substitutions with query string literals (see #new, #LITERAL)
161 162 163 164 165 166 |
# File 'lib/graffiti/squish.rb', line 161 def substitute_literals(s) return s unless s.kind_of? String s.gsub(LITERAL_SCAN) do get_literal_value($1.to_i) end end |
#to_s ⇒ Object
returns original string passed in for parsing
155 156 157 |
# File 'lib/graffiti/squish.rb', line 155 def to_s @query end |
#validate_expression(string) ⇒ Object
validate expression
expression := value [ operator expression ]
value := blank_node | literal_string | number | ‘(’ expression ‘)’
whitespace between tokens (except inside parentheses) is mandatory
196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/graffiti/squish.rb', line 196 def validate_expression(string) # todo: lexical analyser string.split(/[\s(),]+/).each do |token| case token when '', BN, PARAMETER, LITERAL, NUMBER, OPERATOR, AGGREGATE else raise ProgrammingError, "Bad token '#{token}' in expression" end end string end |