File: ts_graffiti.rb

package info (click to toggle)
ruby-graffiti 2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 448 kB
  • ctags: 327
  • sloc: ruby: 2,504; sql: 209; makefile: 2
file content (453 lines) | stat: -rw-r--r-- 13,932 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
#!/usr/bin/env ruby
#
# Graffiti RDF Store tests
#
#   Copyright (c) 2002-2009  Dmitry Borodaenko <angdraug@debian.org>
#
#   This program is free software.
#   You can distribute/modify this program under the terms of
#   the GNU General Public License version 3 or later.
#
# vim: et sw=2 sts=2 ts=8 tw=0

require 'test/unit'
require 'yaml'
require 'sequel'
require 'graffiti'

include Graffiti

class TC_Storage < Test::Unit::TestCase

  def setup
    config = File.open(
      File.join(
        File.dirname(File.dirname(__FILE__)),
        'doc', 'examples', 'samizdat-rdf-config.yaml'
      )
    ) {|f| YAML.load(f.read) }
    @db = create_mock_db
    @store = Store.new(@db, config)
    @ns = @store.config.ns
  end

  def test_query_select
    squish = %{
SELECT ?msg, ?title, ?name, ?date, ?rating
WHERE (dc::title ?msg ?title)
      (dc::creator ?msg ?creator)
      (s::fullName ?creator ?name)
      (dc::date ?msg ?date)
      (rdf::subject ?stmt ?msg)
      (rdf::predicate ?stmt dc::relation)
      (rdf::object ?stmt s::Quality)
      (s::rating ?stmt ?rating)
LITERAL ?rating >= -1
ORDER BY ?rating DESC
USING PRESET NS}

    sql = "SELECT DISTINCT b.id AS msg, b.title AS title, a.full_name AS name, c.published_date AS date, d.rating AS rating
