Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/encoding/sparql-results-tsv.lisp

KindCoveredAll%
expression27217 12.4
branch020 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
2
 
3
 (in-package :org.datagraph.spocq.implementation)
4
 
5
 ;;; tsv serializer as streamed columns.
6
 ;;; each solution is (at least) one line, with the first containing the dimensions
7
 ;;; see http://www.w3.org/TR/sparql11-results-csv-tsv/
8
 
9
 ;;; no reading
10
 
11
 
12
 ;;; writing - either boolean or bindings
13
 
14
 (defun tsv-eol (stream)
15
   (write-char #\newline stream))
16
 
17
 (defun write-tsv-dimensions (dimensions stream)
18
   (when dimensions
19
     (let ((dimension (pop dimensions)))
20
       (if dimensions
21
         (format stream "?~a~c" dimension #\tab)
22
         (format stream "?~a" dimension))
23
       (write-tsv-dimensions dimensions stream))))
24
 
25
 
26
 (defgeneric write-sparql-results+tsv (results stream)
27
   (:documentation "Encode the result field to the stream as text/tsv.
28
  The results are one solutions per line with commas between each term.
29
  The first element is a header, which specifies the variable names.
30
  The remaining entries are solutions sets. if a variable is unbound, there is no output.
31
  If a string is blank, a pair of double quotes appears.")
32
   
33
   (:method ((result symbol) (stream t))
34
     (format stream "result~%")
35
     (encode-turtle-object result stream)
36
     (tsv-eol stream)
37
     (incf-stat *statements-returned*))
38
   
39
   (:method ((results cons) (stream t))
40
     (let* ((dimensions (first results))
41
            (solutions (rest results))
42
            (index 0)
43
            (start (or (response-offset) 0))
44
            (end (response-end)))
45
       (write-tsv-dimensions dimensions stream)
46
       (tsv-eol stream)
47
       (dolist (result solutions)
48
         (when (>= index start)
49
           (when (and end (>= index end))
50
             (return))
51
           (loop for value in result
52
                 for first = t then nil
53
                 do (progn (unless first (write-char #\tab stream))
54
                      (unless (spocq:unbound-variable-p value)
55
                        (encode-turtle-object value stream)))))
56
         (incf index)
57
         (tsv-eol stream))
58
       (incf-stat *statements-returned* index)))
59
   
60
   (:method ((results boolean-generator) (stream t))
61
     (let* ((channel (boolean-generator-channel results)))
62
       (format stream "?result")
63
       (tsv-eol stream)
64
       (write-string (spocq:literal-lexical-form (if (get-field-page channel) spocq.a:|true| spocq.a:|false|)) stream)
65
       (tsv-eol stream)
66
       (incf-stat *statements-returned*)))
67
   
68
   (:method ((results solution-generator) (stream t))
69
     (let* ((dimensions (solution-generator-dimensions results))
70
            (channel (solution-generator-channel results))
71
            (variable-count (length dimensions))
72
            (index 0)
73
            (start (or (response-offset) 0))
74
            (end (response-end)))
75
       (flet ((term-aspect-encoder (term-type term-literal term-language-tag term-datatype)
76
                (encode-turtle-term-aspects term-type term-literal term-language-tag term-datatype stream)))
77
         (declare (dynamic-extent #'term-aspect-encoder))
78
         (let ((term-deconstructor (repository-term-deconstructor *transaction*)))
79
           (write-tsv-dimensions dimensions stream)
80
           (tsv-eol stream)
81
           (do-pages (page channel)
82
             (if (and end (>= index end))
83
               (return)
84
               (if (>= (+ index (array-dimension page 0)) start)
85
                 (cond ((= variable-count (array-dimension page 1))
86
                        (dotimes (page-index (array-dimension page 0))
87
                          (when (>= index start)
88
                            (when (and end (>= index end))
89
                              (return))
90
                            (loop for value-index from 0 below variable-count
91
                                  do (progn (unless (zerop value-index) (write-char #\tab stream))
92
                                            (let ((term-id (aref page page-index value-index)))
93
                                              (cond ((= term-id +null-term-id+))
94
                                                    (t
95
                                                     (funcall term-deconstructor #'term-aspect-encoder *transaction* term-id))))))
96
                            (tsv-eol stream))
97
                          (incf index)))
98
                       (t
99
                        (log-warn "field width mismatch: ~s : ~s."
100
                                  dimensions (array-dimension page 1))
101
                        (incf index (array-dimension page 0))))
102
                 ; otherwise skip the entire page
103
                 (incf index (array-dimension page 0)))))))
104
       (incf-stat *statements-returned* index))))
105
 
106
 
107
 ;;;;;;;
108
 
109
 (defmethod send-error-message ((body t) (stream t) (content-type mime:text/tab-separated-values))
110
   "Send an error response encoded as sse"
111
   (send-error-message body stream mime:application/sparql-query+sse))
112
 
113
 (defmethod send-response-message (operation (message-body t) (stream t)
114
                                             (content-type mime:text/tab-separated-values))
115
   "Given a MESSAGE, and a STREAM with the text/csv CONTENT-TYPE, encode as tsv, streamed"
116
   (when *encoding-trace-output*
117
     (setf stream (make-broadcast-stream *encoding-trace-output* stream)))
118
   (let ((*package* *spocq-reader-package*)
119
         (*expand-literal-values* nil))
120
     (write-sparql-results+tsv message-body stream)))
121
 
122
 (defgeneric tsv-sparql (query pathname &rest args)
123
   (:method ((query t) (destination pathname) &rest args)
124
     (with-open-file (output-stream destination :direction :output 
125
                                    :element-type 'character
126
                                    :if-does-not-exist :create :if-exists :supersede)
127
       (apply #'tsv-sparql query output-stream args)))
128
   (:method ((query t) (destination stream) &rest args)
129
     (multiple-value-bind (results dimensions *task*)
130
                          (apply #'run-sparql query args)
131
       (let ((*expand-literal-values* nil))
132
         (with-accounting
133
             (write-sparql-results+tsv (cons dimensions results)
134
                                       destination))))))
135
 #|
136
 
137
 (write-sparql-results+tsv '((?::a ?::s ?::z) (1 2 3) (<http://example/1> <http://example/2> <http://example/3>))
138
                           *trace-output*)
139
 
140
 (write-sparql-results+tsv 'spocq.a:|true| *trace-output*)
141
 
142
 (send-response-message :test
143
                        '((?::a ?::s ?::z) (1 2 3) (<http://example/1> <http://example/2> <http://example/3>))
144
                        *trace-output*
145
                        mime:text/tab-separated-values)
146
 |#