FROM member AS a
INNER JOIN message AS b ON (b.creator = a.id)
INNER JOIN resource AS c ON (b.id = c.id)
INNER JOIN statement AS d ON (b.id = d.subject)
INNER JOIN resource AS e ON (d.predicate = e.id) AND (e.uriref = 't' AND e.label = 'http://purl.org/dc/elements/1.1/relation')
INNER JOIN resource AS f ON (d.object = f.id) AND (f.uriref = 't' AND f.label = 'http://www.nongnu.org/samizdat/rdf/schema#Quality')
WHERE (c.published_date IS NOT NULL)
AND (a.full_name IS NOT NULL)
AND (d.id IS NOT NULL)
AND (b.title IS NOT NULL)
AND (d.rating >= -1)
ORDER BY d.rating DESC"

    test_squish_select(squish, sql) do |query|
      assert_equal %w[?msg ?title ?name ?date ?rating], query.nodes
      assert query.pattern.include?(["#{@ns['dc']}title", "?msg", "?title", nil, false])
      assert_equal '?rating >= -1', query.literal
      assert_equal '?rating', query.order
      assert_equal 'DESC', query.order_dir
      assert_equal @ns['s'], query.ns['s']
    end

    assert_equal [], @store.select_all(squish)
  end

  def test_query_assert
    # initialize
    query_text = %{
INSERT ?msg
UPDATE ?title = 'Test Message', ?content = 'Some ''text''.'
WHERE (dc::creator ?msg 1)
      (dc::title ?msg ?title)
      (s::content ?msg ?content)
USING dc FOR #{@ns['dc']}
      s FOR #{@ns['s']}}
    begin
      query = SquishAssert.new(@store.config, query_text)
    rescue
      assert false, "SquishAssert initialization raised #{$!.class}: #{$!}"
    end

    # query parser
    assert_equal ['?msg'], query.insert
    assert_equal({'?title' => "'0'", '?content' => "'1'"}, query.update)
    assert query.pattern.include?(["#{@ns['dc']}title", "?msg", "?title", nil, false])
    assert_equal @ns['s'], query.ns['s']
    assert_equal "'Test Message'", query.substitute_literals("'0'")
    assert_equal "'Some ''text''.'", query.substitute_literals("'1'")

    # mock db
    ids = @store.assert(query_text)
    assert_equal [1], ids
    assert_equal 'Test Message', @db[:Message][:id => 1][:title]

    id2 = @store.assert(query_text)
    query_text = %{
UPDATE ?rating = :rating
WHERE (rdf::subject ?stmt :related)
      (rdf::predicate ?stmt dc::relation)
      (rdf::object ?stmt 1)
      (s::voteProposition ?vote ?stmt)
      (s::voteMember ?vote :member)
      (s::voteRating ?vote ?rating)}
    params = {:rating => -1, :related => 2, :member => 3}
    ids = @store.assert(query_text, params)
    assert_equal [], ids
    assert vote = @db[:vote].order(:id).last
    assert_equal -1, vote[:rating].to_i

    params[:rating] = -2
    @store.assert(query_text, params)
    assert vote2 = @db[:vote].order(:id).last
    assert_equal -2, vote2[:rating].to_i
    assert_equal vote[:id], vote2[:id]
  end

  def test_query_assert_expression
    query_text = %{
UPDATE ?rating = 2 * :rating
WHERE (rdf::subject ?stmt :related)
      (rdf::predicate ?stmt dc::relation)
      (rdf::object ?stmt 1)
      (s::voteProposition ?vote ?stmt)
      (s::voteMember ?vote :member)
      (s::voteRating ?vote ?rating)}
    params = {:rating => -1, :related => 2, :member => 3}
    @store.assert(query_text, params)
    assert vote = @db[:vote].order(:id).last
    assert_equal -2, vote[:rating].to_i
  end
  private :test_query_assert_expression

  def test_dangling_blank_node
    squish = %{
SELECT ?msg
WHERE (s::inReplyTo ?msg ?parent)
USING s FOR #{@ns['s']}}

    sql = "SELECT DISTINCT a.id AS msg
FROM resource AS a
INNER JOIN resource AS b ON (a.part_of_subproperty = b.id) AND (b.uriref = 't' AND b.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
WHERE (a.id IS NOT NULL)"

    test_squish_select(squish, sql) do |query|
      assert_equal %w[?msg], query.nodes
      assert query.pattern.include?(["#{@ns['s']}inReplyTo", "?msg", "?parent", nil, false])
      assert_equal @ns['s'], query.ns['s']
    end
  end

  def test_external_resource_no_self_join
    squish = %{SELECT ?id WHERE (s::id tag::Translation ?id)}

    sql = "SELECT DISTINCT a.id AS id
FROM resource AS a
WHERE (a.id IS NOT NULL)
AND ((a.uriref = 't' AND a.label = 'http://www.nongnu.org/samizdat/rdf/tag#Translation'))"

    test_squish_select(squish, sql) do |query|
      assert_equal %w[?id], query.nodes
      assert query.pattern.include?(["#{@ns['s']}id", "#{@ns['tag']}Translation", "?id", nil, false])
      assert_equal @ns['s'], query.ns['s']
    end
  end

  #def test_internal_resource
  #end

  #def test_external_subject_internal_property
  #end

  def test_except
    squish = %{
SELECT ?msg
WHERE (dc::date ?msg ?date)
EXCEPT (s::inReplyTo ?msg ?parent)
       (dct::isVersionOf ?msg ?version_of)
       (dc::creator ?version_of 1)
ORDER BY ?date DESC}

    sql = "SELECT DISTINCT a.id AS msg, a.published_date AS date
FROM resource AS a
LEFT JOIN (
    SELECT a.id AS _field_c
    FROM message AS b
    INNER JOIN resource AS a ON (a.part_of = b.id)
    INNER JOIN resource AS c ON (a.part_of_subproperty = c.id) AND (c.uriref = 't' AND c.label = 'http://purl.org/dc/terms/isVersionOf')
    WHERE (b.creator = 1)
) AS _subquery_a ON (a.id = _subquery_a._field_c)
LEFT JOIN resource AS d ON (a.part_of_subproperty = d.id) AND (d.uriref = 't' AND d.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
WHERE (a.published_date IS NOT NULL)
AND (a.id IS NOT NULL)
AND (_subquery_a._field_c IS NULL)
AND (d.id IS NULL)
ORDER BY a.published_date DESC"

    test_squish_select(squish, sql)
  end

  def test_except_group_by
    squish = %{
SELECT ?msg
WHERE (rdf::predicate ?stmt dc::relation)
      (rdf::subject ?stmt ?msg)
      (rdf::object ?stmt ?tag)
      (dc::date ?stmt ?date)
      (s::rating ?stmt ?rating FILTER ?rating >= 1.5)
      (s::hidden ?msg ?hidden FILTER ?hidden = 'f')
EXCEPT (dct::isPartOf ?msg ?parent)
GROUP BY ?msg
ORDER BY max(?date) DESC}

    sql = "SELECT DISTINCT c.subject AS msg, max(d.published_date)
FROM message AS a
INNER JOIN statement AS c ON (c.subject = a.id) AND (c.rating >= 1.5)
INNER JOIN resource AS b ON (c.subject = b.id)
INNER JOIN resource AS d ON (c.id = d.id)
INNER JOIN resource AS e ON (c.predicate = e.id) AND (e.uriref = 't' AND e.label = 'http://purl.org/dc/elements/1.1/relation')
WHERE (d.published_date IS NOT NULL)
AND (a.hidden IS NOT NULL)
AND (b.part_of IS NULL)
AND (c.rating IS NOT NULL)
AND (c.object IS NOT NULL)
AND ((a.hidden = 'f'))
GROUP BY c.subject
ORDER BY max(d.published_date) DESC"

    test_squish_select(squish, sql)
  end

  def test_optional
    squish = %{
SELECT ?date, ?creator, ?lang, ?parent, ?version_of, ?hidden, ?open
WHERE (dc::date 1 ?date)
OPTIONAL (dc::creator 1 ?creator)
         (dc::language 1 ?lang)
         (s::inReplyTo 1 ?parent)
         (dct::isVersionOf 1 ?version_of)
         (s::hidden 1 ?hidden)
         (s::openForAll 1 ?open)}

    sql = "SELECT DISTINCT a.published_date AS date, b.creator AS creator, b.language AS lang, select_subproperty(a.part_of, d.id) AS parent, select_subproperty(a.part_of, c.id) AS version_of, b.hidden AS hidden, b.open AS open
FROM resource AS a
INNER JOIN message AS b ON (a.id = b.id)
LEFT JOIN resource AS c ON (a.part_of_subproperty = c.id) AND (c.uriref = 't' AND c.label = 'http://purl.org/dc/terms/isVersionOf')
LEFT JOIN resource AS d ON (a.part_of_subproperty = d.id) AND (d.uriref = 't' AND d.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
WHERE (a.published_date IS NOT NULL)
AND ((a.id = 1))"

    test_squish_select(squish, sql)
  end

  def test_except_optional_transitive
    squish = %{
SELECT ?msg
WHERE (rdf::subject ?stmt ?msg)
      (rdf::predicate ?stmt dc::relation)
      (rdf::object ?stmt ?tag)
      (s::rating ?stmt ?rating FILTER ?rating > 0)
      (dc::date ?msg ?date)
EXCEPT (dct::isPartOf ?msg ?parent)
OPTIONAL (dct::isPartOf ?tag ?supertag TRANSITIVE)
LITERAL ?tag = 1 OR ?supertag = 1
ORDER BY ?date DESC}

    sql = "SELECT DISTINCT b.subject AS msg, a.published_date AS date
FROM resource AS a
INNER JOIN statement AS b ON (b.subject = a.id) AND (b.rating > 0)
INNER JOIN resource AS d ON (b.predicate = d.id) AND (d.uriref = 't' AND d.label = 'http://purl.org/dc/elements/1.1/relation')
LEFT JOIN part AS c ON (b.object = c.id)
WHERE (a.published_date IS NOT NULL)
AND (a.part_of IS NULL)
AND (b.rating IS NOT NULL)
AND (b.id IS NOT NULL)
AND (b.object = 1 OR c.part_of = 1)
ORDER BY a.published_date DESC"

    test_squish_select(squish, sql)
  end

  def test_optional_connect_by_object
    squish = %{
SELECT ?event
WHERE (ical::dtstart ?event ?dtstart FILTER ?dtstart >= 'now')
      (ical::dtend ?event ?dtend)
OPTIONAL (s::rruleEvent ?rrule ?event)
         (ical::until ?rrule ?until FILTER ?until IS NULL OR ?until > 'now')
LITERAL ?dtend > 'now' OR ?rrule IS NOT NULL
ORDER BY ?event DESC}

    sql = "SELECT DISTINCT b.id AS event
FROM event AS b
LEFT JOIN recurrence AS a ON (b.id = a.event) AND (a.until IS NULL OR a.until > 'now')
WHERE (b.dtstart IS NOT NULL)
AND ((b.dtstart >= 'now'))
AND (b.dtend > 'now' OR a.id IS NOT NULL)
ORDER BY b.id DESC"

    test_squish_select(squish, sql)
  end
  private :test_optional_connect_by_object

  def test_many_to_many
    # pretend that Vote is a many-to-many relation table
    squish = %{
SELECT ?p, ?date
WHERE (s::voteRating ?p ?vote1 FILTER ?vote1 > 0)
      (s::voteRating ?p ?vote2 FILTER ?vote2 < 0)
      (dc::date ?p ?date)
ORDER BY ?date DESC}

    sql = "SELECT DISTINCT a.id AS p, c.published_date AS date
FROM vote AS a
INNER JOIN vote AS b ON (a.id = b.id) AND (b.rating < 0)
INNER JOIN resource AS c ON (a.id = c.id)
WHERE (c.published_date IS NOT NULL)
AND (a.rating IS NOT NULL)
AND (b.rating IS NOT NULL)
AND ((a.rating > 0))
ORDER BY c.published_date DESC"

    test_squish_select(squish, sql)
  end

  def test_update_null_and_subproperty
    query_text =
      %{INSERT ?msg
      UPDATE ?parent = :parent
      WHERE (dct::isPartOf ?msg ?parent)}
    @store.assert(query_text, :id => 1, :parent => 3)
    assert_equal 3, @db[:resource].filter(:id => 1).get(:part_of)

    # check that subproperty is set
    query_text =
      %{UPDATE ?parent = :parent
      WHERE (s::subTagOf :id ?parent)}
    @store.assert(query_text, :id => 1, :parent => 3)
    assert_equal 3, @db[:resource].filter(:id => 1).get(:part_of)
    assert_equal 2, @db[:resource].filter(:id => 1).get(:part_of_subproperty)

    # check that NULL is handled correctly and that subproperty is unset
    query_text =
      %{UPDATE ?parent = NULL
      WHERE (dct::isPartOf :id ?parent)}
    @store.assert(query_text, :id => 1)
    assert_equal nil, @db[:resource].filter(:id => 1).get(:part_of)
    assert_equal nil, @db[:resource].filter(:id => 1).get(:part_of_subproperty)
  end

  private

  def test_squish_select(squish, sql)
    begin
      query = SquishSelect.new(@store.config, squish)
    rescue
      assert false, "SquishSelect initialization raised #{$!.class}: #{$!}"
    end

    yield query if block_given?

    # query result
    begin
      sql1 = @store.select(query)
    rescue
      assert false, "select with pre-parsed query raised #{$!.class}: #{$!}"
    end
    begin
      sql2 = @store.select(squish)
    rescue
      assert false, "select with query text raised #{$!.class}: #{$!}"
    end
    assert sql1 == sql2

    # transform result
    assert_equal normalize(sql), normalize(sql1),
      "Query doesn't match. Expected:\n#{sql}\nReceived:\n#{sql1}"
  end

  def normalize(sql)
    sql
  end

  def create_mock_db
    db = Sequel.sqlite(:quote_identifiers => false,
                       :integer_booleans  => false)

    db.create_table(:resource) do
      primary_key :id
      Time :published_date
      Integer :part_of
      Integer :part_of_subproperty
      Integer :part_sequence_number
      TrueClass :literal
      TrueClass :uriref
      String :label
    end

    db.create_table(:statement) do
      primary_key :id
      Integer :subject
      Integer :predicate
      Integer :object
      BigDecimal :rating, :size => [4, 2]
    end

    db.create_table(:member) do
      primary_key :id
      String :login
      String :full_name
      String :email
    end

    db.create_table(:message) do
      primary_key :id
      String :title
      Integer :creator
      String :format
      String :language
      TrueClass :open
      TrueClass :hidden
      TrueClass :locked
      String :content
      String :html_full
      String :html_short
    end

    db.create_table(:vote) do
      primary_key :id
      Integer :proposition
      Integer :member
      BigDecimal :rating, :size => 2
    end

    db
  end

  def create_mock_member(db)
    db[:member].insert(
      :login => 'test',
      :full_name => 'test',
      :email => 'test@localhost'
    )
  end
end