Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/utilities.lisp
| Kind | Covered | All | % |
| expression | 3079 | 7816 | 39.4 |
| branch | 261 | 730 | 35.8 |
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; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
(require-features (or :digitool :sbcl :clozure :lispworks)
6
"This file must be conditionalized for ~a."
7
(lisp-implementation-type))
9
(defgeneric describe-class-list (designator)
10
(:method ((name symbol))
11
(let ((class (find-class name nil)))
13
(describe-class-list class))
16
(:method ((class class))
17
(let ((info (sb-kernel::info :type :classoid-cell (class-name class)))
18
(cell (sb-kernel::find-classoid-cell (class-name class)))
19
(classoid (sb-kernel::find-classoid (class-name class))))
20
`( ,(class-name class)
23
:classoid ,classoid))))
24
(defun describe-class (class &optional (stream *trace-output*))
25
(pprint (describe-class-list class) stream)
27
(finish-output stream))
31
(defmethod make-load-form ((str ppcre::str) &optional environment)
32
(make-load-form-saving-slots str :environment environment))
34
(defun call-packaged-macro-character (stream char function-name original-readtable)
35
(let ((actual-reader (find-symbol (symbol-name function-name) *package*)))
37
(funcall actual-reader stream char))
39
(unread-char char stream)
40
(let ((*readtable* original-readtable))
43
(defun call-packaged-dispatch-macro-character (stream char arg function-name original-readtable original-reader)
44
(let ((actual-reader (find-symbol (symbol-name function-name) *package*)))
46
(funcall actual-reader stream char arg))
48
(let ((*readtable* original-readtable))
49
(funcall original-reader char arg)))
51
(unread-char char stream)
52
;;(error "No reader macro defined for the dispatch: ~s ~s." char arg)
53
(let ((*readtable* original-readtable))
58
(defun set-packaged-macro-character (character function-name &optional (readtable *readtable*))
59
(let ((original-reader (get-macro-character character (copy-readtable nil)))
60
(original-readtable (copy-readtable readtable)))
61
(flet ((maybe-reader (stream char)
62
(call-packaged-macro-character stream char function-name original-readtable)))
64
(set-macro-character character original-reader original-readtable))
65
(set-macro-character character #'maybe-reader *readtable*))))
67
(defun set-packaged-dispatch-macro-character (dispatch character function-name &optional (readtable *readtable*))
68
(let ((original-reader (get-dispatch-macro-character dispatch character (copy-readtable nil)))
69
(original-readtable (copy-readtable readtable)))
70
(flet ((maybe-reader (stream char arg)
71
(call-packaged-dispatch-macro-character stream char arg function-name
72
original-readtable original-reader)))
74
(set-dispatch-macro-character dispatch character original-reader original-readtable))
75
(set-dispatch-macro-character dispatch character #'maybe-reader *readtable*))))
77
(defun read-regex-function (stream &rest args)
78
(declare (ignore args))
79
(let ((pattern (read stream t nil t)))
80
(assert (stringp pattern) ()
81
"Invalid regular expression pattern: ~s" pattern)
83
(cl-ppcre:scan-to-strings (load-time-value (cl-ppcre:create-scanner ,pattern))
86
(set-dispatch-macro-character #\# #\~ 'read-regex-function)
89
;;; patches in general
91
(defun load-patch (pathname &key description)
93
(push `(:pathname ,pathname :time ,(get-universal-time) ,@(when description `(:description ,description)))
96
(defun build-revisions ()
97
`(:spocq.i ,*build-revision*
98
,@(when (boundp 'dydra-ndk::*build-revision*)
99
`(:dydra-ndk ,(symbol-value 'dydra-ndk::*build-revision*)))))
103
(defgeneric stream-write-external-utf8-string (stream %string)
104
(:method ((stream amqp:channel) %string)
106
for byte = (cffi:mem-ref %string :uint8 i)
108
do (amqp.i::amqp-stream-write-byte stream byte)))
109
(:method ((stream stream) %string)
110
(write-string (cffi:foreign-string-to-lisp %string) stream)))
116
(defmacro undefun (name parameters &body body)
117
(declare (ignore name parameters body))
118
;(warn "not defining: ~s ~s" name parameters)
121
(defmacro undefmethod (name parameters &body body)
122
(declare (ignore name parameters body))
123
;;(warn "not defining: ~s ~s" name parameters)
126
(defmacro undefgeneric (name parameters &body body)
127
(declare (ignore name parameters body))
128
;(warn "not defining: ~s ~s" name parameters)
131
(defmacro term-number-typecase (term-number &rest body)
132
(let ((op (gensym (string 'term-number-typecase)))
133
(type (gensym (string 'type))))
134
`(flet ((,op (,type) (case ,type ,@body)))
135
(declare (dynamic-extent #',op))
136
(call-with-term-number-type #',op ,term-number))))
138
(defmacro with-term-record ((record) &body body)
139
`(cffi:with-foreign-object (,record '(:struct rdfcache::term))
142
(defmacro with-cursor-record ((record) &body body)
143
`(cffi:with-foreign-object (,record '(:struct rdfcache::cursor))
146
(defmacro _print-unreadable-object ((object stream &rest args) &body body)
147
`((lambda (object stream)
148
(handler-case (print-unreadable-object (object stream ,@args) ,@body)
150
(ignore-errors (print-unreadable-object (object stream :identity t :type t)
151
(format stream "error printing object: ~a" c))))))
154
(setf (ccl:assq '_print-unreadable-object ccl:*fred-special-indent-alist*) 1)
156
(defmacro do-statements ((term-match-variables transaction context subject predicate object)
158
"Given a repository handle and a term pattern, iterate over
159
the matching term combinations with each match variable bound to the respective term. If a variable
160
position is nil, that term is ignored."
162
(let ((parameter-list (loop for variable in term-match-variables
163
if variable collect variable
164
else collect (gensym))))
165
`(flet ((each-statement ,parameter-list
166
,@(unless (every #'symbol-package parameter-list)
167
`((declare (ignore ,@(remove-if #'symbol-package parameter-list)))))
169
(declare (dynamic-extent #'each-statement))
170
(map-statements #'each-statement ,transaction
171
,context ,subject ,predicate ,object))))
174
(defmacro with-matched-terms (((g s p o) &key (subject '(repository-wildcard-term *transaction*))
175
(predicate '(repository-wildcard-term *transaction*))
176
(object '(repository-wildcard-term *transaction*))
179
(let ((op (gensym "matchop")))
180
`(flet ((,op (,g ,s ,p ,o)
181
(declare (ignorable ,g ,s ,p ,o))
183
(declare (dynamic-extent #',op))
184
(repository-call-with-matched-terms #',op *transaction* ,subject ,predicate ,object ,@(when g-s `(:content ,graph))))))
186
(defmacro with-numbered-term ((term number &key (context '*transaction*)) &body body)
189
(declare (type cffi:foreign-pointer ,term ))
191
(declare (dynamic-extent #',op))
192
(repository-call-with-numbered-term #',op ,context ,number))))
194
(defmacro with-numbered-term-aspects (((type literal tag datatype) number &key (context '*transaction*)) &body body)
196
`(flet ((,op (,type ,literal ,tag ,datatype)
197
(declare (type cffi:foreign-pointer ,literal ,tag ,datatype))
199
(declare (dynamic-extent #',op))
200
(repository-call-with-numbered-term-aspects #',op ,context ,number))))
202
(defmacro with-rdfcache-cursor ((cursor) &body body)
203
(let ((op (gensym "with-query-cursor")))
204
`(flet ((,op (,cursor) ,@body))
205
(declare (dynamic-extent #',op))
206
(call-with-rdfcache-cursor #',op))))
210
; monolithic decoding
211
(defmacro with-term-string ((string %term-string) &body body)
212
`(let* ((.term-value-len. (rdfcache::%%utf8-get-length ,%term-string))
213
(.term-value-ptr. ,%term-string)
214
(,string (make-string .term-value-len.)))
215
(declare (type rdfcache::foreign-pointer ,%term-string .term-value-ptr.)
216
(type fixnum .term-value-len.)
217
(type simple-string ,string)
218
(dynamic-extent ,string))
219
(dotimes (.term-value-pos. .term-value-len.)
220
(declare (type fixnum .term-value-pos.))
221
(let ((.term-value-chr. (rdfcache::%%utf8-get-char .term-value-ptr.)))
222
(declare (type fixnum .term-value-chr.))
223
(setf (char ,string .term-value-pos.) (code-char .term-value-chr.)))
224
(setq .term-value-ptr. (rdfcache::%%utf8-next-char .term-value-ptr.)))
228
(defun decode-term-string (string %term-string &optional (length (length string)))
229
(declare (type simple-string string)
230
(type rdfcache::foreign-pointer %term-string)
231
(optimize (speed 3) (safety 0)))
232
(dotimes (term-value-pos length)
233
(declare (type fixnum term-value-pos))
234
(let ((term-value-chr (rdfcache::%%utf8-get-char %term-string)))
235
(declare (type fixnum term-value-chr))
236
(setf (char string term-value-pos) (code-char term-value-chr)))
237
(setq %term-string (rdfcache::%%utf8-next-char %term-string))))
239
(defun call-with-term-string (op %term-string)
240
(declare (type cffi:foreign-pointer %term-string))
241
(let* ((term-value-len (rdfcache::%%utf8-get-length %term-string))
242
(string (make-string term-value-len)))
243
(declare (type fixnum term-value-len)
244
(type simple-string string)
245
(dynamic-extent string))
246
(decode-term-string string %term-string term-value-len)
247
(funcall op string)))
250
(defun call-with-term-string (op %term-string)
251
(declare (type rdfcache::foreign-pointer %term-string))
252
(let ((string (cffi:foreign-string-to-lisp %term-string)))
253
(funcall op string))))
255
(defmacro with-term-attributes ((term-type-var term-value-var term-language-var term-datatype-var) term &body body)
256
(let ((body-op (gensym "BODY-")))
257
`(rdfcache::with-checked-pointer (,term)
258
(let* ((,term-type-var (cffi:foreign-enum-value 'rdfcache::term-type
259
(cffi:foreign-slot-value ,term 'rdfcache::term 'rdfcache::type)))
260
(.term-value-ptr. (cffi:foreign-slot-value ,term 'rdfcache::term 'rdfcache::value))
261
(.term-language-ptr. (cffi:foreign-slot-value ,term 'rdfcache::term 'rdfcache::language))
262
(.term-datatype-ptr. (cffi:foreign-slot-value ,term 'rdfcache::term 'rdfcache::datatype)))
263
(flet ((,body-op (,term-type-var ,term-value-var ,term-language-var ,term-datatype-var)
267
(,body-op :none nil nil nil))
268
(t (with-term-string (,term-value-var .term-value-ptr.)
269
(cond ((and .term-language-ptr. (not (cffi:null-pointer-p .term-language-ptr.)))
270
(with-term-string (,term-language-var .term-language-ptr.)
271
(,body-op ,term-type-var ,term-value-var ,term-language-var nil)))
272
((and .term-datatype-ptr. (not (cffi:null-pointer-p .term-datatype-ptr.)))
273
(with-term-string (,term-datatype-var .term-datatype-ptr.)
274
(,body-op ,term-type-var ,term-value-var nil ,term-datatype-var)))
276
(,body-op ,term-type-var ,term-value-var nil nil)))))))))))
278
(defmacro with-task ((variable &rest args) &body body)
280
`(flet ((,op (,variable)
281
(declare (ignorable ,variable))
283
(declare (dynamic-extent #',op))
284
(call-with-task #',op ,@args))))
287
(defmacro with-task-environment ((&key (query '*query*) (task query) normal-disposition abnormal-disposition)
289
"Bind the given query instance to the dynamic *query* variable, access and bind it's state variables,
290
and execute the body in this dynamic context."
292
`(flet ((,op () ,@body))
293
(declare (dynamic-extent #',op))
294
(call-with-task-environment #',op ,task
295
,@(when normal-disposition `(:normal-disposition ,normal-disposition))
296
,@(when abnormal-disposition `(:abnormal-disposition ,abnormal-disposition))))))
299
;;; foreign data accessors
301
(deftype foreign-array (element-type dimensions)
302
"declare the type and dimensions for a foreign array. these serve documentation purposes
303
only in those runtimes which promote the type to some standard type."
304
(declare (ignore element-type dimensions))
305
'cffi:foreign-pointer)
308
(define-declaration foreign-type (declaration &optional env)
309
"Support declaration information specific to foreign types beyond the
310
external pointer type available as the standard type declaraton.
311
In particular, a the foreign-array type includes the dimensions and the
314
(destructuring-bind (tag type . variables) declaration
315
(let ((old (when env (declaration-information tag env))))
316
(values :declare `(,tag (,type . ,variables)
320
(defun foreign-type-declaration (variable env)
321
(let ((declarations (declaration-information 'foreign-type env)))
322
(loop for (type . variables) in declarations
323
when (member variable variables)
325
finally (error "foreign type is not defined: ~s: ~s" variable env))))
327
(eval-when (:compile-toplevel :load-toplevel :execute)
328
(defun array-row-major-index-form (dimensions &rest subscripts)
329
`(+ ,@(maplist (lambda (s d)
331
`(* (the fixnum ,(car s)) ,(if (cddr d) (apply #'* (cdr d)) (second d)))
332
`(the fixnum ,(car s))))
335
;; (array-row-major-index-form '(* 3 4) 1 2 3) => (+ (* (THE FIXNUM 1) 12) (* (THE FIXNUM 2) 4) (THE FIXNUM 3))
337
(defmacro foreign-array-ref (pointer-variable &rest subscripts &environment env)
338
"rewrite the multi-dimensional foreign array reference to a foreign vector reference given the
339
base pointer and the subscripts."
340
(macroexpand-foreign-array-ref pointer-variable subscripts env))
342
(defun macroexpand-foreign-array-ref (pointer-variable subscripts env)
343
(let ((declaration (foreign-type-declaration pointer-variable env)))
344
(assert (typep declaration '(cons (eql foreign-array))) ()
345
"variable not declared to be a foreign array: ~s : ~s : ~s" pointer-variable declaration env)
346
(destructuring-bind (type element-type dimensions) declaration
347
(declare (ignore type))
348
`(,+matrix-accessor+ (the sb-sys:system-area-pointer ,pointer-variable)
349
(the fixnum (* ,(cffi:foreign-type-size element-type)
350
,(apply #'array-row-major-index-form dimensions subscripts)))))))
353
(define-setf-expander foreign-array-ref (pointer-variable &rest subscripts &environment env)
354
(let ((declaration (foreign-type-declaration pointer-variable env)))
355
(assert (typep declaration '(cons (eql foreign-array))) ()
356
"variable not declared to be a foreign array: ~s : ~s" pointer-variable declaration)
358
(destructuring-bind (type element-type dimensions) declaration
359
(declare (ignore type))
360
(let ((pointer-tmp (gensym))
361
(offset-tmp (gensym))
362
(value-tmp (gensym)))
363
(values (list pointer-tmp offset-tmp)
364
(list `(the sb-sys:system-area-pointer ,pointer-variable)
365
`(the fixnum (* ,(cffi:foreign-type-size element-type)
366
,(apply #'array-row-major-index-form dimensions subscripts))))
368
`(locally (declare (type sb-sys:system-area-pointer ,pointer-tmp)
369
(type fixnum ,offset-tmp))
370
(setf (,+matrix-accessor+ ,pointer-tmp ,offset-tmp) ,value-tmp))
371
`(,+matrix-accessor+ ,pointer-tmp ,offset-tmp))))))
374
(define-setf-expander foreign-quad-ref (pointer-variable &rest subscripts &environment env)
375
(multiple-value-bind (scope bound-p type-info)
376
(variable-information pointer-variable env)
377
(declare (ignore scope))
378
(assert (and bound-p (eq (rest (assoc 'type type-info)) 'SB-SYS:SYSTEM-AREA-POINTER)) ()
379
"variable not declared to be a foreign pointer: ~s : ~s" pointer-variable env)
380
(let ((pointer-tmp (gensym))
381
(offset-tmp (gensym))
384
(values (list pointer-tmp offset-tmp)
385
(list `(the sb-sys:system-area-pointer ,pointer-variable)
386
`(the fixnum (* ,(cffi:foreign-type-size +MATRIX-ELEMENT-TYPE+)
387
,(apply #'array-row-major-index-form dimensions subscripts))))
389
`(locally (declare (type sb-sys:system-area-pointer ,pointer-tmp)
390
(type fixnum ,offset-tmp))
391
(setf (,+matrix-accessor+ ,pointer-tmp ,offset-tmp) ,value-tmp))
392
`(,+matrix-accessor+ ,pointer-tmp ,offset-tmp)))))
395
(defun named-term-number-object (term-number name)
396
(if (= term-number +null-term-id+)
397
(spocq:make-unbound-variable name)
398
(term-number-object term-number)))
400
(defmacro foreign-array-named-object-ref (name pointer-variable &rest subscripts)
401
"Return a reference to a foreign field array as a lisp object. The mapping delegates to rdfcache's
402
construction/caching mechanism except in the case of a null term, which is recognized here mapped to
403
an unbound variable instance. NB. the instance must be returned as that acts as a quasi-first-class
404
value for sort predicates."
406
`(named-term-number-object (foreign-array-ref ,pointer-variable ,@subscripts)
409
#+(or) ; fails because the declarations get lost
410
(defsetf foreign-array-named-object-ref (name pointer-variable &rest subscripts) (value)
411
(declare (ignore name)) ; needed for symmetry with the access form
412
`(setf (foreign-array-ref ,pointer-variable ,@subscripts)
413
(object-term-number ,value)))
416
(define-setf-expander foreign-array-named-object-ref (name pointer-variable &rest subscripts &environment env)
417
(let ((declaration (foreign-type-declaration pointer-variable env)))
418
(assert (typep declaration '(cons (eql foreign-array))) ()
419
"variable not declared to be a foreign array: ~s : ~s" pointer-variable declaration)
421
(destructuring-bind (type element-type dimensions) declaration
422
(declare (ignore type))
423
(let ((pointer-tmp (gensym))
424
(offset-tmp (gensym))
425
(value-tmp (gensym)))
426
(values (list pointer-tmp offset-tmp)
427
(list `(the sb-sys:system-area-pointer ,pointer-variable)
428
`(the fixnum (* ,(cffi:foreign-type-size element-type)
429
,(apply #'array-row-major-index-form dimensions subscripts))))
431
`(locally (declare (type sb-sys:system-area-pointer ,pointer-tmp)
432
(type fixnum ,offset-tmp))
433
(setf (,+matrix-accessor+ ,pointer-tmp ,offset-tmp)
434
(object-term-number ,value-tmp)))
435
`(named-term-number-object (,+matrix-accessor+ ,pointer-tmp ,offset-tmp)
443
(defmacro with-term-string ((string %term-string) &body body)
444
"Bind the STRING variable to the heap representation of the given exteral %TERM-STRING"
446
`(flet ((,op (,string)
447
(declare (dynamic-extent ,string))
449
(declare (dynamic-extent #',op))
450
(call-with-term-string #',op ,%term-string))))
452
(defmacro with-transaction ((&rest args
453
&key (transaction '(task-transaction *task*))
454
(repository '(task-revision *task*))
455
(revision repository)
456
normal-disposition abnormal-disposition)
458
"Wrap the body in a dynamic context which opens and optionaly closes a transaction for the
459
given repository. It uses a transaction instance which is created lazily for the current task
460
and the task's abstract repository instance.
461
It dynamically binds the transaction to *transaction* and execute the body."
463
(declare (ignore normal-disposition abnormal-disposition))
464
(setf args (copy-list args))
465
(remf args :transaction)
466
(remf args :revision)
467
(remf args :repository)
468
(let ((op (gensym "transaction-op-")))
469
`(flet ((,op (*transaction*) ,@body))
470
(declare (dynamic-extent #',op))
471
(unwind-protect (call-with-revision-transaction #',op ,revision ,transaction ,@args)
472
(setq *transaction* nil)))))
474
(defmacro with-open-repository ((repository-id &rest args) &rest body)
475
"Instantiate the given repository, bind its latest revision dynamically to *repository* and execute within a
476
transaction based on that revision."
477
(when (and (find :read-only args) (not (find :read-only-p args)))
478
(warn "with-open-repository requires :read-only-p")
479
(setf args (copy-list args))
480
(setf (getf args :read-only-p) (getf args :read-only))
481
(remf args :read-only))
482
(let ((op (gensym "repository-op")))
483
`(flet ((,op (*repository*) ,@body))
484
(declare (dynamic-extent #',op))
485
(call-with-open-repository #',op ,repository-id ,@args))))
487
(defmacro with-repository (repository &body body)
488
`(let ((*repository* ,repository))
491
(defmacro with-open-transaction ((repository-id &rest args
493
api-key id revision-id
496
normal-disposition abnormal-disposition
497
(read-only-p (not (eq normal-disposition :commit)) rop-s)
500
"Create just an autonomous transaction for the given repository -optionally specific as
501
to revision, bind it dynamically to *transaction* and execute the body."
502
(declare (ignore api-key id revision-id operation if-does-not-exist abnormal-disposition serialize))
503
(let ((op (gensym "transaction-op")))
504
(unless rop-s (setf args (list* :read-only-p read-only-p args)))
505
`(flet ((,op (*transaction*) ,@body))
506
(declare (dynamic-extent #',op))
507
(unwind-protect (call-with-open-transaction #',op ,repository-id ,@args)
508
(setq *transaction* nil)))))
511
(defmacro print-lexical-frame (&optional name &environment env)
512
(let ((vars (remove nil (mapcar #'first (sb-c::lexenv-vars env)))))
513
`(format *trace-output* "~%~@[~a: ~]~s~%"
515
(list ,@(loop for var in vars
516
for var-name = (symbol-name var)
517
when (eq (find-symbol var-name *package*) var)
518
collect `(list ',var ,var))))))
520
;;; class hierarchy manipulation to get abstract iri class
521
;;; both the targets are just standard-object dsp[acializations, so pushed is fine
523
(defgeneric interpose-superclass (add-class amqp-class)
524
(:method ((add-class symbol) (class t))
525
(interpose-superclass (find-class add-class) class))
526
(:method ((add-class t) (class symbol))
527
(interpose-superclass add-class (find-class class)))
528
(:method ((add-class class) (class class))
529
(let ((existing-supers (c2mop:class-direct-superclasses class)))
530
(unless (find add-class existing-supers)
531
(reinitialize-instance class
532
:direct-superclasses (cons add-class existing-supers))))
535
(defgeneric null-sequence-p (object)
536
(:method ((object null)) t)
537
(:method ((object sequence)) (zerop (length object)))
538
(:method ((object t)) nil))
540
(defun class-designator-p (object)
541
(when (and object (symbolp object) (find-class object nil))
545
;;; iri/uri operators
547
(defgeneric iri-p (object)
548
(:documentation "Return true iff the given OBJECT represents an IRI.
549
The implementation distinguishes the self-evident classes (URI and UUID) by type and
550
classifies symbols according to whether their package is registered as a vocabaulary.
551
All other types are non-iri. Requires transparent arguments.")
552
(:method ((object t)) nil)
553
(:method ((object symbol)) (when (get-symbol-uri-namestring object) t))
554
(:method ((object spocq:iri)) t)
555
(:method ((object null)) nil))
557
(deftype iri () '(satisfies iri-p))
558
(deftype iri-designator () '(or iri string))
560
(defgeneric http-url-p (object)
561
(:documentation "Return true iff the given OBJECT represents an http url.
562
The implementation distinguishes the self-evident classes (URI and UUID) by type and
563
classifies symbols according to whether their package is registered as a vocabaulary.
564
All other types are non-iri. Requires transparent arguments.")
565
(:method ((object t)) nil)
566
(:method ((object symbol)) (http-url-p (iri-lexical-form object)))
567
(:method ((object string))
568
(is-http-url object))
569
(:method ((object spocq:http-url)) t)
570
(:method ((object null)) nil))
572
(defgeneric file-url-p (object)
573
(:documentation "Return true iff the given OBJECT represents an http url.
574
The implementation distinguishes the self-evident classes (URI and UUID) by type and
575
classifies symbols according to whether their package is registered as a vocabaulary.
576
All other types are non-iri. Requires transparent arguments.")
577
(:method ((object t)) nil)
578
(:method ((object symbol)) (file-url-p (iri-lexical-form object)))
579
(:method ((object string))
580
(is-file-url-namestring object))
581
(:method ((object spocq:file-url)) t)
582
(:method ((object null)) nil))
584
(defgeneric is-absolute-iri (string)
585
(:method ((datum null))
588
(is-absolute-iri-string (iri-lexical-form datum)))
589
(:method ((lexical-form string))
590
(is-absolute-iri-string lexical-form)))
592
(defun absolute-iri-p (object)
594
(is-absolute-iri (iri-lexical-form object))))
596
(deftype absolute-iri () '(satisfies absolute-iri-p))
598
(defun iri-package-p (package)
599
(when (member package *iri-packages*) t))
601
(defun format-iri (stream datatype &optional colon at)
602
(declare (ignore colon at))
603
(write-string (term-lexical-form datatype) stream))
605
(defgeneric cl-user::format-iri-url-encoded (stream iri &optional colon at)
606
(:method (stream (string string) &optional colon at)
607
(declare (ignore colon at))
608
(loop for c across string
609
for ci = (char-int c)
611
(= 0 (sbit puri::*reserved-characters* ci)))
612
do (write-char c stream)
613
else do (multiple-value-bind (q r) (truncate ci 16)
614
(write-char #\% stream)
615
(write-char (elt puri::*escaped-encoding* q) stream)
616
(write-char (elt puri::*escaped-encoding* r) stream))))
617
(:method (stream (iri t) &optional colon at)
618
(declare (ignore colon at))
619
(cl-user::format-iri-url-encoded stream (iri-lexical-form iri))))
620
;;;(format nil "~/format-iri-url-encoded/" "(select * { }")
622
(defun url-encode (string)
623
(let ((result (make-array (length string) :element-type 'character :fill-pointer 0 :adjustable t)))
624
(loop for c across string
625
for ci = (char-int c)
627
(/= ci #.(char-code #\newline)) ;; added
628
(/= ci #.(char-code #\')) ;; added
629
(= 0 (sbit puri::*reserved-characters* ci)))
630
do (vector-push-extend c result)
631
else do (multiple-value-bind (q r) (truncate ci 16)
632
(vector-push-extend #\% result)
633
(vector-push-extend (elt puri::*escaped-encoding* q) result)
634
(vector-push-extend (elt puri::*escaped-encoding* r) result)))
636
#+(or) ;; alternative definition
637
(defun url-rewrite:url-encode (string)
638
"URL-encode a string."
639
(with-output-to-string (s)
640
(loop for c across string
641
do (cond ((or (char<= #\0 c #\9)
644
(find c "$-_.!*'()," :test #'char=))
648
(t (format s "%~2,'0x" (char-code c)))))))
650
(defun url-decode (string &key (start 0) (end (length string)))
652
(decoded-string (make-array (length string) :element-type 'character :adjustable t :fill-pointer 0)))
653
(loop (when (>= from-i end) (return))
654
(let ((char (char string from-i)))
655
(cond ((eql char #\%)
656
(vector-push-extend (code-char (parse-integer string :start (+ from-i 1) :end (+ from-i 3) :radix 16))
660
(vector-push-extend #\space decoded-string)
663
(vector-push-extend char decoded-string)
667
(defgeneric iri-namespace (iri)
668
(:method ((iri symbol))
669
(when (symbol-uri-namestring iri)
670
(package-name (symbol-package iri))))
671
(:method ((iri spocq:iri))
672
(iri-namespace (spocq:iri-lexical-form iri)))
673
(:method ((iri puri:uri))
674
(case (puri:uri-scheme iri)
676
(if (puri:uri-fragment iri)
677
(format nil "http://~a~@[~a~]#"
680
(let* ((path (rest (puri:uri-parsed-path iri))))
681
(format nil "http://~a/~{~a/~}"
685
(format nil "urn:~a:"
686
(puri:urn-nid iri)))))
687
(:method ((lexical-form string))
688
(iri-namespace (puri:uri lexical-form))))
689
;;; (iri-namespace "http://dydra.com#asdf")
690
;;; (iri-namespace "http://dydra.com/asdf")
691
;;; (iri-namespace "http://dydra.com/asdf/qwer")
692
;;; (iri-namespace "http://dydra.com/asdf/qwer/")
693
;;; (iri-namespace "http://dydra.com/asdf/qwer/#yxcv")
695
(defgeneric iri-local-part (iri)
696
(:method ((iri symbol))
698
(:method ((iri spocq:iri))
699
(iri-local-part (spocq:iri-lexical-form iri)))
700
(:method ((iri puri:uri))
701
(case (puri:uri-scheme iri)
703
(or (puri:uri-fragment iri)
704
(let* ((path (rest (puri:uri-parsed-path iri)))
705
(leaf (first (last path))))
706
(when (plusp (length leaf)) leaf))))
708
(puri:urn-nss iri))))
709
(:method ((lexical-form string))
710
(when (char= #\< (char lexical-form 0))
711
(setf lexical-form (subseq lexical-form 1 (1- (length lexical-form)))))
712
(iri-local-part (puri:uri lexical-form)))
713
(:method ((node spocq:blank-node))
714
(spocq:blank-node-label node)))
716
;;; (iri-local-part "http://dydra.com#asdf")
717
;;; (iri-local-part "http://dydra.com/asdf")
718
;;; (iri-local-part "http://dydra.com/asdf/qwer")
719
;;; (iri-local-part "http://dydra.com/asdf/qwer/")
720
;;; (iri-local-part "http://dydra.com/asdf/qwer/#yxcv")
722
(defgeneric iri-qname (iri)
723
(:method ((iri symbol))
724
(let ((namestring (symbol-uri-namestring iri)))
726
(iri-qname namestring)
727
(concatenate 'string (package-name (symbol-package iri)) ":" (symbol-name iri)))))
729
(iri-qname (puri:uri iri)))
730
(:method ((lexical-form string))
731
(when (char= #\< (char lexical-form 0))
732
(setf lexical-form (subseq lexical-form 1 (1- (length lexical-form)))))
733
(iri-qname (puri:uri lexical-form)))
734
(:method ((iri puri:uri))
735
(let* ((namespace (iri-namespace iri))
736
(local-part (iri-local-part iri))
737
(binding (rassoc namespace *namespace-bindings* :test #'equal)))
739
(concatenate 'string (first binding) ":" local-part)))))
741
(defgeneric iri-label (iri)
742
(:method ((iri symbol))
743
(let ((namestring (symbol-uri-namestring iri)))
745
(iri-label (puri:uri namestring))
747
(:method ((iri spocq:iri))
748
(iri-label (puri:uri (spocq:iri-lexical-form iri))))
749
(:method ((uri puri:uri))
750
(case (puri:uri-scheme uri)
752
(cond ((iri-qname uri))
754
(if (puri:uri-fragment uri)
755
(concatenate 'string (puri:uri-path uri) "#" (puri:uri-fragment uri))
756
(puri:uri-path uri)))
758
(puri:uri-host uri))))
760
(puri:urn-nss uri))))
761
(:method ((lexical-form string))
762
(when (char= #\< (char lexical-form 0))
763
(setf lexical-form (subseq lexical-form 1 (1- (length lexical-form)))))
764
(iri-label (puri:uri lexical-form)))
765
(:method ((node spocq:blank-node))
766
(spocq:blank-node-label node)))
768
;;; (iri-label "http://dydra.com#asdf")
769
;;; (iri-label "http://dydra.com/asdf")
770
;;; (iri-label "http://dydra.com/asdf/qwer")
771
;;; (iri-label "http://dydra.com/asdf/qwer/")
772
;;; (iri-label "http://dydra.com/asdf/qwer/#yxcv")
774
(defparameter *iri-authority-scanner*
775
(cl-ppcre:create-scanner
776
'(:sequence "http" (:greedy-repetition 0 1 #\s) "://"
777
(:register (:GREEDY-REPETITION 0 NIL (:INVERTED-CHAR-CLASS #\/)))
778
(:GREEDY-REPETITION 0 NIL :EVERYTHING))))
779
;;; (cl-ppcre:scan-to-strings *iri-authority-scanner* "<http://asdf/>")
781
(defgeneric iri-authority (iri)
782
(:method ((iri symbol))
783
(let ((namestring (symbol-uri-namestring iri)))
785
(iri-authority (puri:uri namestring))
787
(:method ((iri spocq:iri))
788
(iri-authority (spocq:iri-lexical-form iri)))
789
(:method ((uri puri:uri))
790
(PURI:URI-AUTHORITY uri))
791
(:method ((lexical-form string))
792
(when (char= #\< (char lexical-form 0))
793
(setf lexical-form (subseq lexical-form 1 (1- (length lexical-form)))))
794
(elt (nth-value 1 (cl-ppcre:scan-to-strings *iri-authority-scanner* lexical-form))
796
;;; (iri-authority "<http://asdf/>")
798
(defgeneric iri-keyword (iri)
799
(:method ((iri symbol))
800
(or (get iri 'iri-keyword)
801
(setf (get iri 'iri-keyword) (cons-symbol :keyword (symbol-name iri)))))
803
(cons-symbol :keyword (iri-label iri))))
805
;;; extend the puri library to handle url query arguments
806
(defgeneric iri-parsed-query (iri)
807
(:method ((iri puri:uri))
808
(or (getf (puri:uri-plist iri) :parsed-query)
809
(setf (getf (puri:uri-plist iri) :parsed-query)
810
(loop for (key value) on (split-string (puri:uri-query iri) "&=")
811
append (list (intern (string-upcase key) :keyword) (url-decode value)))))))
813
(defgeneric (setf iri-parsed-query) (parsed iri)
814
(:method ((parsed list) (iri puri:uri))
815
(setf (getf (puri:uri-plist iri) :parsed-query) parsed)))
817
(defgeneric iri-query-argument (iri key)
818
(:method ((iri puri:uri) key)
819
(getf (iri-parsed-query iri) key)))
821
(defgeneric (setf iri-query-argument) (value iri key)
822
(:method (value (iri puri:uri) key)
823
(let ((parsed-query (iri-parsed-query iri)))
825
(setf (getf parsed-query key) value)
826
(remf parsed-query key))
827
(setf (iri-parsed-query iri) parsed-query)
828
(setf (slot-value iri 'puri::query) (format nil "~{~(~a~)=~a~^&~}" parsed-query))
829
(setf (slot-value iri 'puri::string) nil)
833
(defun symbol-variable (symbol)
834
(or (get symbol 'spocq.i::variable)
835
(setf (get symbol 'spocq.i::variable) (make-variable (symbol-name symbol)))))
839
(defmacro incf-stat (variable &optional amount)
840
"provides a distinguished operator for statistic counters."
841
`(incf ,variable ,@(when amount (list amount))))
843
(defun remove-properties (properties plist)
844
(loop for (property value) on plist by #'cddr
845
unless (member property properties)
846
nconc (list property value)))
848
(defun copy-properties (properties plist)
849
(loop for (property value) on plist by #'cddr
850
when (member property properties)
851
nconc (list property value)))
853
;;; unused for solution pages
854
(defmacro collect-solutions ((collector &key (predicate nil) (finally 'rest) (name nil)) &rest body)
855
(let ((list (gensym "LIST-"))
856
(end (gensym "END-")))
857
`(let* ((,list (list nil)) (,end ,list))
859
(flet ((,collector (datum)
861
((nil) `(setf (rest ,end) (list datum) ,end (rest ,end)))
862
(t `(when (funcall ,predicate datum) (setf (rest ,end) (list datum) ,end (rest ,end)))))))
864
(prog1 (,finally ,list)
865
(setf ,list nil ,end nil)))))
868
(setf (ccl:assq 'collect-solutions ccl:*fred-special-indent-alist*) 1)
872
(defun compress-solution-field (field &optional (variables (loop for (variable nil) on (first field)
875
(let ((filter (spocq-compile `(lambda (solution)
876
(flet ((rewrite (&key ,@(mapcar #'(lambda (var)
878
(load-time-value (spocq:make-unbound-variable ',var))))
881
(declare (ignorable ,@variables))
883
(declare (dynamic-extent #'rewrite))
884
(apply #'rewrite solution))))))
885
(cons variables (mapcar filter field))))
888
(defun algebra-thread-count ()
889
"Return the nimber of threads to be created which reducing partitionable algebra operations.
890
Ultimately should reflect the core count. Provisionally just 2."
891
*algebra-thread-count*)
896
(defstruct abstract-solution-field )
897
(defstruct (solution-field (:include abstract-solution-field))
898
;; A list of the variable names for the field.
899
(dimensions () :type list)
900
(sort-dimensions () :type list)
902
(row-index 0 :type fixnum) ; currrent iteration position within a page to read or write
903
(row-count 0 :type fixnum) ; length of the current data array or matrix
904
(length 0 :type fixnum) ; total field length, if page-streamed, the cumulative length
905
; if static, equal to the row count
906
(count 0 :type fixnum) ; caches the expected field size, eg. based on selectivity and repository size
907
(state :closed :type (member :closed :input :output))
910
(defstruct (array-solution-field (:include solution-field (solutions #() :type array)))
911
"A solution-field comprises a dimension list and the solution array / array vector"
914
(defstruct (symbolic-solution-field (:include solution-field (solutions () :type list)))
915
"A solution-field comprises a dimension list and a 2-level solution list"
918
(defstruct (list-solution-field (:include solution-field (solutions () :type list)))
919
"A solution-field comprises a dimension list and a 2-level solution list"
922
(defstruct (null-solution-field (:include solution-field)))
925
(defun solution-field-initialize (field &rest args)
926
(apply #'initialize-solution-field field args))
928
(defun initialize-result-field (field &rest args)
929
(apply #'initialize-solution-field field args))
931
(defgeneric initialize-solution-field (field &key dimensions sort-dimensions row-count data)
934
(defun result-field-dimensions (field)
936
(solution-field-dimensions field)))
938
(defgeneric solution-field-sort (field order)
939
(:documentation "sort the field based on term number values given a row prcedence list"))
942
(defgeneric set-solution-field-solutions (field solutions)
946
(defgeneric solution-field-concatenate (field1 field2)
947
(:method ((field1 t) (field2 null))
949
(:method ((field1 null) (field2 t))
953
(defmethod print-object ((object solution-field) (stream t))
954
(_print-unreadable-object (object stream :type t :identity t)
955
(format stream "~s.~s ~s/~s ~s@~a"
956
(solution-field-dimensions object)
957
(solution-field-sort-dimensions object)
958
(solution-field-row-index object)
959
(solution-field-row-count object)
960
(solution-field-length object)
961
(solution-field-state object))))
965
(defgeneric solution-field-solutions-row-count (field)
966
(:method ((field list)) (length field))
967
(:method ((field array)) (array-dimension field 0))
968
(:method ((field vector)) (reduce #'+ field :key #'(lambda (page) (array-dimension page 0)) :initial-value 0))
969
#+sbcl (:method ((field sb-sys:system-area-pointer))
970
(if (cffi:null-pointer-p field)
972
(rdfcache:matrix-row-count field)))
973
(:method ((field solution-field))
974
(solution-field-row-count (solution-field-solutions field))))
976
(defgeneric solution-field-column-count (field)
977
(:method ((field list)) (length (first field)))
978
(:method ((field array)) (array-dimension field 1))
979
(:method ((field symbolic-solution-field))
980
(length (first (solution-field-solutions field))))
981
(:method ((field array-solution-field))
982
(let ((solutions (solution-field-solutions field)))
985
(sequence (array-dimension (elt solutions 0) 1))
986
(array (array-dimension solutions 1))
989
(defun solution-field-solutions-column-count (field)
990
(solution-field-column-count field))
992
(defgeneric filter-solution-field (field predicate)
993
(:method ((field list) (predicate function))
994
(loop for solution in field
995
when (every predicate solution)
998
(defmacro trace-matrix (&rest args)
999
(if *matrix-trace-output*
1000
`(let ((*print-pretty* nil))
1001
(fresh-line *matrix-trace-output*)
1002
(format *matrix-trace-output* ,@args)
1003
(terpri *matrix-trace-output*)
1004
(finish-output *matrix-trace-output*)
1008
;;; although this is a good logical location to interpose a thread split,
1009
;;; the reduction mechanism means that it would alread have to have happened - otherwise
1010
;;; the field is already complete before it is returned to be used as an argument.
1011
;;; in the always-threaded implementation, this was achievend by generators (the
1012
;;; logical equivalent of volcano's xchg). in this call structure they would still need to
1013
;;; be interposed prospectively.
1014
;;; ideally, the thread should be introduced exactly at the point where a page overflow
1015
;;; happens. up to that point the reduction process sufficis, after that the two steps
1016
;;; should proceed asynchronously.
1017
;;; the most flexible mechaism ist to generalize the reified algebra operators which implement the bgp
1018
;;; processing to cover the entire operator complement and specialize the spocq.e operators to
1019
;;; combine bgp operations either in-line or deferred to threads and to implement the combination
1020
;;; itself inline or as a deferred reified operation.
1021
;;; this delays the htreading decision to the latest possible point based on the cardinality of the
1023
;;; rather than dedicate an operator to this which the planner needs to insert, it would be best
1024
;;; to implement it as an around method on either generic processing function or
1025
;;; add a method to the respective algebraic operators.
1027
(defun call-with-input-fields (function &rest fields)
1028
"Given FUNCTION, a function of no arguments, and FIELDS, the fields upon which to operate,
1029
reset the fields to an initial input state, call the function, and
1030
clean up by releasing the fields' resources as indicated.
1031
VALUES : t : as returned by the function"
1033
(loop for field in fields
1034
unless (eq (solution-field-state field) :input)
1035
do (progn (setf (solution-field-row-index field) 0
1036
(solution-field-state field) :input)
1037
(trace-matrix "input-field: ~s.~s~%~{ ~s~%~}"
1038
(solution-field-dimensions field)
1039
(solution-field-sort-dimensions field)
1040
(term-value-field field))))
1041
(unwind-protect (funcall function)
1042
(loop for field in fields
1043
do (progn (incf-stat *solutions-processed* (solution-field-length field))
1044
(release-field-data field)))))
1046
(defun call-with-result-field (function result-field &rest args)
1047
(unless (eq (solution-field-state result-field) :output)
1048
(apply #'initialize-result-field result-field args)
1049
(setf (solution-field-state result-field) :output))
1050
(unwind-protect (funcall function)
1051
(trace-matrix "result-field: ~s.~s~%~{ ~s~%~}"
1052
(solution-field-dimensions result-field)
1053
(solution-field-sort-dimensions result-field)
1054
(term-value-field result-field))
1055
(incf-stat *solutions-constructed* (solution-field-length result-field))))
1057
(defgeneric release-field-data (field)
1061
(defmacro with-input-fields ((&rest fields) &body body)
1062
(let ((op (gensym "field-op-")))
1063
`(flet ((,op () ,@body))
1064
(declare (dynamic-extent #',op))
1065
(call-with-input-fields #',op ,@fields))))
1067
(defmacro with-result-field ((field &rest arglist) &body body)
1068
(let ((op (gensym "field-op-")))
1069
`(flet ((,op () ,@body))
1070
(declare (dynamic-extent #',op))
1071
(call-with-result-field #',op ,field ,@arglist))))
1075
(defstruct (abstract-field-generator (:include solution-field))
1076
"An abstract-generator adds an activation function and a solution channel to a solution field.
1077
If the channle is present, then the generatin process is active. Otherwise the activation
1078
function is called with a continuation to accept the generated fields.
1079
The channel contains either an already partially materialized field from a thread which
1080
is generating the result, or a complete result from a completed reduction operation
1081
or bgp pattern match."
1086
(concrete-operator nil))
1088
(defmethod print-object ((object abstract-field-generator) (stream t))
1089
(_print-unreadable-object (object stream :type t :identity t)
1090
(format stream "~a x ~a~@[ ~a~]"
1091
(abstract-field-generator-operator object)
1092
(solution-field-dimensions object)
1093
(solution-field-sort-dimensions object))))
1095
(defstruct (solution-generator (:include abstract-field-generator))
1098
(defstruct (leftjoin-solution-generator (:include solution-generator))
1099
"Add the explicit key-dimensions slot to record the initial combination
1100
distinct from the effective combined dimensions. This is used in subsequent
1101
leftjoins to limit keys to dimensions guaranteed to be bound."
1102
(key-dimensions () :type list))
1104
(defgeneric solution-generator-key-dimensions (generator)
1105
(:documentation "Return those dimensions to include in a hash key for a
1106
join / leftjoin. In the leftjoin case, the result field retains this value
1107
rather than the combined dimensions as additional dimensions may not be
1108
bound in a given solution.")
1109
(:method ((generator solution-generator))
1110
(solution-generator-dimensions generator))
1111
(:method ((generator leftjoin-solution-generator))
1112
(leftjoin-solution-generator-key-dimensions generator)))
1114
(defstruct (group-solution-generator (:include solution-generator))
1115
"Add group key bindings to the field description"
1116
(key-bindings () :type list))
1118
(defgeneric solution-generator-key-bindings (generator)
1119
(:documentation "Return the bindings for grouping a field.")
1120
(:method ((generator solution-generator))
1122
(:method ((generator group-solution-generator))
1123
(group-solution-generator-key-bindings generator)))
1125
(defstruct (boolean-generator (:include abstract-field-generator))
1128
(defstruct (bgp-generator (:include solution-generator))
1130
(pattern-function nil))
1132
(defstruct (null-generator (:include solution-generator)))
1135
(defstruct (graph-generator (:include solution-generator))
1136
"A graph-generator generates the particular form of solution field which denotes a graph,
1137
and in which each solution denotes a statement in the graph. Ii is realized as triple-graph-generator
1138
and quad-graph-generator.")
1140
(defstruct (triple-graph-generator (:include graph-generator))
1141
"A triple-graph-generator applies to construct and describe queries.
1142
The means, there are always three dimensions, (?s ?p ?o).")
1144
(defstruct (quad-graph-generator (:include graph-generator))
1145
"A quad-graph-generator is not (yet) used.")
1147
(defstruct (construct-generator (:include triple-graph-generator
1148
(operator 'spocq.a:|construct|)
1149
(dimensions *construct-dimensions*))))
1151
(defstruct (describe-generator (:include triple-graph-generator
1152
(operator 'spocq.a:|describe|)
1153
(dimensions *describe-dimensions*))))
1155
(defstruct (dependent-solution-generator (:include solution-generator))
1156
"A dependent generator expects to be a SIP target, to initiate its execution
1157
based on the passed solution and to emit the effectively merge-joined results."
1161
(defgeneric copy-generator (generator &key dimensions expression)
1162
(:documentation "implements copy operation specific to generators, but with generic name.")
1163
(:method :around ((generator abstract-field-generator) &rest args
1164
&key (dimensions (solution-field-dimensions generator))
1165
(expression (abstract-field-generator-expression generator))
1166
(constituents (abstract-field-generator-constituents generator))
1167
(operator (abstract-field-generator-operator generator)))
1168
(apply #'call-next-method generator :dimensions dimensions :expression expression
1169
:constituents constituents
1172
(:method ((generator boolean-generator) &rest args)
1173
(apply #'make-boolean-generator args))
1174
(:method ((generator solution-generator) &rest args)
1175
(apply #'make-solution-generator args))
1176
(:method ((generator bgp-generator) &rest args &key (pattern (bgp-generator-pattern generator))
1178
(apply #'make-bgp-generator
1182
(defgeneric solution-generator-patterns (generator)
1183
(:method ((generator bgp-generator))
1184
(list (bgp-generator-pattern generator)))
1185
(:method ((generator abstract-field-generator))
1186
(reduce #'append (abstract-field-generator-constituents generator)
1187
:key #'solution-generator-patterns)))
1191
(defmethod print-generator-tree ((generator abstract-field-generator) &key (stream *trace-output*))
1192
(let* ((op (abstract-field-generator-operator generator))
1193
(channel (abstract-field-generator-channel generator))
1194
(*print-pretty* nil)
1195
(generator-level (if (boundp 'generator-level) (1+ (symbol-value 'generator-level)) 1)))
1196
(declare (special generator-level))
1197
(format stream "~&~vT~a :~a"
1198
(* 2 generator-level) op channel)
1199
(loop for constituent in (abstract-field-generator-constituents generator)
1200
do (print-generator-tree constituent :stream stream))))
1203
(defstruct version-map vector length)
1204
(defstruct (timestamp-map (:include version-map (vector nil :type vector))))
1205
(defstruct (ordinal-map (:include version-map)))
1206
(defstruct (foreign-ordinal-map (:include ordinal-map (vector nil :type cffi:foreign-pointer)))
1207
"Intended to contain a foreign ordinal vector and include the length")
1208
(defstruct (vector-ordinal-map (:include ordinal-map (vector nil :type vector)))
1209
"Intended to contain a heap ordinal vector and include the length")
1214
;;; packaged in structs to support sbcl thread-safe operators
1216
(deftype atomic-index () #+sbcl 'sb-ext:word #-sbcl 'integer)
1218
(defstruct (task-indices (:conc-name task-))
1219
(blank-node-index 0 :type atomic-index)
1220
(sequence-index 0 :type atomic-index)
1221
(variable-index 0 :type atomic-index))
1222
(setq *task-indices* (make-task-indices))
1224
(defun next-blank-node-index ()
1225
#+sbcl (1+ (sb-ext:atomic-incf (task-blank-node-index *task-indices*)))
1226
#+lw (system:atomic-fixnum-incf (task-blank-node-index *task-indices*))
1227
#-(or lw sbcl) (incf (task-blank-node-index *task-indices*)))
1229
(defun next-sequence-index ()
1230
#+sbcl (1+ (sb-ext:atomic-incf (task-sequence-index *task-indices*)))
1231
#+lw (system:atomic-fixnum-incf (task-sequence-index *task-indices*))
1232
#-(or lw sbcl) (incf (task-sequence-index *task-indices*)))
1234
(defun next-variable-index ()
1235
#+sbcl (1+ (sb-ext:atomic-incf (task-variable-index *task-indices*)))
1236
#+lw (system:atomic-fixnum-incf (task-variable-index *task-indices*))
1237
#-(or lw sbcl) (incf (task-variable-index *task-indices*)))
1240
(defun construct-dimensions ()
1241
*construct-dimensions*)
1243
(defun describe-dimensions ()
1244
*describe-dimensions*)
1246
(defun union-dimensions (dimensions1 dimensions2)
1247
(sort (remove-duplicates (append dimensions1 dimensions2) :from-end t)
1250
(defun intersect-dimensions (dimensions1 dimensions2)
1251
(sort (intersection dimensions1 dimensions2)
1254
(defgeneric difference-dimensions (dimensions1 dimensions2)
1255
(:method ((dimensions1 list) (dimensions2 list))
1256
(sort (set-difference dimensions1 dimensions2)
1258
(:method ((dimensions1 vector) (dimensions2 t))
1259
(difference-dimensions (loop for x across dimensions1 collect x) dimensions2))
1260
(:method ((dimensions1 t) (dimensions2 vector))
1261
(difference-dimensions dimensions1 (loop for x across dimensions2 collect x))))
1263
(defun join-dimensions (dimensions1 dimensions2)
1264
"given two dimension systems, return their intersection.
1265
allow for nil elements."
1266
(loop for dimension in dimensions1
1267
when (and dimension (member dimension dimensions2))
1270
(defun join-result-dimensions (left &optional right)
1271
(flet ((undistinguished-not-context (variable)
1272
#+(or)(or (and (undistinguished-variable-p variable)
1273
(not (eq variable +context-variable+)))
1274
(null (symbol-package variable)))
1275
(null (symbol-package variable))))
1276
(declare (dynamic-extent #'undistinguished-not-context))
1278
(loop with projected-left = (remove-if #'undistinguished-not-context left)
1279
for dimension in right
1280
unless (or (member dimension projected-left)
1281
(undistinguished-not-context dimension))
1282
collect dimension into projected-right
1283
finally (return (append projected-left projected-right)))
1284
(remove-if #'undistinguished-not-context left))))
1286
(defun join-key-dimensions (left right)
1287
;; iff an undistinguished variable binds the context, then it is from a quad match and should be retained
1288
(flet ((undistinguished-not-context (variable)
1289
#+(or)(or (and (undistinguished-variable-p variable)
1290
(not (eq variable +context-variable+)))
1291
(null (symbol-package variable)))
1292
(null (symbol-package variable))))
1293
(declare (dynamic-extent #'undistinguished-not-context))
1294
(loop for dimension in left
1295
unless (undistinguished-not-context dimension)
1296
when (member dimension right)
1297
collect dimension)))
1300
(defun unit-table ()
1303
(defparameter *page-cache* nil)
1305
(defun clear-page-cache ()
1307
(map-into (make-array 16 :adjustable t)
1308
#'(lambda () (make-array 16 :adjustable nil :fill-pointer 0 :initial-element nil)))))
1311
(defparameter *page-cache-count-maximum* 16)
1312
(defparameter *page-cache-lock* (bt:make-lock "page cache"))
1314
(defun get-page-cache (width)
1315
(when (< (length *page-cache*) width)
1316
(setq *page-cache* (adjust-array *page-cache* width))
1317
(map-into *page-cache*
1321
(make-array 16 :adjustable nil :fill-pointer 0 :initial-element nil)))
1323
(aref *page-cache* (1- width)))
1326
(defun make-page (length width)
1327
(cond ((and *page-cache* (= length *field-page-length*))
1330
(make-array (list length width) :element-type 'fixnum :initial-element +NULL-TERM-ID+))
1333
(let ((length (* (array-dimension page 0) (array-dimension page 1))))
1335
(setf (row-major-aref page i) +NULL-TERM-ID+)))
1339
(fill (sb-impl::%array-data-vector page) +NULL-TERM-ID+)
1342
(bt:with-lock-held (*page-cache-lock*)
1343
(let ((cache (get-page-cache width)))
1344
(if (plusp (fill-pointer cache))
1345
(clear-page (vector-pop cache))
1350
(make-array (list length width) :element-type 'fixnum))))
1352
(defun release-page (page)
1353
(assert-argument-types release-page (page array))
1354
(when (and *page-cache* (= (array-dimension page 0) *field-page-length*))
1355
(bt:with-lock-held (*page-cache-lock*)
1356
(let ((cache (get-page-cache (array-dimension page 1))))
1357
(assert-argument-types release-page (cache vector))
1358
(when (< (fill-pointer cache) (array-dimension cache 0))
1359
(vector-push page cache))))))
1361
(defun adjust-page (page dimensions)
1362
(adjust-array page dimensions))
1365
(defun copy-page (page &optional (new-page (apply #'make-page (array-dimensions page))))
1366
(let* ((dimensions (array-dimensions page))
1367
(size (apply #'* dimensions))
1368
(source (make-array size :displaced-to page :element-type 'fixnum))
1369
(destination (make-array size :displaced-to new-page :element-type 'fixnum)))
1370
(replace destination source)
1373
(defun copy-page (page &optional (new-page (apply #'make-page (array-dimensions page)) np-s))
1374
(when np-s (assert (equal (array-dimensions new-page) (array-dimensions page)) ()
1375
"page dimensions not identical: ~s != ~s." (array-dimensions new-page) (array-dimensions page)))
1376
(let* ((source (sb-impl::%array-data-vector page))
1377
(destination (sb-impl::%array-data-vector new-page)))
1378
(replace destination source)
1382
(defun clear-page (page)
1383
(assert (arrayp page) () "clear-page: invalid page data: ~a" page)
1384
(let ((length (* (array-dimension page 0) (array-dimension page 1))))
1386
(setf (row-major-aref page i) +NULL-TERM-ID+)))
1389
(defun clear-page (page)
1390
(assert (arrayp page) () "clear-page: invalid page data: ~a" page)
1391
(fill (sb-impl::%array-data-vector page) +NULL-TERM-ID+)
1394
;; (defun make-pages (count) (dotimes (x count) (release-page (make-page *field-page-length* 4))))
1395
;; (defun make-arrays (count) (dotimes (x count) (make-array (list *field-page-length* 4))))
1397
(defmacro do-pages ((page source) &rest body)
1398
(let ((op (gensym "do-pages")))
1399
`(flet ((,op (,page)
1400
(declare (ignorable ,page))
1401
(block nil ,@body)))
1402
(declare (dynamic-extent #',op))
1403
(map-pages #',op ,source))))
1406
(setf (ccl:assq 'do-pages ccl:*fred-special-indent-alist*) 1)
1409
(defgeneric map-pages (function source)
1410
(:documentation "Apply the given FUNCTION to each of the pages from the given SOURCE
1411
with appropriate steps to release once no linger in use.")
1412
(:method (op (source function))
1413
(loop for page = (funcall source)
1415
do (funcall op page))))
1418
(declaim (ftype (function (fixnum fixnum) (simple-array fixnum (* *))) make-page))
1421
(defmacro do-solution-field (solution-variables solution-field &body body)
1422
"nb. the operator is used both for arbitrary solution fields and triple/quad
1423
fields. In the former case, there is no concern for order, but triples/quads
1424
should maintain sexp-statement order as there is no otehr indication."
1426
(let ((step-op (gensym "do-solution-field-step"))
1427
(access-op (gensym "do-solution-field-reader")))
1428
`(flet ((,step-op ,solution-variables
1429
(declare (fixnum ,@solution-variables))
1431
(,access-op (step-op page solution-index)
1432
(declare (type (simple-array fixnum (* ,(length solution-variables))) page)
1434
(optimize (speed 3) (safety 0)))
1435
(funcall step-op ,@(loop for i from 0 below (length solution-variables)
1436
collect `(aref page solution-index ,i)))))
1437
(declare (dynamic-extent #',step-op #',access-op))
1438
(map-solution-field #',step-op #',access-op ,solution-field))))
1440
(setf (ccl:assq 'do-solution-field ccl:*fred-special-indent-alist*) 2)
1443
(defgeneric map-solution-field (step-function reader-function solution-field)
1444
(:method (step-function reader-function (solution-field vector))
1445
(loop for page across solution-field
1446
do (map-solution-field step-function reader-function page)))
1447
(:method ((step-function t) (access-function function) (solution-page array))
1448
(dotimes (solution-index (array-dimension solution-page 0))
1449
(funcall access-function step-function solution-page solution-index)))
1450
(:method (step-function access-function (field solution-field))
1451
(map-solution-field step-function access-function (solution-field-solutions field))))
1454
(defmacro do-solution-field-inline (solution-variables solution-field &body body &environment env)
1455
(let* ((field-var (if (eq solution-field (macroexpand-1 solution-field env))
1456
solution-field (gensym "field-")))
1457
(field-index (gensym))
1459
(declare (type (simple-array fixnum (* ,(length solution-variables))) ,field-var))
1460
(loop for ,field-index from 0 below (array-dimension ,field-var 0)
1461
do (progn ,@body)))))
1462
`(macrolet ,(loop for variable in solution-variables
1463
for variable-index from 0
1464
collect `(,variable (aref ,field-var ,field-index ,variable-index)))
1465
,(if (eq field-var solution-field)
1467
`(let `((,field-var ,solution-field)) ,body)))))
1470
(defmacro do-solution-field-inline (solution-variables solution-field &body body)
1471
`(let ((.field. ,solution-field))
1472
(dotimes (i (length .field.))
1473
(let ((.page. (aref .field. i)))
1474
(declare (type (simple-array fixnum (* ,(length solution-variables))) .page.)
1475
(optimize (speed 3) (safety 0)))
1476
;; allow short pages
1477
(dotimes (.solution-index. (array-dimension .page. 0))
1478
(let ,(loop for i from 0 below (length solution-variables)
1479
for var in solution-variables
1480
collect `(,var (aref .page. .solution-index. ,i)))
1484
(defmacro field-object-aref (field i j &optional name)
1485
"Return a reference to a field array as a lisp object. The mapping delegates to rdfcache's
1486
construction/caching mechanism except in the case of a null term, which is recognized here mapped to
1487
an unbound variable instance. NB. the instance must be returned as that acts as a quasi-first-class
1488
value for sort predicates."
1491
(if (= id +null-term-id+)
1492
,(spocq:make-unbound-variable name)
1493
(term-number-object id)))
1494
(aref ,field ,i ,j)))
1496
(defmacro field-vector-object-aref (field-vector j &optional name)
1497
"Return a reference to a field array as a lisp object. The mapping delegates to rdfcache's
1498
construction/caching mechanism except in the case of a null term, which is recognized here mapped to
1499
an unbound variable instance. NB. the instance must be returned as that acts as a quasi-first-class
1500
value for sort predicates."
1503
(if (= id +null-term-id+)
1504
,(spocq:make-unbound-variable name)
1505
(term-number-object id)))
1506
(aref ,field-vector ,j)))
1509
(define-setf-expander field-object-aref (field i j &optional name &environment env)
1510
(declare (ignore name))
1511
(multiple-value-bind (dummies vals newval setter getter)
1512
(get-setf-expansion field env)
1513
(declare (ignore newval setter))
1514
(let ((store (gensym)))
1518
`(setf (aref ,getter ,i ,j) (object-term-number ,store))
1519
`(aref ,getter ,i ,j)))))
1520
;;; (macroexpand-1 '(field-object-aref page index offset))
1521
;;; (macroexpand-1 '(field-object-aref page index offset var))
1522
;;; (macroexpand-1 '(setf (field-object-aref page index offset var) 2))
1524
(defmacro field-object-or-unbound-aref (field i j &optional name)
1525
;; allow an unbound variable instance
1526
;; for sbcl works only read-only - otherwise it objects to possible of writing it back
1527
`(or (term-number-object (aref ,field ,i ,j))
1528
(load-time-value (spocq:make-unbound-variable ',name))))
1530
(defmacro field-aref (field i j)
1531
"Provide a retargetable interface to matrix elements."
1532
`(aref ,field ,i ,j))
1534
(defmacro field-vector-aref (field-vector j)
1535
"Provide a retargetable interface to vector elements."
1536
`(aref ,field-vector ,j))
1538
(defsetf field-aref (field i j) (value)
1539
`(setf (aref ,field ,i ,j) ,value))
1541
(defsetf field-vector-aref (field-vector j) (value)
1542
`(setf (aref ,field-vector ,j) ,value))
1544
(defun field-object-aref-p (expression)
1545
(and (consp expression)
1546
(eq (first expression) 'field-object-aref)))
1548
(defun field-vector-object-aref-p (expression)
1549
(and (consp expression)
1550
(eq (first expression) 'field-vector-object-aref)))
1552
(defun field-object-aref-aref (expression)
1553
"Return just the aref proper from a field-object-aref expression"
1554
(subseq expression 1 4))
1556
(defun field-vector-object-aref-aref (expression)
1557
"Return just the aref proper from a field-vector-object-aref expression"
1558
(subseq expression 1 3))
1560
(defun default-context-p (term)
1561
(equal *default-context-identifier* term))
1562
(defun default-context-term-number-p (term-number)
1563
(or (eql term-number rdfcache:*default-context-number*)
1564
(eql term-number #xffffffff)))
1565
(declaim (inline default-context-term-number-p))
1567
(defun abstract-graph-term-p (term)
1569
((|urn:dydra|:|named| |urn:dydra|:|all| |urn:dydra|:|default|)
1574
(defun wildcard-term-p (term)
1575
(equal *wildcard-identifier* term))
1577
;;; note, the difference signatures require distint predicates
1578
(defun term-number-object-p (expression)
1579
(and (consp expression)
1580
(eq (first expression) 'term-number-object)))
1582
(defun statement-properties (statement)
1583
(member-if #'keywordp statement))
1585
(defun (setf statement-properties) (properties statement)
1586
(or (loop for rest on statement
1587
when (keywordp (first (rest rest)))
1588
do (progn (setf (rest rest) properties)
1589
(return properties)))
1590
(setf (rest (last statement)) properties)))
1592
(defun statement-count (statement)
1593
(getf (statement-properties statement) :count))
1595
(defun (setf statement-count) (count statement)
1597
(setf (getf (statement-properties statement) :count) count)
1598
(remf (statement-properties statement) :count))
1601
(defun statement-dimensions (statement)
1602
(or (getf (statement-properties statement) :dimensions)
1603
(setf (getf (statement-properties statement) :dimensions)
1604
(loop for term in (statement-terms statement)
1605
collect (if (variable-p term)
1607
(gensym "constant"))))))
1609
;;; no setter: it is generated on-demand
1610
;;;(defun (setf statement-dimensions) (dimensions statement)
1611
;;; (setf (getf (statement-properties statement) :dimensions) dimensions))
1613
(defun statement-terms (statement)
1614
(labels ((butlast-keyword (list)
1615
(loop for element in list
1616
until (keywordp element)
1618
(case (first statement)
1619
((spocq.a:|triple| :triple spocq.a:|quad| :quad)
1620
(butlast-keyword (rest statement)))
1621
(t (butlast-keyword statement)))))
1622
;;; (mapcar #'statement-terms '((:quad s p o) (:quad s p 3 :equivalents ((o 3)))))
1624
(defun statement-variables (statement)
1625
(remove-if-not #'variable-p (statement-terms statement)))
1626
(defun statement-constants (statement)
1627
(remove-if-not #'spocq.e:constantp (statement-terms statement)))
1628
(defun statement-blank-nodes (statement)
1629
(remove-if-not #'spocq:blank-node-p (statement-terms statement)))
1631
(defun statement-subject (statement)
1632
(case (first statement)
1633
((spocq.a:|triple| :triple spocq.a:|quad| :quad) (second statement))
1634
(t (case (length statement)
1635
;; 3 -> s.p.o, 4 -> c.s.p.o
1636
(3 (first statement))
1637
(4 (second statement))))))
1639
(defun statement-predicate (statement)
1640
(case (first statement)
1641
((spocq.a:|triple| :triple spocq.a:|quad| :quad) (third statement))
1642
(t (case (length statement)
1643
;; 3 -> s.p.o, 4 -> c.s.p.o
1644
(3 (second statement))
1645
(4 (third statement))))))
1647
(defun statement-object (statement)
1648
(case (first statement)
1649
((spocq.a:|triple| :triple spocq.a:|quad| :quad) (fourth statement))
1650
(t (case (length statement)
1651
;; 3 -> s.p.o, 4 -> c.s.p.o
1652
(3 (third statement))
1653
(4 (fourth statement))))))
1655
(defun statement-context (statement)
1656
(case (first statement)
1657
((spocq.a:|triple| :triple) nil)
1658
((spocq.a:|quad| :quad) (fifth statement))
1659
(t (case (length statement)
1660
;; 3 -> s.p.o, 4 -> c.s.p.o
1662
(4 (first statement))))))
1664
(defun triple-terms (statement)
1665
(labels ((butlast-keyword (list)
1666
(loop for element in list
1667
until (keywordp element)
1669
(case (first statement)
1670
((spocq.a:|triple| :triple) (butlast-keyword (rest statement)))
1671
(t (butlast-keyword statement)))))
1673
(defun quad-terms (statement)
1674
(statement-terms statement))
1677
(defun statement-options (statement)
1678
(member-if #'keywordp (rest statement)))
1680
(defmacro solution-value (form)
1681
"Iff the form is an expression, wrap evaluation in a handler which suppresses errors
1682
and replaces them with an unbound variable. If the form is atomic, just evaluate it."
1685
`(handler-case ,form
1687
(declare (ignore c))
1688
(load-time-value (spocq:make-unbound-variable ',form)))
1689
(:no-error (value &rest values) (declare (ignore values))
1691
((t) spocq.a:|true|)
1692
((nil) spocq.a:|false|)
1696
(defun find-statement (statements &key subject predicate object context)
1697
(loop for statement in statements
1698
when (and (or (null subject) (equal (statement-subject statement) subject))
1699
(or (null predicate) (equal (statement-predicate statement) predicate))
1700
(or (null object) (equal (statement-object statement) object))
1701
(or (null context) (equal (statement-context statement) context)))
1702
do (return statement)))
1704
(defun statement-property-list (statements &key subject predicate object context)
1705
(loop for statement in statements
1706
when (and (or (null subject) (funcall subject (statement-subject statement)))
1707
(or (null predicate) (funcall predicate (statement-predicate statement)))
1708
(or (null object) (funcall object (statement-object statement)))
1709
(or (null context) (funcall context (statement-context statement))))
1710
collect (statement-predicate statement)
1711
collect (statement-object statement)))
1714
;; (loop for i from 0 below 8 sum (* i *field-page-length* 4))
1716
(defun test-field-array (&optional (count 100))
1717
(dotimes (pass count)
1718
(let ((field (make-array 8))
1721
(setf (aref field i) (make-array (list *field-page-length* 4) :initial-element i :element-type 'fixnum)))
1722
(do-solution-field (a b c d) field
1728
;; (time (test-field-array)) : 0.141 seconds / 13,246,000 bytes consed
1730
(defun test-field-array-inline (&optional (count 100))
1731
(dotimes (pass count)
1732
(let ((field (make-array 8))
1735
(setf (aref field i) (make-array (list *field-page-length* 4) :initial-element i :element-type 'fixnum)))
1736
(do-solution-field-inline (a b c d) field
1742
;; (time (test-field-array-inline)) : 0.167 seconds of real time
1743
;; no real advantage over out-of-line
1745
(defun test-field-list (&optional (count 100))
1746
(dotimes (pass count)
1747
(let ((field (loop for i from 0 below 8
1748
collect (loop for j from 0 below *field-page-length*
1749
collect (loop for var in '(a b c d)
1750
nconc `(,var ,i)))))
1752
(flet ((op (&key ((a a) 0) ((b b) 0) ((c c) 0) ((d d) 0))
1757
(declare (dynamic-extent #'op))
1758
(dolist (page field)
1759
(dolist (solution page)
1760
(apply #'op solution))))
1762
;; (time (test-field-list)) : 0.487 seconds of real time / 65,573,504 bytes consed
1769
(defun make-digest (string type)
1770
(let* ((length (length string))
1771
(buffer-length (* length 4))
1772
(buffer (make-array buffer-length :element-type '(unsigned-byte 8)))
1774
(hex-characters "0123456789abcdef")
1775
(encoder (load-time-value (content-encoding-byte-encoder (content-encoding :utf-8)))))
1776
(flet ((buffer-insert-byte (buffer byte)
1777
(declare (type (array (unsigned-byte 8) (*)) buffer))
1778
(declare (type (unsigned-byte 8) byte))
1779
;; check bounds here as it's finally the encoded positioning
1780
(assert (< count buffer-length) () "redis key overrun.")
1781
(setf (aref buffer count) byte)
1783
(declare (dynamic-extent #'buffer-insert-byte)) ; just in case
1785
(funcall encoder (char string i) #'buffer-insert-byte buffer))
1786
(let* ((digest (ironclad:digest-sequence type buffer :end count))
1787
(hex-digest (make-array (* (length digest) 2) :element-type 'character)))
1788
(loop for digest-byte across digest
1789
for hex-index from 0 below (length hex-digest) by 2
1790
do (setf (char hex-digest hex-index) (aref hex-characters (ash digest-byte -4))
1791
(char hex-digest (1+ hex-index)) (aref hex-characters (logand digest-byte #x0f))))
1792
(values hex-digest digest)))))
1794
(defun make-md5-digest (string) (make-digest string 'ironclad:md5))
1795
(defun make-sha1-digest (string) (make-digest string 'ironclad:sha1))
1796
(defun make-sha224-digest (string) (make-digest string 'ironclad:sha224))
1797
(defgeneric make-sha256-digest (object)
1798
(:documentation "return a base-16 hex string encoding of the sha256 hash value of the
1799
given object. given a pathname, compute the hash from the respective file.
1800
when used to construct a urn, this encoding differs from https://tools.ietf.org/html/draft-thiemann-hash-urn-01.")
1801
(:method ((string string)) (make-digest string 'ironclad:sha256))
1802
(:method ((pathname pathname))
1803
(let ((process (run-program *executable-pathname.shasum*
1804
`("-a" "256" ,(namestring (truename pathname)))
1807
))) ;; isolate rapper from dydra libraries
1808
(unless (and process (zerop (run-program-exit-code process)))
1809
(error "make-sha256-digest failed: ~s" (run-program-exit-code process)))
1810
(unwind-protect (let* ((hash (read-line (run-program-output process)))
1811
(end (position-if-not #'alphanumericp hash)))
1812
(subseq hash 0 end))
1813
(run-program-close process)))))
1814
(defun make-sha384-digest (string) (make-digest string 'ironclad:sha384))
1815
(defun make-sha512-digest (string) (make-digest string 'ironclad:sha512))
1816
;;; (mapcar #'(lambda (f) (funcall f "")) '(make-md5-digest make-sha1-digest make-sha224-digest))
1817
;;; (mapcar #'(lambda (f) (funcall f "")) '(make-sha256-digest make-sha384-digest make-sha512-digest))
1819
(defgeneric iri-sha1 (iri)
1820
(:documentation "Given an iri string or object, compute the sha1 for its
1821
ntriples encoding.")
1822
(:method ((iri string))
1823
(make-sha1-digest (concatenate 'string "<" iri ">")))
1824
(:method ((iri spocq:iri))
1825
(iri-sha1 (spocq:iri-lexical-form iri)))
1826
(:method ((iri symbol))
1827
(iri-sha1 (iri-lexical-form iri)))
1828
(:method ((uri puri:uri))
1829
(iri-sha1 (with-output-to-string (stream) (puri:render-uri uri stream)))))
1832
;;; os interface operators
1837
(defun log-level-qualifies? (level)
1838
(find level (member *log-level* *log-levels*)))
1842
(defun posix-log-level (level)
1843
(or (rest (assoc level (load-time-value `((:trace . ,sb-posix:log-debug)
1844
(:debug . ,sb-posix:log-debug)
1845
(:info . ,sb-posix:log-info)
1846
(:notice . ,sb-posix:log-notice)
1847
(:warn . ,sb-posix:log-warning)
1848
(:error . ,sb-posix:log-err)
1849
(:critical . ,sb-posix:log-crit)
1850
(:fatal . ,sb-posix:log-emerg)))))
1852
(defun posix-option-flag (key)
1853
(or (rest (assoc key (load-time-value `((:cons . ,sb-posix:log-cons)
1854
(:ndelay . ,sb-posix:log-ndelay)
1855
(:nowait . ,sb-posix:log-nowait)
1856
(:odelay . ,sb-posix:log-odelay)
1857
(:perror . ,sb-posix:log-perror)
1858
(:pid . ,sb-posix:log-pid)))))
1860
(defun posix-log-facility (facility)
1861
(or (rest (assoc facility (load-time-value `((:authpriv . ,sb-posix:log-authpriv)
1862
(:cron . ,sb-posix:log-cron)
1863
(:daemon . ,sb-posix:log-daemon)
1864
(:ftp . ,sb-posix:log-ftp)
1865
(:lpr . ,sb-posix:log-lpr)
1866
(:mail . ,sb-posix:log-mail)
1867
(:news . ,sb-posix:log-news)
1868
(:syslog . ,sb-posix:log-syslog)
1869
(:user . ,sb-posix:log-user)
1870
(:uucp . ,sb-posix:log-uucp)))))
1875
(defun posix-log-level (level)
1876
(or (rest (assoc level (load-time-value `((:trace . 7)
1885
(defun posix-option-flag (key)
1886
(or (rest (assoc key (load-time-value `((:cons . 2)
1893
(defun posix-log-facility (facility)
1894
(or (rest (assoc facility (load-time-value `((:authpriv . 10)
1907
(defun posix-log-level (level)
1908
(or (rest (assoc level (load-time-value `((:trace . ,bsd::log-debug)
1909
(:debug . ,bsd::log-debug)
1910
(:info . ,bsd::log-info)
1911
(:notice . ,bsd::log-notice)
1912
(:warn . ,bsd::log-warning)
1913
(:error . ,bsd::log-err)
1914
(:critical . ,bsd::log-crit)
1915
(:fatal . ,bsd::log-emerg)))))
1917
(defun posix-option-flag (key)
1918
(or (rest (assoc key (load-time-value `((:cons . ,bsd::log-cons)
1919
(:ndelay . ,bsd::log-ndelay)
1920
(:nowait . ,bsd::log-nowait)
1921
(:odelay . ,bsd::log-odelay)
1922
(:perror . ,bsd::log-perror)
1923
(:pid . ,bsd::log-pid)))))
1925
(defun posix-log-facility (facility)
1926
(or (rest (assoc facility (load-time-value `((:authpriv . ,bsd::log-authpriv)
1927
(:chron . ,bsd::log-chron)
1928
(:daemon . ,bsd::log-daemon)
1929
(:ftp . ,bsd::log-ftp)
1930
(:lpr . ,bsd::log-lpr)
1931
(:mail . ,bsd::log-mail)
1932
(:news . ,bsd::log-news)
1933
(:syslog . ,bsd::log-syslog)
1934
(:user . ,bsd::log-user)
1935
(:uucp . ,bsd::log-uucp)))))
1939
(defmacro without-interrupts (&body body)
1940
`(sb-sys:without-interrupts ,@body))
1943
(defmacro without-interrupts (&body)
1944
(cerror "continue to compile" "no definition present for without-interrupts")
1949
;;; define an open-log version which ensure that the
1950
;;; identity string is projected into the c heap.
1952
(cffi:defcfun ("openlog" %openlog) :void (id :string) (options :int) (facility :int))
1954
(defun open-log (&key (identity *service-name*) (options nil) (facility :log-daemon) (title nil))
1955
(when (stringp identity)
1956
(setf identity (cffi:foreign-string-alloc identity)))
1957
(when (listp options)
1958
(setf options (reduce #'+ options :key #'posix-option-flag :initial-value 0)))
1959
(when (symbolp facility)
1960
(setf facility (posix-log-facility facility)))
1963
(let* ((title-pointer (sb-alien:deref sb-sys::*native-posix-argv* 0))
1964
(old-title (sb-alien:cast title-pointer sb-alien:c-string))
1965
(title-length (length old-title)))
1966
(dotimes (i (min title-length (length title)))
1967
(setf (sb-alien:deref title-pointer i) (char-code (char title i))))))
1968
(%openlog identity options facility))
1970
(cffi:defcfun ("syslog" %syslog) :void (priority :int) (c-format :string) (c-message :string))
1972
(defun write-log (level format-control &rest args)
1973
(handler-case (let* ((*print-pretty* nil)
1975
(message (format nil "[~a] ~?" *thread-name* format-control args)))
1977
(cffi:with-foreign-strings ((%cformat "%s")
1979
(%syslog (posix-log-level level) %cformat %message)))
1980
;; write anything above :notice to the terminal, if present
1981
(when (and (not (find :notice (member level *log-levels*)))
1983
(interactive-stream-p *trace-output*))
1984
(format *trace-output* "~&;;;~a~%" message)))
1986
(setq *log-condition* error)
1990
(defun call-if-log-level-p (level operator)
1991
"Given a log LEVEL and an OPERATOR, call the operator iff the log level is satisfied."
1992
(declare (dynamic-extent operator))
1993
(when (log-level-qualifies? level)
1994
(funcall operator)))
1997
(macrolet ((def-log-op (level)
1999
(defun ,(cons-symbol :spocq.i :write-log- level) (format-control &rest args)
2000
(when (log-level-qualifies? ,level)
2001
(apply #'write-log ,level format-control args)))
2002
(defmacro ,(cons-symbol :spocq.i :log- level) (format-control &rest args)
2003
`(flet ((call-write-log ()
2004
(write-log ,,level ,format-control ,@args)
2006
(declare (dynamic-extent (function call-write-log)))
2007
(call-if-log-level-p ,,level (function call-write-log))))
2008
(defun ,(cons-symbol :spocq.i :log- level "!") (format-control &rest args)
2009
(apply #'write-log ,level format-control args)))))
2011
(def-log-op :critical)
2014
(def-log-op :notice)
2016
(def-log-op :debug))
2018
(defmacro log-trace (format-control &rest args)
2019
`(flet ((call-write-log ()
2020
(write-log :trace ,format-control ,@args)
2021
(terpri *trace-output*)
2022
(format *trace-output* ,format-control ,@args)
2024
(declare (dynamic-extent #'call-write-log))
2025
(call-if-log-level-p :trace #'call-write-log)))
2026
(defun log-trace! (format-control &rest args)
2027
(apply #'write-log :trace format-control args))
2030
(defun log-stacktrace (format-control &rest args)
2031
(flet ((call-write-log ()
2032
(apply #'write-log :error format-control args)
2034
(bt:with-recursive-lock-held (*trace-lock*)
2035
(with-open-file (stream *log-pathname* :direction :output :if-exists :append :if-does-not-exist :create
2036
#+(or sbcl lispworks) :external-format #+sbcl :utf8 #+lispworks :utf-8)
2037
(format stream "~&~%[~/date:format-iso-time/ ~s]~% ~?"
2038
(get-universal-time) (getpid) format-control args)
2039
(print-stacktrace stream)))
2041
(log-error "failed to write tracelog entry: ~a" error)
2042
(setq *log-condition* error)
2046
(defun write-tracelog (format-control &rest args)
2048
(with-open-file (stream *log-pathname* :direction :output :if-exists :append :if-does-not-exist :create
2049
#+(or sbcl lispworks) :external-format #+sbcl :utf8 #+lispworks :utf-8)
2050
(format stream "~&~%[~/date:format-iso-time/ ~s]~% ~?"
2051
(get-universal-time) (getpid) format-control args))
2053
(log-error "failed to write tracelog entry: ~a" error)
2054
(setq *log-condition* error)
2057
(defgeneric log-metadata (message object)
2058
(:method (message (object t))
2059
(log-debug "~a ~a" message object)))
2062
(defun print-stacktrace (stream &key (start 0) (count) (verbosity 1))
2067
(defun print-stacktrace (stream &key (start 0) (count) (verbosity 1))
2068
(let ((*standard-output* stream))
2069
(mp:print-process-stack-frames mp:*current-process*)))
2073
(defun print-stacktrace (stream &key (start 0) (count) (verbosity 1))
2074
(declare (ignore count))
2075
(let ((*debug-io* stream))
2076
(ccl::print-call-history :stack-group ccl::*current-stack-group* :start-frame start :detailed-p (plusp verbosity))))
2079
(defun print-stacktrace (stream &key (start 0) (count most-positive-fixnum) (verbosity 1)
2080
((:print-array *print-array*) nil)
2081
((:print-pretty *print-pretty*) nil))
2082
(declare (ignore verbosity))
2084
(sb-debug::map-backtrace (lambda (frame)
2085
(sb-debug::print-frame-call frame stream ;:verbosity verbosity
2089
(fresh-line stream))
2091
;;; (sb-thread:interrupt-thread (first (sb-thread:list-all-threads)) #'(lambda () (print-stacktrace *trace-output*)))
2096
(defmacro barrier ((kind) &body body)
2097
`(sb-thread:barrier (,kind) ,@body))
2100
(defmacro barrier ((kind) &body body)
2101
(declare (ignore kind))
2104
;;; memory monitoring
2106
(defun log-memory-usage ()
2107
;; always log it and leave to syslog to filter
2109
(multiple-value-bind (usedbytes static-used staticlib-used) (ccl::%usedbytes)
2110
(write-log :notice "MEMORY: dynamic ~d, static ~d, lib ~d; QUERIES: ~d"
2117
(defun log-memory-usage (gen)
2118
;; always log it and leave to syslog to filter
2119
(write-log :notice "MEMORY: ~d/~a; QUERIES: ~d"
2120
gen (system:room-values) (query-count)))
2123
(defun log-memory-usage ()
2124
(let* ((dynamic (sb-kernel::dynamic-usage))
2125
(level (cond ((<= dynamic 2000000000) :info)
2126
((<= dynamic 4000000000) :notice)
2128
;; 7/8 or 1/2 the full heap size
2129
(limit (floor (* 7 (/ (/ (SB-EXT:DYNAMIC-SPACE-SIZE) 2) 8)))))
2130
(when (log-level-qualifies? level)
2131
(write-log level "MEMORY: dynamic ~d, static ~d, stack ~d; QUERIES: ~d"
2133
(sb-kernel::static-space-usage)
2134
(sb-kernel::control-stack-usage)
2136
(when (> dynamic limit)
2137
(bt:make-thread #'(lambda ()
2138
(write-log :warn "initiate full GC @~d" limit)
2139
(sb-ext:gc :full t))))))
2141
(defun trace-when (stream format &rest args)
2142
(flet ((do-format ()
2143
(let ((*print-pretty* nil))
2144
(prog1 (etypecase format
2145
((or function symbol) (funcall format stream))
2146
(string (apply #'format stream format args)))
2147
(finish-output stream)))))
2149
(if (eq (sb-thread:mutex-owner *trace-lock*) (bt:current-thread))
2151
(bt:with-recursive-lock-held (*trace-lock*) (do-format))))))
2153
(defmacro trace-always (operator &rest values)
2154
(let ((op (gensym "trace-")))
2156
(format ,op "~&~a[~a] : ~@{~a~^ ~}~%"
2157
',operator (bt:thread-name (bt:current-thread)) ,@values)))
2158
(declare (dynamic-extent #',op))
2159
(trace-when *trace-output* #',op))))
2161
(defvar *algebra-trace-output* nil)
2162
(defmacro trace-algebra (operator &rest values)
2163
(let ((op (gensym "trace-")))
2165
(format ,op "~&~a[~a] : "',operator (bt:thread-name (bt:current-thread)))
2166
(format ,op ,(if (and (stringp (first values)) (find #\~ (first values))) (pop values) "~@{~a~^ ~}~%")
2169
(declare (dynamic-extent #',op))
2170
(trace-when *algebra-trace-output* #',op))))
2172
(defvar *bgp-trace-output* nil)
2173
(defmacro trace-bgp (operator &rest values)
2174
(let ((op (gensym "trace-")))
2176
(format ,op "~&bgp: ~a[~a] : ~@{~a~^ ~}~%"
2177
',operator (bt:thread-name (bt:current-thread)) ,@values)))
2178
(declare (dynamic-extent #',op))
2179
(trace-when *bgp-trace-output* #',op))))
2180
(defmacro trace-bgp-operator (operator &rest values)
2181
(let ((op (gensym "trace-")))
2183
(format ,op "~&bgp: ~a[~a] : ~@{~a~^ ~}~%"
2184
',operator (bt:thread-name (bt:current-thread)) ,@values)))
2185
(declare (dynamic-extent #',op))
2186
(trace-when *bgp-trace-output* #',op))))
2189
(defvar *data-trace-output* nil)
2190
(defmacro trace-data (operator &rest values)
2191
(let ((op (gensym "trace-")))
2193
(format ,op "~&~a[~a] : ~@{~a~^ ~}~%"
2194
',operator (bt:thread-name (bt:current-thread)) ,@values)))
2195
(declare (dynamic-extent #',op))
2196
(trace-when *data-trace-output* #',op))))
2198
(defvar *thread-trace-output* nil)
2199
(defmacro trace-threads (operator &rest values)
2201
((cons (eql function))
2202
`(trace-when *thread-trace-output* ,operator))
2203
((cons (eql lambda))
2204
`(trace-when *thread-trace-output* (function ,operator)))
2206
(let ((op (gensym "trace-")))
2208
(format ,op "~&~a[~a] : ~@{~a~^ ~}~%"
2209
',operator (bt:thread-name (bt:current-thread)) ,@values)))
2210
(declare (dynamic-extent #',op))
2211
(trace-when *thread-trace-output* #',op))))))
2213
(defun trace-transaction (context %record &optional (read-only-p nil))
2214
(when *transaction-trace-output*
2215
(trace-when *transaction-trace-output*
2216
"~&~s~@[ ( ~s )~]" context read-only-p)
2217
(rdfcache:print-transaction %record *transaction-trace-output*)
2218
(terpri *transaction-trace-output*)))
2220
(defvar *encoding-trace-output* nil)
2223
;;; external programs
2225
(:documentation "external programs"
2226
"execute external processes given the full program pathname and the arguments.
2227
permit to supply an input stream and/or capture the output stream.")
2229
(defparameter *run-program-verbose* t)
2231
(defun run-program (executable command-arguments &rest process-arguments
2232
&key wait (environment nil e-p)
2234
(setf executable (namestring executable))
2235
(log-notice "run-program: ~s . ~s~@[ . ~s~]"
2236
executable command-arguments process-arguments)
2238
(let ((process (apply #'sb-ext:run-program executable command-arguments
2239
(append (when e-p `(:environment ,environment))
2240
process-arguments))))
2242
(cond ((zerop (sb-ext:process-exit-code process))
2245
(when *run-program-verbose*
2246
(log-warn "run-program: abnormal exit: ~s: ~s . ~s"
2247
(sb-ext:process-exit-code process)
2248
executable command-arguments))
2249
(sb-ext:process-close process)
2250
;; return process in any case to afford access to the code
2255
(defun run-program-close (process)
2256
(sb-ext:process-wait process)
2257
(sb-ext:process-close process)
2258
;; read that to release the zombie
2259
(sb-ext:process-exit-code process))
2261
(defun run-program-error (process)
2263
(sb-ext:process-error process))
2265
(defun run-program-exit-code (process)
2267
(sb-ext:process-exit-code process))
2269
(defun run-program-input (process)
2271
(sb-ext:process-input process))
2273
(defun run-program-output (process)
2275
(sb-ext:process-output process))
2277
(defun run-program-wait (process)
2279
(sb-ext:process-wait process))
2281
(defmacro with-open-program ((stream executable command-arguments &rest process-arguments) &body body)
2282
`(call-with-open-program #'(lambda (,stream) ,@body)
2283
,executable ,command-arguments
2284
,@process-arguments))
2286
(defun call-with-open-program (function executable command-arguments &rest process-arguments)
2287
(let ((process (apply #'run-program executable command-arguments
2290
process-arguments)))
2293
(case (run-program-exit-code process)
2295
(funcall function (run-program-output process)))
2296
(t (error "run-program failed: ~s: ~s"
2297
(run-program-exit-code process)
2298
(cons executable command-arguments))))
2299
(close (run-program-output process))
2300
(run-program-close process)))
2302
(error "run-program failed: ~s: ~s"
2303
(run-program-exit-code process)
2304
(cons executable command-arguments))))))
2307
(defun read-http-headers (stream &rest headers)
2308
"read the given stream, to extract the given headers up to the
2309
point of a zero-length line."
2310
(declare (dynamic-extent headers))
2311
(let ((whitespace #(#\space #\tab #\return #\linefeed)))
2312
(labels ((collector (collection)
2313
(let ((line (read-line stream nil nil)))
2314
(if (and (plusp (length line))
2315
(not (find (char line 0) whitespace)))
2316
(let* ((colon (position #\: line)))
2317
(if (and colon (= colon 3) (string-equal "via" line :end2 colon))
2318
;; pass over proxy headers
2319
(collector (append collection (apply #'read-http-headers stream headers)))
2320
(loop for header in headers
2322
(= (length (string header)) colon)
2323
(string-equal header line :end2 colon))
2324
return (collector (acons header (string-trim whitespace (subseq line (1+ colon)))
2326
finally (return (collector collection)))))
2327
(reverse collection)))))
2330
;;; these are used in the encoding operators to compare with external the term datatype string
2331
;;; to judge that the term is a string with and explicit type
2332
(defparameter |%http://www.w3.org/2001/XMLSchema#string| nil)
2333
(defun |%http://www.w3.org/2001/XMLSchema#string| ()
2334
(or |%http://www.w3.org/2001/XMLSchema#string|
2335
(setf |%http://www.w3.org/2001/XMLSchema#string| (datatype-foreign-string '|xsd|:|string|))))
2337
(defparameter |%http://www.w3.org/1999/02/22-rdf-syntax-ns#langString| nil)
2338
(defun |%http://www.w3.org/1999/02/22-rdf-syntax-ns#langString| ()
2339
(or |%http://www.w3.org/1999/02/22-rdf-syntax-ns#langString|
2340
(setf |%http://www.w3.org/1999/02/22-rdf-syntax-ns#langString| (datatype-foreign-string '|rdf|:|langString|))))
2342
(defun %string-equal (%s1 %s2)
2343
(and (not (cffi:null-pointer-p %s1))
2344
(not (cffi:null-pointer-p %s2))
2346
for c1 = (cffi:mem-ref %s1 :char i)
2347
for c2 = (cffi:mem-ref %s2 :char i)
2348
do (cond ((zerop c1)
2349
(return (zerop c2)))
2356
(defun string-begins-equal (string initial-string)
2357
(when (and string initial-string
2358
(>= (length string) (length initial-string)))
2359
(string-equal string initial-string :end1 (length initial-string))))
2362
;;; process information
2383
#+(or clozure mcl) (short-site-name)
2384
#+sbcl (sb-unix:unix-gethostname))))
2386
(defun server-host-name ()
2387
(or *server-host-name*
2388
(setq *server-host-name* (host-name))))
2390
(defun server-uri ()
2392
(setq *server-uri* (intern-iri (concatenate 'string *server-protocol* "://" (server-host-name))))))
2396
(setq *site-name* (host-name))))
2398
(defun site-namespace ()
2399
"construct the site namespace uri on demand.
2400
this combines the site name always with the http protocol"
2401
(or *site-namespace*
2402
(setq *site-namespace* (intern-iri (concatenate 'string "http" "://" (site-name))))))
2405
"construct the site location uri on demand.
2406
this combines the site name with the active protocol - http or https as configured."
2408
(setq *site-uri* (intern-iri (concatenate 'string *site-protocol* "://" (site-name))))))
2410
(defun query-exchange ()
2411
(or *query-exchange*
2412
(setq *query-exchange* (format nil "~a.query" *service-name*))))
2414
(defun engine-query-queue-name ()
2415
(or *engine-query-queue*
2416
(setq *query-queue* (format nil "~a.query" *service-name*))))
2418
(defun engine-store-queue-name ()
2419
(or *engine-store-queue*
2420
(setq *engine-store-queue* (format nil "~a.store.~a.~a" *service-name* (host-name) (getpid)))))
2422
(defun engine-store-routing-key ()
2423
(or *engine-store-routing-key*
2424
(setq *engine-store-routing-key*
2425
(format nil "~a.spocq.~a" (host-name) (getpid)))))
2427
(defun store-exchange ()
2428
(or *store-exchange*
2429
(setq *store-exchange* (format nil "~a.store" *service-name*))))
2431
(defun store-store-queue-name ()
2432
(or *store-store-queue*
2433
(setq *store-store-queue* (format nil "~a.store" *service-name*))))
2439
(integer (nth arg (command-line-argument-list)))
2440
((or string keyword)
2441
(let ((arg-entry (member arg (command-line-argument-list) :test #'string-equal)))
2442
(if (and arg-entry (>= (length arg) 2) (char= #\- (char arg 1)))
2443
(let ((value (second arg-entry)))
2444
(if (and value (not (string-equal "--" value :end2 (min 2 (length value)))))
2447
(not (null arg-entry)))))))
2448
(eval-when (:compile-toplevel :load-toplevel :execute)
2449
(defvar *getarg-options* ()))
2450
(define-compiler-macro getarg (&whole form arg)
2452
(pushnew arg *getarg-options* :test #'string-equal))
2455
(defun getargs (name)
2456
(loop with args = (command-line-argument-list)
2457
for arg = (pop args)
2459
when (equal arg name)
2460
collect (pop args)))
2463
(defun command-line-argument-list ()
2464
ccl:*command-line-argument-list*)
2467
(defun command-line-argument-list ()
2471
(defun command-line-argument-list ()
2472
system:*command-line-arguments-list*)
2475
(defun command-line-argument-list ()
2476
sb-ext:*posix-argv*)
2480
(defun get-space-time-usage ()
2481
;;(values (get-internal-run-time) (get-internal-real-time) (ccl::total-bytes-allocated)))
2482
(values (rdfcache:time-in-thread) (get-internal-real-time) (ccl::total-bytes-allocated)))
2484
#+lispworks ; nb. 'utime' => microseconds
2485
(defun get-space-time-usage ()
2486
;;(values (get-internal-run-time) (get-internal-real-time) 0))
2487
(values (rdfcache:time-in-thread) (get-internal-real-time) 0))
2489
#+sbcl ; nb. 'utime' => microseconds
2490
(defun get-space-time-usage ()
2491
;;(values (get-internal-run-time) (get-internal-real-time) (sb-impl::get-bytes-consed)))
2492
(values (rdfcache:time-in-thread) (get-internal-real-time) (sb-impl::get-bytes-consed)))
2494
(defparameter *in.call-with-accounting* nil)
2496
(defun accounting-properties ()
2497
"Return the accounting properties as accumulated since the start of a call-with-accounting
2498
and reset the values. The properties are in the form of a p-list with keys intended for an
2499
accounting note. allocation and time properties are then reset to their current values and the
2500
others are reset to zero."
2502
(multiple-value-bind (run-time real-time bytes-allocated)
2503
(get-space-time-usage)
2504
(let ((delta-bytes (if (< bytes-allocated *bytes-allocated*)
2505
(+ bytes-allocated (- most-positive-fixnum *bytes-allocated*))
2506
(- bytes-allocated *bytes-allocated*))))
2507
(prog1 `(:|algebra_operations| ,*algebra-operations*
2508
:|bytes_allocated| ,delta-bytes
2509
:|bytes_read| ,*bytes-read*
2510
:|bytes_written| ,*bytes-written*
2511
:|match_requests| ,*match-requests*
2512
:|match_responses| ,*match-responses*
2513
:|run_time| ,(- run-time *run-time*)
2514
:|real_time| ,(- real-time *real-time*)
2515
:|solutions_constructed| ,*solutions-constructed*
2516
:|solutions_processed| ,*solutions-processed*
2517
:|solutions_returned| ,*statements-returned*)
2518
(unless *in.call-with-accounting*
2519
(warn "accounting-properties not in call-with-accounting"))
2520
(setq *bytes-allocated* bytes-allocated
2525
*algebra-operations* 0
2527
*real-time* real-time
2528
*solutions-constructed* 0
2529
*solutions-processed* 0
2530
*statements-returned* 0)))))
2532
(defmacro with-accounting (&rest body)
2533
(let ((op (gensym "ACCOUNTING-CONTEXT-")))
2534
`(flet ((,op () ,@body))
2535
(declare (dynamic-extent #',op))
2536
(call-with-accounting #',op))))
2538
(defun call-with-accounting (function)
2539
(declare (dynamic-extent function))
2540
(let ((*algebra-operations* 0)
2543
(*match-requests* 0)
2544
(*match-responses* 0)
2545
(*solutions-constructed* 0)
2546
(*solutions-processed* 0)
2547
(*statements-returned* 0)
2548
(*in.call-with-accounting* t))
2549
(multiple-value-bind (*run-time* *real-time* *bytes-allocated*)
2550
(get-space-time-usage)
2551
(funcall function))))
2553
;;; an indirection for debugging
2554
(defgeneric put-accounting-note (task note)
2555
(:method ((task t) (note t))
2556
(log-stacktrace "invalid task accounting note: ~s . ~s" task note)
2559
(defun generate-accounting-note (state &key (task *query*))
2560
;; a non-generic function to avoid dynamic compilation of an effective method
2561
;; which, in spcl can lead to the error "The value NIL is not of type SB-C::VOP"
2562
"Send a note to an accounting channel which combines the give query with an indicator for
2563
the processing state/phase and a snapshot of the accounting properties since the previous note."
2569
(cond ((typep task 'task)
2570
(update-task-state task state)
2571
(put-accounting-note task
2572
(list* :|task_id| (task-id task)
2574
(accounting-properties))))
2576
(log-stacktrace "invalid task for accounting note: ~s." task))))
2578
(log-stacktrace "invalid state for accounting note: ~s , ~s" state task))))
2581
(defun get-accounting-notes ()
2582
(bt:with-lock-held (*accounting-notes-lock*)
2583
(prog1 (coerce *accounting-notes* 'list)
2584
(fill *accounting-notes* nil)
2585
(setf (fill-pointer *accounting-notes*) 0))))
2587
(defun accounting-note-count ()
2588
(length *accounting-notes*))
2590
;;; thread introspection
2592
(defparameter *algebra-idle-priority* 0)
2593
(defparameter *algebra-running-priority* nil) ; 3)
2595
(defun thread-priority ()
2599
(defun (setf thread-priority) (value)
2601
(when (and *algebra-running-priority* (zerop (sb-unix:unix-getuid)))
2602
(rdfcache:boost value))
2606
(defun thread-locked-p (thread)
2608
#+sbcl (eq thread (sb-thread:mutex-owner sb-c::**world-lock**)))
2610
(defun list-queues ()
2611
(list *accounting-notes*
2612
*error-condition-channel*
2614
*algebra-task-channel*
2616
(defun list-thread-operations (&key (verbose nil))
2618
(loop for thread in (bt:all-threads)
2619
for operations = (if (thread-locked-p thread)
2621
(cons (list (sb-thread:symbol-value-in-thread '*task* thread nil))
2622
(sb-thread:symbol-value-in-thread '*thread-operations* thread nil)))
2626
(cons thread operations)
2627
(cons (bt:thread-name thread) (mapcar #'first operations))))
2631
(defun trace-thread-operations (&key (interval 1) (duration nil) verbose)
2632
(loop with start = (get-universal-time)
2633
with last-operations = ()
2634
for operations = (list-thread-operations :verbose verbose)
2635
while (or (null duration)
2636
(< (get-universal-time) (+ start duration)))
2637
if (equalp last-operations operations)
2638
do (when interval (sleep interval))
2639
else do (progn (when operations
2640
(setf last-operations operations)
2641
(format *trace-output* "~&~:w~%~%" (cons (iso-time) operations))
2642
(finish-output *trace-output*))
2643
(when interval (sleep interval)))))
2645
(defun backtrace-threads (&key name (start 0) (count most-positive-fixnum) (stream *trace-output*))
2646
(loop for th in (bt:all-threads)
2647
when (and (not (eq th (bt:current-thread)))
2649
(string-equal (bt:thread-name th) name :end1 (min (length (bt:thread-name th)) (length name))))
2651
collect (backtrace-thread th :start start :count count :stream stream)))
2653
(defun backtrace-thread (thread &key (start 0) (count most-positive-fixnum) (stream *trace-output*))
2655
(format stream "~%~%----------[~a ..." (bt:thread-name thread))
2656
(finish-output *trace-output*)
2657
(cond ((thread-locked-p thread)
2658
(format stream " locked]"))
2661
(progn (bt:interrupt-thread thread
2663
(format stream " : ~a" *task*)
2664
(handler-case (print-stacktrace stream :start start :count count)
2665
(error (c) (format stream "~%~%error in backtrace: ~a" c)))
2666
(format stream "... ~a]----------~%" (bt:thread-name (bt:current-thread)))
2667
(finish-output stream)
2669
(dotimes (i 10 (format stream " uninterruptable]"))
2672
(error (c) (format stream "~%~%error in interrupt-thread: ~a" c)))))
2675
(defun print-thread-operations (&key (stream *trace-output*) (verbose nil))
2676
(format stream "~&operations @gc: ~a~%" (list-thread-operations :verbose verbose))
2680
;(setq *query-maximum-threads* nil)
2681
;(setq *agp-maximum-threads* 0)
2683
;(list-thread-operations) ; :verbose t)
2684
;(trace-thread-operations)
2685
;(backtrace-threads :name "response")
2686
;(backtrace-threads :name "response" :start 16 :count 8)
2687
;(backtrace-threads :name "request" :start 16 :count 8)
2688
;(backtrace-threads :name "algebra" :start 16 :count 8)
2689
;(backtrace-threads :start 16 :count 16)
2690
;(backtrace-threads :name "response3" :start 16 :count 8)
2691
;(backtrace-threads :name "management" :start 16 :count 8)
2692
;(backtrace-threads :name "re" :start 16 :count 8)
2693
; (push 'print-thread-operations sb-ext:*after-gc-hooks*)
2701
(multiple-value-bind (t-or-nil ut st) (sb-unix::unix-getrusage 0)
2702
(declare (ignore t-or-nil))
2703
(let ((r (funcall fun)))
2704
(multiple-value-bind (t-or-nil nut nst) (sb-unix::unix-getrusage 0)
2705
(declare (ignore t-or-nil))
2706
(values r (- nut ut) (- nst st))))))
2708
(time (dt #'(lambda () (dotimes (x 1000000) (subseq "aaaa" 2)))))
2709
;;; turns out, that linux times to 10ms resolution only, while osx
2710
;;; appears to yield microsecond resolution values :
2711
;;; * (list (software-type) (multiple-value-list (sb-unix::unix-getrusage 0)))
2712
;;; ("Darwin" (T 876206 617965 0 0 0 0 0 0 0 1 5 0 0 14426 27 0))
2714
;;; * (list (software-type) (multiple-value-list (sb-unix::unix-getrusage 0)))
2715
;;; ("Linux" (T 20000 20000 0 0 0 0 6212 238 0 44952 0 0 0 0 263 2))
2720
;;; expression utilities
2722
;;; while solutions are represented as p-lists, a special equality predicate is used.
2723
;;; once they are just value sequences, the simpler alternative suffices
2725
(defun plist-same-term-p (plist1 plist2)
2726
;; allow for atomic keys
2727
(if (and (consp plist1) (consp plist2))
2728
(locally (declare (optimize (speed 3) (safety 0)))
2729
(do ((plist1 (cdr plist1) (cdr plist1)) (plist2 (cdr plist2) (cdr plist2)))
2730
((or (null plist1) (null plist2))
2731
(and (null plist1) (null plist2)))
2732
(let ((value1 (pop plist1)) (value2 (pop plist2)))
2733
(unless (or (eql value1 value2)
2734
(spocq.e:same-term value1 value2))
2736
(spocq.e:same-term plist1 plist2)))
2738
(defun list-same-term-p (list1 list2)
2739
(if (and (consp list1) (consp list2))
2740
(locally (declare (optimize (speed 3) (safety 0))
2741
(type list list1 list2))
2742
(loop for value1 in list1
2743
unless (and list2 (spocq.e:same-term value1 (pop list2)))
2745
finally (return (null list2))))
2746
(spocq.e:same-term list1 list2)))
2748
(defun term-id-list-equalp (list1 list2)
2749
(if (and (consp list1) (consp list2))
2750
(locally (declare (optimize (speed 3) (safety 0))
2751
(type list list1 list2))
2752
(loop for value1 in list1
2753
unless (and list2 (= (the fixnum value1) (the fixnum (pop list2))))
2755
finally (return (null list2))))
2756
(= (the fixnum list1) (the fixnum list2))))
2758
(defun sb-impl-mix (x y) ; from sbcl:src;code;target-sxhash
2759
(declare (optimize (speed 3) (safety 0)))
2760
(declare (type (and fixnum unsigned-byte) x y))
2761
(let ((xy (+ (* x 3) y)))
2762
(logand most-positive-fixnum
2767
(defun term-sxhash (term)
2769
(fixnum (sxhash (the fixnum term)))
2771
(spocq:blank-node (sxhash (spocq:blank-node-label term)))
2772
(spocq:iri (sxhash (spocq:iri-lexical-form term)))
2773
(spocq:plain-literal (sb-impl-mix (sxhash (spocq:literal-lexical-form term))
2774
(sxhash (spocq:plain-literal-language-tag term))))
2775
(spocq:atomic-typed-literal (sxhash (literal-value term)))
2776
(spocq:literal (sxhash (spocq:literal-lexical-form term)))
2779
(defun term-id-list-psxhash (key)
2780
(declare (optimize (speed 3) (safety 0)))
2782
;; could skip the keys for solution plists, but not for triples
2783
(cons (sb-impl-mix (sxhash (the fixnum (first key))) (term-id-list-psxhash (rest key))))
2784
(fixnum (sxhash (the fixnum key)))
2785
(t (term-sxhash key))))
2787
(defun list-psxhash (key)
2788
(declare (optimize (speed 3) (safety 0)))
2790
;; could skip the keys for solution plists, but not for triples
2791
(cons (sb-impl-mix (term-sxhash (first key)) (list-psxhash (rest key))))
2792
(t (term-sxhash key))))
2794
(defun solution-equalp (s1 s2) (list-same-term-p s1 s2))
2799
(defun make-solution-cache (&key single-thread (size 64))
2800
(declare (ignore single-thread))
2801
(make-hash-table :test 'solution-equalp
2802
:hash-function 'list-psxhash
2804
(defun make-triple-cache (&key single-thread (size 64))
2805
(declare (ignore single-thread))
2806
(make-hash-table :test 'list-same-term-p :hash-function 'list-psxhash :size size))
2807
(defun make-term-id-cache (&key single-thread (size 64))
2808
(declare (ignore single-thread))
2809
(make-hash-table :test 'term-id-list-equalp
2810
:hash-function 'term-id-list-psxhash
2812
(defun (setf get-solution) (solution key-value cache)
2813
(setf (gethash key-value cache) solution))
2814
(defun get-solution (solution cache &key (key nil) (key-value (if key (funcall key solution) solution)))
2815
(gethash key-value cache)))
2819
(defun make-solution-cache (&key (single-thread t) (size 64))
2820
(make-hash-table :test 'solution-equalp
2821
:hash-function 'list-psxhash
2823
:single-thread single-thread))
2824
(defun make-triple-cache (&key (single-thread t)(size 64))
2825
(make-hash-table :test 'list-same-term-p
2826
:hash-function 'list-psxhash
2828
:single-thread single-thread))
2829
(defun make-term-id-cache (&key (single-thread t) (size 64))
2830
(make-hash-table :test 'term-id-list-equalp
2831
:hash-function 'term-id-list-psxhash
2833
:single-thread single-thread))
2834
(defun (setf get-solution) (solution key-value cache)
2835
(setf (gethash key-value cache) solution))
2836
(defun get-solution (solution cache &key (key nil) (key-value (if key (funcall key solution) solution)))
2837
(gethash key-value cache)))
2842
(sb-ext:define-hash-table-test solution-equalp list-psxhash)
2843
(defun make-solution-cache (&key (single-thread t) (size 64))
2844
(make-hash-table :test 'solution-equalp
2846
:synchronized (not single-thread)))
2847
(sb-ext:define-hash-table-test list-same-term-p list-psxhash)
2848
(defun make-triple-cache (&key (single-thread t) (size 64))
2849
(make-hash-table :test 'list-same-term-p
2851
:synchronized (not single-thread)))
2852
(sb-ext:define-hash-table-test term-id-list-equalp term-id-list-psxhash)
2853
(defun make-term-id-cache (&key (single-thread t) (size 64))
2854
(make-hash-table :test 'term-id-list-equalp
2856
:synchronized (not single-thread)))
2857
(defun (setf get-solution) (solution key-value cache)
2858
(setf (gethash key-value cache) solution))
2859
(defun get-solution (key-value cache)
2860
(gethash key-value cache))
2862
(defmacro with-locked-cache ((cache) &body body)
2863
`(sb-ext:with-locked-hash-table (,cache) ,@body)))
2867
nb. sxhash may not be the right function to use. specifically for sbcl it is definitely suboptimal.
2868
there, given that +max-hash-depthoid+ limits recursion to the first four element of the list,
2869
a hashtable used for a join or a distinct would degenerate to list comparison as soon
2870
as the first two bindings are the same. sp2b yields that already in q6 @10.
2874
* (sxhash '(?::|name| "Lonce Jiron" ?::|author| _::|LonceJiron| ?::|yr| 1953 ?::|document|
2875
<http://localhost/publications/inprocs/Proceeding1/1953/Inproceeding15>
2876
?::|class| <http://localhost/vocabulary/bench/Inproceedings> ?::|author2|
2877
_::|KatrynVolmer| ?::|yr2| 1954 ?::|document2|
2878
<http://localhost/publications/inprocs/Proceeding1/1954/Inproceeding4>
2879
?::|class2| <http://localhost/vocabulary/bench/Inproceedings>))
2882
* (sxhash '(?::|name| "Lonce Jiron" ?::|author| _::|LonceJiron| ?::|yr| 1953 ?::|document|
2883
<http://localhost/publications/inprocs/Proceeding1/1953/Inproceeding15>
2884
?::|class| <http://localhost/vocabulary/bench/Inproceedings> ?::|author2|
2885
_::|ValentinaTopia| ?::|yr2| 1950 ?::|document2|
2886
<http://localhost/publications/inprocs/Proceeding1/1950/Inproceeding19>
2887
?::|class2| <http://localhost/vocabulary/bench/Inproceedings>))
2891
as the variation appears at element 12
2895
(:documentation "blank nodes and skolemization"
2896
"blank nodes are created in two contexts: as pattern terms and as data terms
2897
in the former case, they need be unique within the document only, for which
2898
a task-specific counter suffices. for the latter purpose, they should be
2899
unique within the store. the generation takes to steps. first generate an
2900
eight character string, of which the first is a random letter and the
2901
remaining 7 are the result of random character encoded base 37. this is
2902
resolved avainst the store. if a node is alredy present, repeat the
2903
process. otherwise use it a data.
2904
re randomness: to use random as the basis for this, requires that the
2905
work with a random state is threadsafe. at least sbcl does not rebind it, so
2906
it must be ensured when threads are created.
2908
wrt syntax turtle[4] allows initial alpha+_ followed by alphanumeric,
2909
rdf+xml allows an NCName, SPARQL[6] allows alpha+_ followed by alphanumeric
2910
plus some additions, n3 qname bnf[7] permits the same NCName with the a;phabetic initial
2911
character restriction, while the ntriples syntax[8] limits the subsequent
2912
characters to alphanumeric
2913
This generates initial alpha, followed by (alphanumeric+_)*7 where the '_'
2914
appears as left-padding.
2915
This is stricter than the SPARQL syntax - which applies to an explicit
2916
skolemization, which permits an initial digit , '.', and various other inclusions.
2918
[1] : http://answers.semanticweb.com/questions/8336/what-is-skolemization
2919
[2] : http://web.ing.puc.cl/~marenas/publications/iswc11.pdf
2920
[3] : http://www.w3.org/2011/rdf-wg/track/issues/40
2922
[4] : http://www.w3.org/TeamSubmission/turtle/#nodeID
2923
[5] : http://www.w3.org/TR/REC-rdf-syntax/#rdf-id
2924
[6] : http://www.w3.org/TR/2012/PR-sparql11-query-20121108/#rBLANK_NODE_LABEL
2925
[7] : http://www.w3.org/2000/10/swap/grammar/n3-ietf.txt
2926
[8] : http://www.w3.org/2001/sw/RDFCore/ntriples/
2929
(defun blank-node-global-prefix ()
2930
(or *blank-node-global-prefix*
2931
(setq *blank-node-global-prefix* (aref "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" (day-in-month)))))
2933
(defun compute-blank-node-global-label (&optional (prefix nil))
2934
"cf. the ntriples syntax, which requires the '0', as '_' is not permitted.
2935
coerce to lower case, which leaves the upper case containing alphabetic for
2937
(let ((label-number (random *blank-node-global-label-limit*)))
2938
;; nb. (not (> (expt 36 6) (expt 16 8)))
2939
(format nil "~@[~a_~]~(~c~36,7,'0r_~36,7,'0r~)" prefix (blank-node-global-prefix) label-number
2940
(logand (get-internal-real-time) #xffffffff))))
2941
;; (compute-blank-node-global-label)
2943
(defun cons-global-blank-node (&key (transaction *transaction*) (prefix nil))
2944
"construct and return a blank node which is globally unique. if a transaction
2945
is available, ensure this with respect to the store.
2946
the uniqueness test is global within the repository, and if a transaction is
2947
active, the node is interned."
2949
(flet ((lookup-node (node)
2950
(cffi:with-foreign-objects ((%term '(:struct rdfcache::term)))
2951
(rdfcache::%clear-term %term)
2953
(progn (set-optional-term %term node)
2954
(rdfcache:lookup-term-number %term))
2955
(clear-optional-term %term)))))
2956
(or (loop for count below 10
2957
do (let* ((label (compute-blank-node-global-label prefix))
2958
(node (spocq:make-blank-node label)))
2959
(when (null (lookup-node node))
2961
(rdfcache-object-term-number transaction node))
2963
(error "no unique blank node generated"))))
2965
(defun skolemize-insertions-p ()
2966
(member (blank-node-skolemize) '(|urn:dydra|:|skolemize| |urn:dydra|:|skolemize-insert|)))
2968
(defun skolemize-encoding-p ()
2969
(plusp (length (blank-node-prefix))))
2972
(defun cons-blank-node (&optional (prefix (blank-node-prefix)))
2973
"construct and return a blank node unique within the context of the query.
2974
these are used in query patterns and in generated output. For insert operations,
2975
the stricter cons-global-blank-node is used, as it guarantees uniqueness within the store."
2977
(intern-blank-node (concatenate 'string
2978
(if (zerop (length prefix)) *blank-node-cons-prefix* prefix)
2979
(princ-to-string (next-blank-node-index)))))
2981
(defun cons-sequence-uri ()
2982
"Construct and return an rdf sequence uri. Index the respective counter to ensure that
2983
numbers iterate across the domain within the context of a given query process."
2984
(intern-term-aspects :uri (format nil "http://www.w3.org/1999/02/22-rdf-syntax-ns#_~d" (next-sequence-index)) nil nil))
2986
(defun cons-sequence-number ()
2987
"Construct and return an rdf sequence number. Index the respective counter to ensure that
2988
numbers iterate across the domain within the context of a given query process."
2989
(next-sequence-index))
2991
(defun variable-p (object)
2992
"true iff the object represents a variable."
2993
(and (symbolp object)
2994
(eq (symbol-package object) *variable-package*)))
2996
(defun cons-variable (&optional (prefix ""))
2997
"construct an undistinguished variable"
2998
(cons-symbol *variable-package* "?" prefix (princ-to-string (next-variable-index))))
2999
#+(or) ; to get rid of undistingusihed variables (see below)
3000
(defun cons-variable (&optional (prefix ""))
3001
"construct an undistinguished variable"
3002
(cons-symbol *variable-package* "!" prefix (princ-to-string (next-variable-index))))
3004
;;; characterize variables as to whether they are "real" or correspond to blank nodes - whether
3005
;;; from the original query expression or generated, eg. for property paths.
3006
;;; but, this would confuse things (deftype variable () (satisfies undistinguished-variable-p))
3008
(defun undistinguished-variable-p (object)
3009
"true iff the object represents an undistinguished variable.
3010
wrt terminology: http://en.wikipedia.org/wiki/Conjunctive_query"
3011
(and (variable-p object)
3012
(let ((name (symbol-name object)))
3013
(and (> (length name) 0) (eql (char name 0) #\?)))))
3015
(deftype undistinguished-variable () '(satisfies undistinguished-variable-p))
3017
(defun undistinguished-variables (variables)
3018
(remove-if-not #'undistinguished-variable-p variables))
3020
(defun distinguished-variable-p (object)
3021
"true iff the object represents an undistinguished variable."
3022
(and (variable-p object)
3023
(let ((name (symbol-name object)))
3024
(and (> (length name) 0) (not (eql (char name 0) #\?))))))
3026
(deftype distinguished-variable () '(satisfies distinguished-variable-p))
3028
(defun distinguished-variables (variables)
3029
(remove-if-not #'distinguished-variable-p variables))
3032
(defun variable-name (object)
3036
(defun make-variable (name)
3037
(intern name *variable-package*))
3040
(defgeneric expression-variables (expression)
3041
(:documentation "Extract a list of the expression's variables. In general, this includes blank nodes as
3042
they can be bound bgp operations. Later operations may need to exclude them.")
3043
(:method ((expresion null))
3045
(:method ((expression symbol))
3046
(when (variable-p expression)
3048
(:method ((expression cons))
3049
(union (expression-variables (first expression))
3050
(expression-variables (rest expression))))
3051
(:method ((expression t))
3054
(defun bindings-variables (bindings)
3055
"Return the variable component of a binding. Allow a singleton binding as well as to a value form."
3056
(loop for form in bindings
3057
collect (etypecase form
3061
(defun bindings-value-forms (bindings)
3062
"Return the value component of a binding. Allow a singleton binding as well as to a value form."
3063
(loop for form in bindings
3064
collect (etypecase form
3065
(cons (second form))
3068
(defun bindings-value-variables (bindings)
3069
"Return the variables from the value components of a binding.
3070
Allow a singleton binding as well as to a value form.
3071
Suppress any variable bound in the set"
3072
(loop for form in bindings
3073
collect (etypecase form (cons (second form)) (symbol (list form))) into expression-variables
3074
when (and (consp form) (not (eq (first form) (second form))))
3075
collect (first form) into local-variables
3076
finally (return (set-difference (expression-variables expression-variables) local-variables))))
3078
(defgeneric expression-projected-variables (expression)
3079
(:method ((expression cons))
3080
(case (first expression)
3082
(destructuring-bind (field-expression variable value-expression) (rest expression)
3083
(declare (ignore value-expression)) ; it cannot introduce variables
3084
(let ((field-variables (expression-projected-variables field-expression)))
3085
(if (find variable field-variables)
3087
(cons variable field-variables)))))
3089
(remove-if-not #'distinguished-variable-p (rest expression)))
3091
;; a project adds at most the declared variables
3094
(if (listp (third expression))
3095
;; a select adds either the declared or bound variables
3096
(labels ((select-variables (variables)
3098
(destructuring-bind (first . rest) variables
3100
(keyword (select-variables (cdr rest)))
3101
(symbol (if (variable-p first)
3102
(cons first (select-variables rest))
3103
(select-variables rest)))
3104
(cons (cons (first first) (select-variables rest))))))))
3105
(select-variables (third expression)))
3106
;; or all from the field expression
3107
(expression-projected-variables (second expression))))
3110
(reduce #'append (rest expression) :key #'expression-projected-variables :initial-value nil)
3112
(spocq.a::|equivalents|
3113
(mapcar #'first (rest expression)))
3115
(expression-projected-variables (rest expression)))
3119
(expression-projected-variables (second expression)))
3127
(if (and (variable-p (first expression))
3128
(consp (rest expression))
3129
(null (cddr expression)))
3130
;; if it's a binding, just the bound variable
3131
(list (first expression))
3132
;; anything else adds everything present
3133
(remove-duplicates (reduce #'append expression :key #'expression-projected-variables)
3135
(:method ((expression t))
3136
(expression-variables expression)))
3139
(defgeneric operation-free-variables (operation arguments)
3140
(:documentation "returns all variables which are free in the form.
3141
in distinction to projected variables this includes those in an exists, excludes those bound by
3142
an extend, values, or select form. the variables from a bgp are included as well, as they are
3143
also subject to dynamic bindings.")
3145
(:method ((operation (eql 'spocq.a:|bgp|)) arguments)
3146
(reduce #'append arguments :key #'expression-free-variables :initial-value nil))
3148
(:method ((operation (eql 'spocq.a:|bindings|)) arguments)
3149
"The vlaue forms are constant"
3152
(:method ((operation (eql 'spocq.a:|declare|)) arguments)
3155
(:method ((operation (eql 'spocq.a:|extend|)) arguments)
3156
"An extend form can have free variables in both the base field and the binding value expression.
3157
Those in the value expression must be netted against thos eprojected from the field.
3158
The variable has no consequence."
3159
(destructuring-bind (field-expression variable value-expression) arguments
3160
(declare (ignore variable))
3161
(union (set-difference (expression-variables value-expression)
3162
(expression-projected-variables field-expression))
3163
(expression-free-variables field-expression))))
3165
(:method ((operation (eql 'spocq.a:|extend|)) arguments)
3166
"An extent leaves free anything which is not projected. The binding variable ha no immediate effect."
3167
(destructuring-bind (field variable value . options) arguments
3168
(declare (ignore variable options))
3169
(set-difference (union (expression-free-variables value)
3170
(expression-free-variables field))
3171
(expression-projected-variables field))))
3173
(:method ((operation (eql 'spocq.a::|equivalents|)) (arguments t))
3176
(:method ((operation (eql 'spocq.a:|filter|)) arguments)
3177
(destructuring-bind (field predicate . options) arguments
3178
(declare (ignore options))
3179
(union (set-difference (expression-free-variables predicate) (expression-projected-variables field))
3180
(expression-free-variables field))))
3182
(:method ((operation (eql 'spocq.a:|project|)) arguments)
3183
(operation-free-variables 'spocq.a:|select| arguments))
3185
(:method ((operation (eql 'spocq.a:|select|)) arguments)
3186
(labels ((projection-free-variables (projection projected free)
3187
;; return as two values the projetion bindings and the values' free variables
3189
(destructuring-bind (first . rest) projection
3191
(keyword (projection-free-variables (cdr rest) projected free))
3192
(symbol (if (variable-p first)
3193
(projection-free-variables rest (cons first projected) free)
3194
(projection-free-variables rest projected free)))
3195
(cons (projection-free-variables rest
3196
(cons (first first) projected)
3197
(set-difference (expression-variables (second first)) projected)))
3198
(t (projection-free-variables rest projected free))))
3200
;; those varibles free in the projection clause, but not projected from the field
3201
(set-difference (projection-free-variables (second arguments) nil
3202
(expression-free-variables (first arguments)))
3203
(expression-projected-variables (first arguments)))))
3205
(:method ((operation (eql 'spocq.a:|slice|)) arguments)
3206
(expression-free-variables (first arguments)))
3208
(:method ((operation (eql 'spocq.a:|triple|)) arguments)
3209
"A triple allows that any variable present can be dynamiclly bound."
3210
(loop for elt in arguments for i below 3
3211
when (distinguished-variable-p elt) collect elt))
3213
(:method ((operation t) arguments)
3214
(reduce #'append arguments :key #'expression-free-variables)))
3217
(defgeneric expression-free-variables (expression)
3218
(:method ((expression symbol))
3219
;; Allow any variable
3220
;; - the non-distinguished cases is handled for bgp forms in isolation
3221
;; - having generates a ?::?having-xxx variable for each constraint
3222
(when (variable-p expression)
3224
(:method ((expression cons))
3225
(operation-free-variables (first expression) (rest expression)))
3226
(:method ((expression t))
3229
(defgeneric operation-matched-variables (operation arguments)
3230
(:documentation "returns all variables which are from a bgp are included as well.")
3232
(:method ((operation (eql 'spocq.a:|bgp|)) arguments)
3233
(reduce #'append arguments :key #'expression-matched-variables :initial-value nil))
3235
(:method ((operation (eql 'spocq.a:|triple|)) arguments)
3236
"any variable present"
3237
(loop for elt in arguments for i below 3
3238
when (variable-p elt) collect elt))
3240
(:method ((operation t) arguments)
3241
(reduce #'append arguments :key #'expression-matched-variables)))
3243
(defgeneric expression-matched-variables (expression)
3244
(:method ((expression cons))
3245
(operation-matched-variables (first expression) (rest expression)))
3246
(:method ((expression t))
3249
(defun expression-free-dimensions (expression)
3250
"Return the expression's free variables, ordered and deduplicated"
3251
(sort (remove-duplicates (copy-list (expression-free-variables expression))) #'string-lessp))
3254
(defgeneric expression-dimensions (expression)
3255
;; limit the dimensions to those which are actually projected !
3256
(:method ((expression t)) (sort (copy-list (expression-projected-variables expression)) #'string-lessp))
3257
(:method ((expression solution-field)) (solution-field-dimensions expression)))
3259
#+(or) ;; superseded by bgp-projected-dimensions
3260
(defun bgp-dimensions (statements)
3261
"for a basic graph pattern return both distinguished and non-distinguished variables
3262
in order the anonymous variable be available to join."
3263
(case *class.repository*
3264
(rdfcache-matrix-repository
3265
;; a bgp applied as a matrix yields the complete join dimensionality of the
3266
;; patterns - including duplicates and constants from a single pattern
3268
(reduce #'join-result-dimensions (loop for pattern in statements
3269
when (triple-form-p pattern)
3270
collect (remove-if-not #'variable-p (statement-terms pattern)))
3273
(sort (expression-variables statements) #'string-lessp))))
3275
(defun bgp-projected-dimensions (body)
3276
(if (consp (first body))
3277
(let ((variables ()))
3278
(loop for statement-pattern in body
3279
do (loop for variable in (bgp-statement-projected-dimensions statement-pattern)
3280
do (pushnew variable variables)))
3281
(sort variables #'string-lessp))))
3283
(defun bgp-statement-projected-dimensions (pattern)
3284
;; filters and identifiers cannot contribute projection
3285
(cond ((triple-form-p pattern)
3286
(let ((terms (statement-terms pattern)))
3287
(destructuring-bind (s p o &optional g) terms
3288
(declare (ignore s o))
3289
(cond ((active-verb-p p) ;; the subject and object are artefacts of the lists
3291
(cons g (active-verb-results p))
3292
(active-verb-results p)))
3294
(loop for term in terms
3295
when (variable-p term)
3297
((graph-form-p pattern)
3298
(let ((graph (second pattern)))
3299
(when (variable-p graph)
3307
;;; x5 faster, relative to loop or find,
3308
(defun bgp-pattern-type (statements)
3309
(some #'(lambda (s) (and (consp s)
3310
(eq (pop s) 'spocq.a:|triple|)
3311
(eq (first (setf s (cdr s))) |rdf|:|type|)
3315
(defun bgp-pattern-predicates (statements)
3316
(loop for statement in statements
3317
when (triple-form-p statement)
3318
collect (statement-predicate statement)))
3320
(defun bgp-pattern-subject (statements)
3321
(let ((pattern-subject ()))
3322
(loop for statement in statements
3323
when (triple-form-p statement)
3324
do (let ((subject (statement-subject statement)))
3325
(if (null pattern-subject)
3326
(setf pattern-subject subject)
3327
(unless (equalp pattern-subject subject)
3329
finally (return pattern-subject))))
3332
;;; derive expression variable with scoping
3334
(defgeneric parse-expression-variables (expression)
3335
(:documentation "given a symbolic algebra expression, return as several values the
3337
all : all distinguished variables;
3338
bound : all variables which are bound, in their outermost scope,
3339
in either an extend or a select form;
3340
free : all variables which are not bound in their outermost scope. this may include
3341
projection references, but not projection bindings
3342
projected : all variables which appear in a select projection, in their outermost scope,
3343
whether simple references or bindings, or, absent a projection form all free variables.
3345
delegate to parse-operation-variables for the operator-specific logic.")
3347
(:method ((expression symbol))
3348
;; Allow any variable
3349
;; - the non-distinguished cases is handled for bgp forms in isolation
3350
;; - having generates a ?::?having-xxx variable for each constraint
3351
(when (variable-p expression)
3353
(:method ((expression cons))
3354
(parse-operation-variables (first expression) (rest expression)))
3355
(:method ((generator solution-generator))
3356
(let ((dimensions (solution-generator-dimensions generator)))
3357
(values dimensions nil dimensions dimensions)))
3358
(:method ((expression t))
3361
(macrolet ((collecting-variables ((elt list) form)
3366
(loop for ,elt in ,list
3367
do (multiple-value-bind (arg-all arg-bound arg-free arg-projected)
3369
(setf all (union all arg-all)
3370
bound (union bound arg-bound)
3371
free (union free arg-free)
3372
projected (union projected arg-projected))))
3373
(values all bound free projected))))
3375
(defgeneric parse-operation-variables (operation arguments)
3376
(:documentation "analyse the given operator arguments according to binding and scoping
3377
rules and return as several values the variable sets: all bound free projected.")
3379
(:method ((operation (eql 'spocq.a:|bgp|)) arguments)
3380
"all free variables in any triple are free in the bgp"
3381
(collecting-variables (pattern arguments) (parse-expression-variables pattern)))
3382
;;; (parse-expression-variables (parse-sparql "select * where {?s ?p _:test . _:test ?p2 ?o2}"))
3385
(:method ((operation (eql 'spocq.a:|bindings|)) arguments)
3386
"The variables are bound, while the value forms are constant."
3387
(destructuring-bind (solutions variables) arguments
3388
(declare (ignore solutions))
3389
(values variables variables nil nil)))
3390
;;; (parse-expression-variables (parse-sparql "select * where {?s ?p _:test . values ?z { 1 2 }}"))
3391
;;; (parse-expression-variables (parse-sparql "select * where {?s ?p _:test . values ?p { 1 2 }}"))
3393
(:method ((operation (eql 'spocq.a:|declare|)) arguments)
3396
(:method ((operation (eql 'spocq.a::|equivalents|)) (arguments t))
3399
(:method ((operation (eql 'spocq.a:|extend|)) arguments)
3400
"An extend form can have free variables in both the base field and the binding value expression.
3401
Those in the value expression must be netted against those projected from the field.
3402
The variable, if present, is then eliminated, as that would be am error."
3403
(destructuring-bind (field-expression variable value-expression) arguments
3404
(multiple-value-bind (field-all field-bindings field-free field-projection)
3405
(parse-expression-variables field-expression)
3406
(multiple-value-bind (value-all value-bindings value-free value-projection)
3407
(parse-expression-variables value-expression)
3408
(when value-bindings
3409
(log-debug "pov: binding in value form: ~s: ~s" value-bindings value-expression))
3410
(when value-bindings
3411
(log-debug "pov: projection in value form: ~s: ~s" value-projection value-expression))
3412
;; do not check the new binding constraint here
3413
(pushnew variable field-bindings)
3414
(values (union field-all value-all)
3416
(union field-free value-free)
3417
field-projection)))))
3418
;;; (parse-expression-variables (parse-sparql "select * where { {?s ?a ?b} . bind (1 as ?a) . bind (2 as ?b) . }"))
3420
(:method ((operation (eql 'spocq.a:|filter|)) arguments)
3421
(destructuring-bind (field predicate . options) arguments
3422
(declare (ignore options))
3423
(multiple-value-bind (field-all field-bindings field-free field-projection)
3424
(parse-expression-variables field)
3425
(multiple-value-bind (predicate-all predicate-bindings predicate-free predicate-projection)
3426
(parse-expression-variables predicate)
3427
(declare (ignore predicate-bindings predicate-projection))
3428
(values (union field-all predicate-all)
3429
;; no binding from a predicate subexpression (eg exists) can be apparent
3431
(union field-free predicate-free)
3432
;; no projection from a predicate subexpression (eg exists) can be apparent
3433
field-projection)))))
3434
;;; (parse-expression-variables (parse-sparql "select * where { {?s ?a ?b} . filter(1 = ?b) . }"))
3435
;;; (parse-expression-variables (parse-sparql "select * where { {?s ?a ?b} . filter(exists {?x ?a ?b}) . }"))
3436
;;; (parse-expression-variables (parse-sparql "select * where { {?s ?a ?b} . filter(exists {select ?x where {?x ?a ?b}}) . }"))
3438
(:method ((operation (eql 'spocq.a:|join|)) arguments)
3439
(multiple-value-bind (first-all first-bindings first-free first-projection)
3440
(parse-expression-variables (first arguments))
3441
(multiple-value-bind (second-all second-bindings second-free second-projection)
3442
(parse-expression-variables (second arguments))
3443
(let ((join-free (union first-free second-free)))
3444
(values (union first-all second-all)
3445
(set-difference (union first-bindings second-bindings) join-free)
3447
(union first-projection second-projection))))))
3449
(:method ((operation (eql 'spocq.a:|leftjoin|)) arguments)
3450
(parse-operation-variables 'spocq.a:|join| arguments))
3452
(:method ((operation (eql 'spocq.a:|project|)) arguments)
3453
(destructuring-bind (field dimensions . options) arguments
3454
(declare (ignore options))
3455
(multiple-value-bind (field-all field-bindings field-free field-projection)
3456
(parse-expression-variables field)
3457
(declare (ignore field-projection))
3458
(values field-all ;; dimensions must be in the field
3463
(:method ((operation (eql 'spocq.a:|select|)) arguments)
3464
(destructuring-bind (field selection-specification . options) arguments
3465
(declare (ignore options))
3466
(loop (if (keywordp (first selection-specification))
3467
(setf selection-specification (cddr selection-specification))
3469
(multiple-value-bind (field-all field-bindings field-free field-projection)
3470
(parse-expression-variables field)
3471
(declare (ignore field-projection)) ;; is superseded
3472
(loop for selection in selection-specification
3473
with selection-projection = ()
3474
do (typecase selection
3475
(symbol (when (variable-p selection)
3476
(pushnew selection selection-projection)))
3477
(cons (destructuring-bind (variable value) selection
3478
(pushnew variable field-bindings)
3479
(pushnew variable selection-projection)
3480
(multiple-value-bind (value-all value-bindings value-free value-projection)
3481
(parse-expression-variables value)
3482
(setf field-all (union value-all field-all))
3483
(setf field-bindings (union field-bindings value-bindings)) ;; should not be any
3484
(when (intersection field-free value-bindings)
3485
(log-debug "pov: free variable as bindings: ~s" value))
3486
(setf field-free (union field-free value-free))
3487
(when value-projection
3488
(log-debug "pov: projection variable in value: ~s" value)))))
3490
finally (return (values field-all
3492
;; the free variables are reduced by the bindings
3493
(set-difference field-free field-bindings)
3494
selection-projection))))))
3496
(:method ((operation (eql 'spocq.a:|service|)) arguments)
3497
(parse-expression-variables (first arguments)))
3499
(:method ((operation (eql 'spocq.a:|slice|)) arguments)
3500
(parse-expression-variables (first arguments)))
3502
(:method ((operation (eql 'spocq.a:|triple|)) arguments)
3503
"All distinguished variable present in a triple is free and can be dynamiclly bound.
3504
The are also projected."
3506
(loop for elt in arguments for i below 3
3507
when (distinguished-variable-p elt)
3508
do (pushnew elt all))
3509
(values all nil all all)))
3511
(:method ((operation (eql 'spocq.a:|union|)) arguments)
3512
(parse-operation-variables 'spocq.a:|join| arguments))
3514
(:method ((operation t) arguments)
3515
(collecting-variables (argument arguments) (parse-expression-variables argument))
3520
(defun expression-blank-nodes (expression)
3522
(labels ((collect (expression)
3523
(cond ((null expression) )
3524
((spocq:blank-node-p expression)
3525
(pushnew expression nodes :test #'equalp))
3527
(collect (first expression))
3528
(collect (rest expression)))
3530
(collect expression))
3533
(defun expression-undistinguished-variables (expression)
3534
(let ((variables ()))
3535
(labels ((collect (expression)
3536
(cond ((null expression) )
3537
((undistinguished-variable-p expression)
3538
(pushnew expression variables))
3540
(collect (first expression))
3541
(collect (rest expression)))
3543
(collect expression))
3546
(defun expression-resources (expression)
3547
(declare (ftype function spocq.e:iri-p))
3549
(labels ((collect (expression)
3550
(cond ((null expression) )
3552
(pushnew expression result))
3554
(collect (first expression))
3555
(collect (rest expression)))
3557
(collect expression))
3561
(defun spocq.e:constantp (object)
3562
"Return true iff the term is a constant.
3563
This includes all boxed term structures, native strings and numbers, and symbols which represent
3567
(spocq:term (not (spocq:blank-node-p object)))
3570
(symbol (iri-p object))))
3572
(defun spocq.e:literalp (object)
3573
"Return true iff the term is a literal.
3574
This includes boxed literal structures and native strings and numbers."
3582
(defun expression-constants (expression)
3584
(labels ((collect (expression)
3585
(cond ((null expression) )
3586
((spocq.e:constantp expression)
3587
(pushnew expression result :test #'spocq.e:|=|))
3589
(collect (first expression))
3590
(collect (rest expression)))
3592
(collect expression))
3596
(defun expression-algebra-operators (expression &key (unique-p t) (triples-p nil))
3598
(labels ((collect (expression)
3599
(cond ((null expression) )
3601
(destructuring-bind (op . args) expression
3602
(if (and (symbolp op)
3603
(eq (symbol-package op) *ALGEBRA-PACKAGE*)
3604
(or (fboundp op) (macro-function op)
3605
(and triples-p (eq op 'spocq.a:|triple|))))
3607
(pushnew (first expression) result)
3608
(push (first expression) result))
3609
(collect (first expression)))
3610
(dolist (elt args) (collect elt))))
3612
(collect expression))
3615
(defun expression-algebra-expressions (expression &key (triples-p nil) (operators nil))
3617
(labels ((collect (expression)
3618
(cond ((null expression) )
3620
(destructuring-bind (op . args) expression
3621
(if (and (symbolp op)
3622
(eq (symbol-package op) *ALGEBRA-PACKAGE*)
3623
(or (null operators) (member op operators))
3624
(or (fboundp op) (macro-function op)
3625
(and triples-p (eq op 'spocq.a:|triple|))))
3626
(push expression result)
3628
(dolist (elt args) (collect elt))))
3630
(collect expression))
3633
(defun expression-operators (expression)
3635
(labels ((collect (expression)
3636
(cond ((null expression) )
3638
(destructuring-bind (op . args) expression
3639
(if (and (symbolp op)
3640
(or (fboundp op) (macro-function op)))
3641
(pushnew (first expression) result)
3642
(collect (first expression)))
3643
(dolist (elt args) (collect elt))))
3645
(collect expression))
3648
(defgeneric expression-form-p (source predicate)
3649
(:method ((source list) (predicate function))
3651
(labels ((walk (form)
3652
(cond ((funcall predicate form)
3655
(map nil #'walk form)))))
3658
(:method ((source t) predicate)
3659
;; from test;sore;encoding;ssf-sparql
3660
(expression-form-p (ignore-errors (parse-sparql source)) predicate))
3662
(:method ((source t) (operator symbol))
3663
(expression-form-p source
3664
#'(lambda (form) (and (consp form)
3665
(eq (first form) operator))))))
3667
(defun expression-service-forms (expression)
3668
(expression-form-p expression #'service-form-p))
3670
(defun expression-select-forms (expression)
3671
(expression-form-p expression #'select-form-p))
3673
(defun expression-table-forms (expression)
3674
(expression-form-p expression #'table-form-p))
3676
(defparameter *expression-pattern-statements-graph* nil)
3678
(defun expression-pattern-statements (expression)
3679
"Walk an expression and return the pattern statements.
3680
Where a bgp is within the scope of a graph, its statements are transformed into quads.
3681
Otherwise, the literal triples suffice."
3683
(let ((statements ())
3684
(*expression-pattern-statements-graph* nil))
3685
(labels ((collect-statements (expression)
3686
(cond ((bgp-form-p expression)
3687
(loop for statement in (rest expression)
3688
when (triple-form-p statement)
3689
do (push (if *expression-pattern-statements-graph*
3690
`(spocq.a:|quad| ,@(rest statement)
3691
,(if (variable-p *expression-pattern-statements-graph*)
3693
*expression-pattern-statements-graph*))
3697
((graph-form-p expression)
3698
(let ((*expression-pattern-statements-graph* (second expression)))
3699
(walk-tree #'collect-statements (third expression))
3703
(declare (dynamic-extent #'collect-statements))
3704
(walk-tree #'collect-statements expression))
3705
(remove-duplicates statements :test #'equal)))
3707
(defun expression-pattern-predicates (expression)
3708
(remove-duplicates (mapcar #'statement-predicate (expression-pattern-statements expression))))
3710
(defun expression-pattern-classes (expression)
3711
"Walk an expression and return the pattern classes.
3712
Each pattern collects the respective statement predicate for a given subject.
3713
Graph scope has no effect."
3716
(labels ((collect-statements (expression)
3717
(cond ((bgp-form-p expression)
3718
(loop for statement in (rest expression)
3719
with bgp-classes = ()
3720
when (and (triple-form-p statement) (not (eq (statement-predicate statement) |rdf|:|type|)))
3721
do (pushnew (statement-predicate statement) (getf bgp-classes (statement-subject statement)))
3722
finally (loop for (class predicates) on bgp-classes by #'cddr
3723
do (push predicates classes)))
3727
(declare (dynamic-extent #'collect-statements))
3728
(walk-tree #'collect-statements expression))
3729
(remove-duplicates classes :test #'(lambda (c1 c2) (null (set-exclusive-or c1 c2))))))
3731
(defun expression-triples (expression)
3732
"Walk an expression and return the literal triples - that is w/o graph augmentation."
3734
(let ((statements ())
3735
(*expression-pattern-statements-graph* nil))
3736
(labels ((collect-statements (expression)
3737
(cond ((bgp-form-p expression)
3738
(dolist (sub-expression (rest expression))
3739
(collect-statements sub-expression)))
3740
((triple-form-p expression)
3741
(push expression statements))
3744
(declare (dynamic-extent #'collect-statements))
3745
(walk-tree #'collect-statements expression))
3746
(remove-duplicates statements :test #'equal)))
3748
(defgeneric expression-bgps (source)
3749
(:method ((source list))
3751
(labels ((walk (form)
3752
(cond ((bgp-form-p form)
3755
(map nil #'walk form)))))
3758
(:method ((source t))
3759
;; from test;sore;encoding;ssf-sparql
3760
(expression-bgps (parse-sparql source))))
3763
(defun aggregate-expression-p (expression)
3764
(labels ((test-for-aggregate-op (expression)
3765
(cond ((consp expression)
3766
(if (aggregate-operator-p (first expression))
3767
(return-from aggregate-expression-p t)
3768
(map nil #'test-for-aggregate-op expression)))
3770
(test-for-aggregate-op expression))
3773
(defun aggregate-operator-p (object)
3774
(when (member object '(spocq.a:|avg| spocq.a:|count| spocq.a:|corr| spocq.a:|group_concat|
3775
spocq.a:|max| spocq.a:|min|
3776
spocq.a:|sample| spocq.a:|std| spocq.a:|sum|))
3779
(defun temporal-expression-p (e)
3780
"Return true iff the argument is an expression which is a logical conjunction of temporal relations
3781
drawn from the OWL time ontology."
3783
(destructuring-bind (op . args) e
3785
((spocq.a:|and| spocq.a:|or| spocq.a:|not| spocq.a:|exprlist|
3786
ORG.DATAGRAPH.SPOCQ.ALGEBRA:|\|\|| ORG.DATAGRAPH.SPOCQ.ALGEBRA:|&&| ORG.DATAGRAPH.SPOCQ.ALGEBRA:|!|)
3787
(some #'temporal-expression-p args))
3789
(and (symbolp op) (eq (symbol-package op) (load-time-value (symbol-package '|time|:|intervalAfter|)))))))))
3791
(defun temporal-value-p (e)
3793
(destructuring-bind (op . args) e
3794
(declare (ignore args))
3795
(member op '(|dydra|:|version-end| |dydra|:|version-start| |dydra|:|version|)))))
3797
(defgeneric revision-interval-end-record (version)
3798
(:method ((version t)) nil)
3799
(:method ((version spocq:revision-interval))
3800
(rlmdb:revision-record (spocq:revision-interval-end version))))
3802
(defgeneric revision-interval-start-record (version)
3803
(:method ((version t)) nil)
3804
(:method ((version spocq:revision-interval))
3805
(rlmdb:revision-record (spocq:revision-interval-start version))))
3807
(defgeneric revision-interval-end-date-time (version)
3808
(:method ((version t)) nil)
3809
(:method ((version spocq:revision-interval))
3810
(let ((record (revision-interval-end-record version)))
3813
(rlmdb:revision-record (rlmdb:revision-record-date-time record))))))
3814
(defgeneric revision-interval-end-ordinal (version)
3815
(:method ((version t)) nil)
3816
(:method ((version spocq:revision-interval))
3817
(let ((record (revision-interval-end-record version)))
3820
(rlmdb:revision-record (rlmdb:revision-record-ordinal record))))))
3821
(defgeneric revision-interval-end-uuid (version)
3822
(:method ((version t)) nil)
3823
(:method ((version spocq:revision-interval))
3824
(let ((record (revision-interval-end-record version)))
3827
(rlmdb:revision-record (rlmdb:revision-record-uuid record))))))
3829
(defgeneric revision-interval-start-date-time (version)
3830
(:method ((version t)) nil)
3831
(:method ((version spocq:revision-interval))
3832
(let ((record (revision-interval-start-record version)))
3835
(rlmdb:revision-record (rlmdb:revision-record-date-time record))))))
3836
(defgeneric revision-interval-start-ordinal (version)
3837
(:method ((version t)) nil)
3838
(:method ((version spocq:revision-interval))
3839
(let ((record (revision-interval-start-record version)))
3842
(rlmdb:revision-record (rlmdb:revision-record-ordinal record))))))
3843
(defgeneric revision-interval-start-uuid (version)
3844
(:method ((version t)) nil)
3845
(:method ((version spocq:revision-interval))
3846
(let ((record (revision-interval-start-record version)))
3849
(rlmdb:revision-record (rlmdb:revision-record-uuid record))))))
3853
(defun map-tree (function tree)
3854
"Copy the tree under the control of the mapped function.
3855
If it returns a given node, copy that node recursively. If it returns some other value,
3856
replace the node with that value."
3857
(declare (dynamic-extent function))
3859
(flet ((map-element (e) (map-tree function e)))
3860
(declare (dynamic-extent #'map-element))
3861
(let ((replacement (funcall function tree)))
3862
(if (eq replacement tree)
3864
(cons (cons (map-tree function (first tree))
3865
(map-tree function (rest tree))))
3867
(vector (map 'vector #'map-element tree))
3872
(defun replace-values-data (sse values-data &key (if-does-not-exist :error))
3873
"Substitute the bindings of a values clause into the matching form in the query.
3874
This applies the bindings by rewriting the query.
3875
The current implementation performs the substitution in-line, while evaluating
3877
(see spocq.e:stream-bindings)"
3878
(destructuring-bind (values-data-values-field values-data-dimensions) values-data
3879
(let* ((substitutions 0)
3880
(result (map-tree #'(lambda (form)
3881
(or (and (bindings-form-p form)
3882
(destructuring-bind (op form-value-field form-dimensions) form
3883
(declare (ignore form-value-field))
3884
(when (equal form-dimensions values-data-dimensions)
3885
(incf substitutions)
3886
`(,op ,values-data-values-field ,values-data-dimensions))))
3889
(unless (plusp substitutions)
3890
(ecase if-does-not-exist
3891
(:error (spocq.e:request-error "no values clause present for request values variables: ~a"
3892
values-data-dimensions))
3896
(defgeneric bind-sparql-expression (sse bindings)
3898
"rewrite a query sse form to replace any variables present in the bindings with their values.
3899
this applies to pattern variables and select, aggregation or filter expressions only.
3900
The intent is to propagate bindings through to service locations with the precedence
3901
1. constants in the expression
3902
2. supplied bindings - including new dimensions
3903
the latter intend to cause request bindings to precede any sip values (see ")
3904
(:method (sse (bindings cons))
3905
(labels ((bind-bindings (variables)
3906
(loop for binding in variables
3907
collect (typecase binding
3908
(cons (cons (first binding) (map-tree #'bind-form (rest binding))))
3909
(t (let ((value (rest (assoc binding bindings))))
3910
(if value (list binding value) binding))))))
3914
((spocq.a:|triple| spocq.a:|quad|)
3915
(sublis bindings form))
3917
(destructuring-bind (op field variables &rest args) form
3918
`(,op ,(map-tree #'bind-form field)
3919
,(when (consp variables)
3920
(append (loop for first = (first variables)
3921
until (not (keywordp first))
3923
collect (ecase (pop variables)
3924
(:having (sublis bindings (pop variables)))
3925
(:group-by (bind-bindings (pop variables)))))
3926
(append (bind-bindings variables)
3927
(loop for binding in bindings
3928
for (var . value) = binding
3929
unless (loop for select-var in variables
3930
when (or (eq select-var var) (and (consp select-var)
3931
(eq (first select-var) var)))
3933
collect (list var value)))))
3937
(declare (dynamic-extent #'bind-form))
3938
(map-tree #'bind-form sse)))
3939
(:method (sse (bindings null))
3941
;;; (bind-sparql-expression (parse-sparql "select ?x (abs(?y) as ?abs) where { ?s ?p1 ?x . ?s ?p2 ?y} group by ?s") '((?::|x| . 1) (?::|a| . 1)))
3943
(defun cross-join-bindings (field1 field2 &optional slice)
3944
(destructuring-bind (solution-list1 dimensions1) (rest field1)
3945
(destructuring-bind (solution-list2 dimensions2) (rest field2)
3946
(if (intersection dimensions1 dimensions2) ;; not a cross-join
3947
`(spocq.e:join ,field1 ,field2 ,@(rest slice))
3948
(let ((solutions (loop for solution1 in solution-list1
3949
append (loop for solution2 in solution-list2
3950
collect (append solution1 solution2)))))
3952
(destructuring-bind (&key (start 0) end) (rest slice)
3953
(setf solutions (subseq solutions start end))))
3954
`(spocq.a:|bindings| ,solutions ,(append dimensions1 dimensions2)))))))
3956
(defgeneric add-sparql-bindings (bindings sse)
3958
"rewrite a query sse form to intrduce the given bindings into a leaf.")
3959
(:method ((bindings cons) sse)
3960
(cond ((eq (first bindings) 'spocq.a:|bindings|) )
3961
((and (consp (first bindings)) (variable-p (caar bindings)))
3962
(setf bindings `(spocq.a:|bindings| (,(mapcar #'rest bindings))
3963
,(mapcar #'first bindings))))
3965
(error "invalid bindings: ~s" bindings)))
3966
(add-sparql-bindings-to-operator bindings (first sse) (rest sse)))
3967
(:method ((bindings null) sse)
3970
(defgeneric add-sparql-bindings-to-operator (bindings operator arguments)
3971
(:method (bindings (operator t) args)
3972
(error "form does not combine with bindings: ~s . ~s" operator args)))
3974
(macrolet ((def-sparql-transform (operator lambda-list &body body)
3975
`(defmethod add-sparql-bindings-to-operator (bindings (operator (eql ',operator)) parameters)
3976
(destructuring-bind ,lambda-list (cons bindings parameters)
3979
(def-sparql-transform spocq.a:|ask| (bindings field)
3980
`(spocq.a:|ask| ,(add-sparql-bindings bindings field)))
3982
(def-sparql-transform spocq.a:|bgp| (bindings &rest triples)
3983
`(spocq.a:|join| ,bindings (spocq.a:|bgp| ,@triples)))
3985
(def-sparql-transform spocq.a:|bindings| (bindings values variables)
3986
(cross-join-bindings bindings `(spocq.a:|bindings| ,values ,variables)))
3988
(def-sparql-transform spocq.a:|construct| (bindings field triples)
3989
`(spocq.a:|construct| ,(add-sparql-bindings bindings field) ,triples))
3991
(def-sparql-transform spocq.a:|describe| (bindings field subjects)
3992
`(spocq.a:|describe| ,(add-sparql-bindings bindings field) ,subjects))
3994
(def-sparql-transform spocq.a:|diff| (bindings field1 field2 test-expression)
3995
`(spocq.a:|join| ,bindings (spocq.a:|diff| ,field1 ,field2 ,test-expression)))
3997
(def-sparql-transform spocq.a:|distinct| (bindings field &rest args)
3998
`(spocq.a:|distinct| ,(add-sparql-bindings bindings field) ,@args))
4000
(def-sparql-transform spocq.a:|extend| (bindings field variable value-expression)
4001
`(spocq.a:|extend| ,(add-sparql-bindings bindings field) ,variable ,value-expression))
4003
(def-sparql-transform spocq.a:|filter| (bindings field test-expression)
4004
`(spocq.a:|filter| ,(add-sparql-bindings bindings field) ,test-expression))
4006
(def-sparql-transform spocq.a:|graph| (bindings name group-graph-pattern)
4007
`(spocq.a:|join| ,bindings (spocq.a:|graph| ,name ,group-graph-pattern)))
4009
(def-sparql-transform spocq.a:|join| (bindings field1 field2)
4010
`(spocq.a:|join| ,field1 ,(add-sparql-bindings bindings field2)))
4012
(def-sparql-transform spocq.a:|leftjoin| (bindings field1 field2 &rest args)
4013
;; augment the base field
4014
`(spocq.a:|leftjoin| ,(add-sparql-bindings bindings field1) ,field2 ,@args))
4016
(def-sparql-transform spocq.a:|minus| (bindings field1 field2)
4017
`(spocq.a:|join| ,bindings (spocq.a:|minus| ,field1 ,field2)))
4019
(def-sparql-transform spocq.a:|null| (bindings dimensions)
4020
;; do not reduce immediately
4021
`(spocq.a:|join| ,bindings (spocq.a:|null| ,dimensions)))
4023
(def-sparql-transform spocq.a:|order| (bindings field order-expression-list)
4024
`(spocq.a:|order| ,(add-sparql-bindings bindings field) ,order-expression-list))
4026
(def-sparql-transform spocq.a:|project| (bindings field variables &rest args)
4027
`(spocq.a:|project| ,(add-sparql-bindings bindings field) ,variables ,@args))
4029
(def-sparql-transform spocq.a:|reduced| (bindings field &rest args)
4030
`(spocq.a:|reduced| ,(add-sparql-bindings bindings field) ,@args))
4032
(def-sparql-transform spocq.a:|select| (bindings field variables &rest args)
4033
`(spocq.a:|select| ,(add-sparql-bindings bindings field) ,variables ,@args))
4035
(def-sparql-transform spocq.a:|service| (bindings name group-graph-pattern &rest args)
4036
`(spocq.a:|service| ,name ,(add-sparql-bindings bindings group-graph-pattern) ,@args))
4038
(def-sparql-transform spocq.a:|slice| (bindings field &rest args)
4039
`(spocq.a:|slice| ,(add-sparql-bindings bindings field) ,@args))
4041
(def-sparql-transform spocq.a:|table| (bindings &rest args)
4042
`(spocq.a:|join| ,bindings (spocq.a:|table| ,@args)))
4044
(def-sparql-transform spocq.a:|union| (bindings field1 field2 test-expression)
4045
`(spocq.a:|join| ,bindings (spocq.a:|union| ,field1 ,field2 ,test-expression)))
4048
;;; (add-sparql-bindings '(spocq.a:|bindings| (?::a) ((1) (2))) (parse-sparql "select * where {?s ?p ?o}"))
4049
;;; (add-sparql-bindings '((?::a . 1) (?::b .2)) (parse-sparql "select * where {?s ?p ?o}"))
4051
(defun walk-tree (function tree)
4052
"Walk the tree under the control of the mapped function.
4053
If it returns true, continue, otherwise return. Leaves the tree unmodified."
4055
(declare (dynamic-extent function))
4057
(flet ((map-element (e) (walk-tree function e)))
4058
(declare (dynamic-extent #'map-element))
4059
(when (funcall function tree)
4061
(cons (walk-tree function (first tree))
4062
(walk-tree function (rest tree)))
4063
(vector (map nil #'map-element tree))))))
4066
(defun merge-property-lists (&rest lists)
4067
(flet ((do-merge (l1 l2)
4068
(loop for (property value) on l2 by #'cddr
4069
when (eq (getf l1 property lists) lists)
4070
do (progn (push value l1)
4071
(push property l1)))
4073
(reduce #'do-merge lists)))
4074
;; (merge-property-lists '(:a 1 :b 2) '(:b 3 :c 3) '(:d 4 :a 4))
4076
(:documentation "blank nodes and variables in patterns and data"
4077
"quad/triple patterns appear in three interpretation contexts:
4078
- graph/bgp matching patterns
4079
these may have constants, blank nodes, and variables - both
4080
distinguished and non. the match binds distinguished variables
4081
but neither non-distinguished nor blank nodes.
4082
- graph-triple patterns specified insertion or deletion in
4083
combination with bindings from a where clause
4084
in both contexts, these may have distinguished variables to
4085
accept the bindings from the where clause.
4086
for deletion, neither blank nodes no non-distinguished variables
4087
are permitted, as bindings are required.
4088
for insertion blank nodes and non-distinguished variables are
4089
permitted and are mapped to new nodes per pattern invocation.
4090
- graph/triple data specified in-line for insertion or deletion
4091
in both contexts, these may _not_ have distinguished variables.
4092
for deletion, they may not have blank nodes or non-distinguished
4093
variables while for insertion, blank nodes are mapped to new nodes
4094
per pattern invocation.
4096
the constraints are applied in the delete/insert/update operators
4097
and the logic to generate new blank nodes is in the code which
4098
computes modify operators and that which interns fields.
4100
cf. http://www.w3.org/TR/sparql11-query/#sparqlGrammar
4102
8. The QuadData and QuadPattern rules both use rule Quads. The rule QuadData,
4103
used in INSERT DATA and DELETE DATA, must not allow variables in the quad
4105
9. Blank node syntax is not allowed in DELETE WHERE, the DeleteClause for
4106
DELETE, nor in DELETE DATA.")
4109
(defun validate-quad-data (data mode)
4110
"Require that the data comprise constants or new blank node indicators only.
4111
if anything else is present, signal an error."
4112
(flet ((insertion-term-p (term)
4113
(not (distinguished-variable-p term)))
4114
(deletion-term-p (term)
4115
(not (or (variable-p term)
4116
(spocq:blank-node-p term)))))
4117
(let ((term-predicate (ecase mode
4118
(:insert #'insertion-term-p)
4119
(:delete #'deletion-term-p))))
4120
(labels ((validate-data (data)
4123
(cons (case (first data)
4125
(map nil #'validate-data (rest data)))
4127
(destructuring-bind (iri . statements) (rest data)
4128
(assert (iri-p iri) ()
4129
"Graph designator must be an iri: ~s." iri)
4130
(map nil #'validate-data statements)))
4131
((spocq.a:|triple| spocq.a:|quad|)
4132
(unless (every term-predicate (statement-terms data))
4133
(spocq.e:request-error "Statement terms may not be variables: ~a." data)))
4135
(if (consp (first data))
4136
(loop for datum in data
4137
do (validate-data datum))
4138
(error "Invalid quad data: ~s." data)))))
4140
(error "Invalid quad data: ~s." data)))))
4141
(validate-data data)))))
4144
(defun validate-quad-pattern (pattern mode)
4145
"Require that, for deletion, the pattern may not include nondistinguished variables or
4146
blank node indicators.
4147
if anything else is present, signal an error."
4148
(flet ((deletion-term-p (term)
4149
(not (or (undistinguished-variable-p term)
4150
(spocq:blank-node-p term)))))
4151
(let ((term-predicate (ecase mode
4152
(:insert #'identity)
4153
(:delete #'deletion-term-p))))
4154
(labels ((validate-pattern (pattern)
4157
(cons (case (first pattern)
4159
(map nil #'validate-pattern (rest pattern)))
4161
(destructuring-bind (var-or-iri . statements) (rest pattern)
4162
(assert (or (iri-p var-or-iri) (variable-p var-or-iri)) ()
4163
"Graph designator must be a variable or an iri: ~s." var-or-iri)
4164
(map nil #'validate-pattern statements)))
4165
((spocq.a:|triple| spocq.a:|quad|)
4166
(unless (every term-predicate (statement-terms pattern))
4167
(spocq.e:request-error "Statement terms must be constant or variable: ~a." pattern)))
4169
(if (consp (first pattern))
4170
(loop for datum in pattern
4171
do (validate-pattern datum))
4172
(error "Invalid quad pattern: ~s." pattern)))))
4174
(error "Invalid quad pattern: ~s." pattern)))))
4175
(validate-pattern pattern)))))
4178
(defun symbol-uri-namestring (symbol)
4179
"retrieve namestring, create if not present"
4180
(de.setf.resource.implementation::symbol-uri-namestring symbol))
4182
(defun (setf symbol-uri-namestring) (uri-namestring symbol)
4183
(setf (de.setf.resource.implementation::symbol-uri-namestring symbol) uri-namestring))
4186
(defun get-symbol-uri-namestring (symbol)
4187
"retrieve, but do not create, the namestring.
4188
should be present for iri only."
4189
(get symbol :namestring))
4191
(defun symbol-term-id (symbol)
4192
(get symbol 'symbol-term-id))
4194
(defun (setf symbol-term-id) (id symbol)
4195
(setf (get symbol 'symbol-term-id) id))
4197
(defun symbol-string-id (symbol)
4198
"return the string dictionary id for the string which is the
4199
lexical form of the respective type name"
4200
(get symbol 'symbol-string-id))
4202
(defun (setf symbol-string-id) (id symbol)
4203
(setf (get symbol 'symbol-string-id) id))
4205
(defvar *foreign-string-addresses* (make-hash-table :test 'eql))
4207
(defun datatype-foreign-string (datatype-symbol)
4208
(or (get datatype-symbol 'foreign-string)
4209
(error "No foreign string present: ~s." datatype-symbol)))
4211
(defun (setf datatype-foreign-string) (pointer datatype-symbol)
4212
(setf (gethash (cffi:pointer-address pointer) *foreign-string-addresses*) datatype-symbol)
4213
(setf (get datatype-symbol 'foreign-string) pointer))
4215
(defun contingently-foreign-free (string-pointer)
4216
(when string-pointer
4217
(cond ((cffi:null-pointer-p string-pointer) nil)
4218
((gethash (cffi:pointer-address string-pointer) *foreign-string-addresses*) nil)
4219
(t (cffi:foreign-free string-pointer) t))))
4221
(defgeneric initialize-store (repository-class store-uri store-library)
4222
(:method ((repository-type symbol) store-uri store-library)
4223
(initialize-store (find-class repository-type) store-uri store-library))
4224
(:method ((repository-class class) store-uri store-library)
4225
(c2mop:ensure-finalized repository-class)
4226
(initialize-store (allocate-instance repository-class) store-uri store-library)))
4228
(defgeneric reinitialize-store (repository-class)
4229
(:method ((repository-type symbol))
4230
(reinitialize-store (find-class repository-type)))
4231
(:method ((repository-class class))
4232
(c2mop:ensure-finalized repository-class)
4233
(reinitialize-store (allocate-instance repository-class)))
4234
(:method ((repository t))
4238
(defun initialize-uri-symbols ()
4239
(clrhash *foreign-string-addresses*)
4240
(dolist (package *iri-packages* t) (initialize-package-uri-symbols package)))
4242
(defgeneric initialize-package-uri-symbols (package)
4243
(:method ((package package))
4244
(flet ((initialize-symbol (symbol lexical-form)
4245
(let ((foreign-string (cffi:foreign-string-alloc lexical-form)))
4246
(export symbol (symbol-package symbol))
4247
(setf (datatype-foreign-string symbol) foreign-string)
4248
(cond ((boundp symbol)
4249
(unless (eq (symbol-value symbol) symbol)
4250
(log-warn "Invalid uri symbol binding: ~s -> ~s." symbol (symbol-value symbol))))
4252
(eval `(defconstant ,symbol ',symbol)))))))
4253
(let ((package-symbol (intern "" package)))
4254
(initialize-symbol package-symbol (package-name package)))
4255
(with-package-iterator (get-next-symbol package :external :internal)
4256
(loop (multiple-value-bind (next-p symbol) (get-next-symbol)
4257
(unless next-p (return))
4258
;; ensure namestring is present
4259
(initialize-symbol symbol (de.setf.resource.implementation::symbol-uri-namestring symbol))))))
4261
(:method ((designator string))
4262
(initialize-package-uri-symbols (find-package designator))))
4264
(describe 'spocq.a:|describe|)
4265
(initialize-uri-symbols)
4266
(describe 'spocq.a:|describe|)
4268
(defparameter *true-all-context-term-number* nil)
4269
(defparameter *true-default-context-term-number* nil)
4270
(defparameter *true-named-context-term-number* nil)
4271
(defparameter *true-none-context-term-number* nil)
4272
(defparameter *none-context-term-number* -4)
4274
(defun initialize-interned-terms ()
4275
"Reset the lexical->spocq registry and the rdfcache<->spocq term registries.
4276
For each vocabulary term (uri) register all three equivalences between the symbol and its term number,
4277
and cache its lexical form and foreign string."
4279
(clrhash *lexical->spocq-term-registry*)
4280
(clrhash *spocq->store-term-registry*)
4281
(clrhash *store->spocq-term-registry*)
4282
(initialize-uri-symbols)
4285
(cffi:with-foreign-objects ((%term '(:struct rdfcache::term)))
4286
(rdfcache::%clear-term %term)
4287
(labels ((bind-term-number (symbol term-number)
4288
(setf (gethash term-number *store->spocq-term-registry*) symbol
4289
(gethash symbol *spocq->store-term-registry*) term-number
4290
(symbol-term-id symbol) term-number))
4291
(register-term (symbol lexical-form)
4292
(setf (gethash (list :uri lexical-form) *lexical->spocq-term-registry*) symbol)
4294
(cffi:with-foreign-string (%lexical-form lexical-form)
4295
(rdfcache:initialize-term %term :uri %lexical-form :language nil :datatype nil)
4296
(let ((id (rdfcache::lookup-term-number %term)))
4298
(bind-term-number symbol id))
4300
(log-warn "Interning missing term <~a>." lexical-form)
4301
(bind-term-number symbol
4302
(rdfcache:intern-term nil
4303
(cffi:foreign-slot-value %term '(:struct rdfcache::term) 'rdfcache::type)
4304
(cffi:foreign-slot-value %term '(:struct rdfcache::term) 'rdfcache::value)
4305
(cffi:foreign-slot-value %term '(:struct rdfcache::term) 'rdfcache::language)
4306
(cffi:foreign-slot-value %term '(:struct rdfcache::term) 'rdfcache::datatype)))))))))
4308
(dolist (package *iri-packages*)
4309
(let ((package-symbol (intern "" package)))
4310
(register-term package-symbol (package-name package)))
4311
(with-package-iterator (get-next-symbol package :external :internal)
4312
(loop (multiple-value-bind (next-p symbol) (get-next-symbol)
4313
(unless next-p (return))
4314
(let ((namestring (symbol-uri-namestring symbol)))
4315
(register-term symbol namestring)
4316
(setf (symbol-string-id symbol) (rlmdb::string-dictionary-get namestring)))))))))
4318
(log-error "Terms not present in the store: ~a."
4319
(with-output-to-string (stream)
4320
(dolist (failure failures)
4321
(write-char #\space stream)
4322
(encode-turtle-object failure stream))))
4323
(cerror "Continue without them."
4324
"Cannot locate ~d/~d terms in the store." (length failures) count))
4325
(flet ((reregister-term (symbol id)
4326
(unless (eql (symbol-term-id symbol) id)
4327
(log-info "reregister: ~s: ~s." symbol id)
4328
;; leave the original id -> symbol map entry
4329
(setf (gethash id *store->spocq-term-registry*) symbol
4330
(gethash symbol *spocq->store-term-registry*) id
4331
(symbol-term-id symbol) id))))
4332
(unless *true-all-context-term-number*
4333
(setf *true-all-context-term-number* (symbol-term-id '|urn:dydra|:|all|)))
4334
(reregister-term '|urn:dydra|:|all| rdfcache:*all-context-number*)
4335
(unless *true-default-context-term-number*
4336
(setf *true-default-context-term-number* (symbol-term-id '|urn:dydra|:|default|)))
4337
(reregister-term '|urn:dydra|:|default| rdfcache:*default-context-number*)
4338
(unless *true-named-context-term-number*
4339
(setf *true-named-context-term-number* (symbol-term-id '|urn:dydra|:|named|)))
4340
(reregister-term '|urn:dydra|:|named| rdfcache:*named-context-number*)
4341
(unless *true-none-context-term-number*
4342
(setf *true-none-context-term-number* (symbol-term-id '|urn:dydra|:|none|)))
4343
(reregister-term '|urn:dydra|:|none| *none-context-term-number*))
4345
;;; (initialize-interned-terms)
4348
;; (gethash '(:uri "http://www.w3.org/2001/XMLSchema#yearMonthDuration") *lexical->spocq-term-registry*)
4350
#+(or) ;; did not handle rdf:_1 &co
4351
(defun construct-prefixed-name (item)
4352
(let* ((colon-position (or (position #\: item) (error "invalid pname_ld syntax: ~s." item)))
4353
(pn_prefix (subseq item 0 colon-position))
4354
(pn_local (subseq item (1+ colon-position)))
4355
(namespace-uri (prefix-namespace pn_prefix))
4356
;; need to cache these
4357
(namespace-namestring (term-lexical-form namespace-uri)))
4358
(if (member namespace-namestring *iri-package-names* :test #'string-equal)
4359
(or (find-symbol pn_local namespace-namestring)
4360
(when *strict-vocabulary-terms*
4361
(error "Invalid vocabulary resource: ~s." item))
4362
(merge-and-intern-iri (concatenate 'string namespace-namestring pn_local)))
4363
(merge-and-intern-iri (concatenate 'string namespace-namestring pn_local)))))
4365
(defun construct-prefixed-name (item)
4366
(let* ((colon-position (or (position #\: item) (error "invalid pname_ld syntax: ~s." item)))
4367
(pn_prefix (subseq item 0 colon-position))
4368
(pn_local (subseq item (1+ colon-position)))
4369
(namespace-uri (prefix-namespace pn_prefix))
4370
;; need to cache these
4371
(namespace-namestring (term-lexical-form namespace-uri)))
4372
;; the un-escape from pn_local bnf must be performed here as the item here is itself a terminal
4373
(setf pn_local (remove #\\ pn_local))
4374
(intern-iri (concatenate 'string namespace-namestring pn_local))))
4377
(defparameter *uri-unreserved-characters*
4378
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~")
4379
(defparameter *uri-unreserved-bytes*
4380
(map 'vector #'char-code *uri-unreserved-characters*))
4382
(defun encode-for-uri (string)
4383
"Return the string with any reserved characters replaced by a string of characters which
4384
encode the 'corresponding percent-encoded US-ASCII octet', as specified by RFC 3986. This
4385
implements the XQuery function [fn:encode-for-uri](http://www.w3.org/TR/xpath-functions/#func-encode-for-uri)
4386
for use in SPARQL's encode_fo_uri operator."
4387
(if (position-if #'(lambda (c) (not (find c *uri-unreserved-characters*))) string)
4388
(let* ((utf8-bytes (encode-string string :utf-8))
4389
(utf8-end (length utf8-bytes))
4390
(result (make-string (+ utf8-end (* 2 (count-if #'(lambda (b) (not (find b *uri-unreserved-bytes*)))
4392
(result-position 0))
4393
(loop for byte across utf8-bytes
4394
if (position byte *uri-unreserved-bytes*)
4395
do (setf (aref result result-position) (code-char byte)
4396
result-position (1+ result-position))
4397
else do (multiple-value-bind (high low) (truncate byte 16)
4398
(setf (aref result result-position) #\%)
4399
(setf (aref result (incf result-position)) (aref *uri-unreserved-characters* high))
4400
(setf (aref result (incf result-position)) (aref *uri-unreserved-characters* low))
4401
(incf result-position)))
4405
;;; cache literal expression for round-tripping
4407
(defun literal-string (value)
4408
"For literals which have internal representations, return the external representation
4409
if one was present."
4410
(gethash value *literal-strings*))
4412
(defun (setf literal-string) (string value)
4413
"For literals which have internal representations, return the external representation
4414
if one was present."
4415
(setf (gethash value *literal-strings*) string))
4418
(defgeneric construct-iri (lexical-form)
4419
(:method ((lexical-form string))
4420
(construct-term :uri lexical-form nil nil))
4421
(:method ((iri spocq:iri))
4423
(:method ((iri uuid:uuid))
4424
(construct-iri (prin1-to-string iri)))
4425
(:method ((uri puri:uri))
4426
(construct-iri (with-output-to-string (stream) (puri:render-uri uri stream)))))
4428
(defgeneric intern-iri-normalized (lexical-form)
4429
(:method ((lexical-form string))
4430
(when (char= #\< (char lexical-form 0)) ;; coerce <...> forms
4431
(assert (char= #\> (char lexical-form (1- (length lexical-form)))))
4432
(setf lexical-form (subseq lexical-form 1 (1- (length lexical-form)))))
4433
(intern-iri lexical-form))
4437
(defgeneric intern-iri (lexical-form)
4438
(:method ((lexical-form string))
4439
(intern-term-aspects :uri lexical-form nil nil))
4440
(:method ((iri spocq:iri))
4441
"Given an iri instance, reintern it to ensure identity in the given context."
4442
(intern-term-aspects :uri (spocq:iri-lexical-form iri) nil nil))
4443
(:method ((uuid uuid:uuid))
4445
(:method ((uri puri:uri))
4446
(intern-iri (with-output-to-string (stream) (puri:render-uri uri stream))))
4447
(:method ((iri null))
4448
;; allow null -> null
4450
(:method ((iri symbol))
4451
(assert (get-symbol-uri-namestring iri) () "Invalid symbol iri: ~s." iri)
4453
(:method ((object t))
4454
(error "Invalid iri form: ~s." object)))
4456
(defun (setf intern-iri) (value lexical-form)
4457
(setf (gethash (list :uri lexical-form) *lexical->spocq-term-registry*) value))
4459
(defgeneric ensure-iri (object)
4460
(:method ((iri spocq:iri)) iri)
4461
(:method ((object null)) (error "Invalid iri form: ~s." object))
4462
(:method ((object t)) (intern-iri object)))
4464
(defgeneric iri-equal (iri1 iri2)
4465
(:method ((iri1 symbol) (iri2 symbol))
4467
(:method ((iri1 t) (iri2 t))
4469
(string-equal (iri-lexical-form iri1) (iri-lexical-form iri2)))))
4471
(defgeneric intern-sub-iri (base extension)
4472
(:method ((base string) (extension string))
4473
(intern-iri (concatenate 'string base extension)))
4474
(:method ((base spocq:iri) (extension string))
4475
(intern-sub-iri (spocq:iri-lexical-form base) extension)))
4478
(defgeneric intern-uuid (object)
4479
(:method ((lexical-form string))
4480
(intern-iri (cond ((string-equal "urn:uuid:" lexical-form :end2 (min (length lexical-form) 9))
4482
((string-equal "uuid:" lexical-form :end2 (min (length lexical-form) 5))
4483
(concatenate 'string "urn:" lexical-form))
4485
(concatenate 'string "urn:uuid:" lexical-form)))))
4486
(:method ((uuid uuid:uuid))
4487
(intern-iri (prin1-to-string uuid))))
4490
(cffi:define-foreign-library libuuid
4491
(:unix (:or "libuuid.so.1" "libuuid.so"))
4492
(t (:default "libuuid")))
4493
(CFFI:LOAD-FOREIGN-LIBRARY 'libuuid)
4494
(cffi:defcfun ("uuid_generate_time" %uuid-generate-time) :pointer (uuid :pointer))
4495
(cffi:defcfun ("uuid_generate_random" %uuid-generate-random) :pointer (uuid :pointer))
4496
(cffi:defcfun ("uuid_time" %uuid-time) :uint64 (uuid :pointer) (timeval :pointer))
4497
(cffi:defcstruct timeval
4500
(cffi:defcstruct v1-uuid
4503
(time_hi_and_version :uint16)
4508
(defun make-uuid-vector ()
4509
(make-array +uuid-length+ :element-type '(unsigned-byte 8)))
4510
(defun uuid-vector-p (object)
4511
(typep object '(array (unsigned-byte 8) (16))))
4512
(deftype uuid-vector () '(satisfies uuid-vector-p))
4514
(defparameter *uuid-timeout* 1)
4516
(defun %uuid-to-string (%uuid &optional (uuid (make-string 36 :initial-element #\-) uuid-s))
4517
(let ((hex "0123456789ABCDEF"))
4518
(labels ((transcribe-byte (%uuid uuid from to)
4519
(let ((byte (cffi:mem-aref %uuid :uint8 from)))
4520
(setf (char uuid to) (char hex (ash byte -4))
4521
(char uuid (1+ to)) (char hex (logand byte #x0f)))))
4522
(transcribe-segment (%uuid uuid from to count)
4523
(loop for i below count
4526
do (transcribe-byte %uuid uuid from to)))
4527
(transcribe-uuid (%uuid uuid)
4528
(transcribe-segment %uuid uuid 0 0 4)
4529
(transcribe-segment %uuid uuid 4 9 2)
4530
(transcribe-segment %uuid uuid 6 14 2)
4531
(transcribe-segment %uuid uuid 8 19 2)
4532
(transcribe-segment %uuid uuid 10 24 6)))
4534
(loop for to in '(8 13 18 23) do (setf (char uuid to) #\-)))
4535
(transcribe-uuid %uuid uuid)
4538
(defun make-v1-uuid-string (&optional (uuid (make-string 36 :initial-element #\-)))
4539
(bt:with-timeout (*uuid-timeout*)
4540
(cffi:with-foreign-objects ((%uuid '(:struct v1-uuid)))
4541
(%uuid-generate-time %uuid)
4542
(%uuid-to-string %uuid uuid))))
4544
(defun make-v1-uuid-array (&optional (vector (make-array 16 :element-type '(unsigned-byte 8))))
4545
(bt:with-timeout (*uuid-timeout*)
4546
(cffi:with-foreign-objects ((%uuid '(:struct v1-uuid)))
4547
(%uuid-generate-time %uuid)
4548
(loop for i below 16
4549
do (setf (aref vector i) (cffi:mem-aref %uuid :uint8 i)))
4552
(defun make-v4-uuid-string (&optional (uuid (make-string 36 :initial-element #\-)))
4553
(cffi:with-foreign-objects ((%uuid '(:struct v1-uuid)))
4554
(%uuid-generate-random %uuid)
4555
(%uuid-to-string %uuid uuid)))
4557
(defgeneric uuid-timestamp (uuid)
4558
(:method ((uuid uuid:uuid))
4559
(uuid-timestamp (uuid:uuid-to-byte-array uuid)))
4560
(:method ((uuid vector))
4561
(rdfcache::with-uuid (%uuid)
4562
(loop for i below 16
4563
do (setf (cffi:mem-aref %uuid :uint8 i) (aref uuid i)))
4564
(cffi:with-foreign-object (%timeval '(:struct timeval))
4565
(%uuid-time %uuid %timeval)
4566
(+ (* 1000000 (cffi:foreign-slot-value %timeval '(:struct timeval) 'tv_sec))
4567
(cffi:foreign-slot-value %timeval '(:struct timeval) 'tv_usec)))))
4568
(:method ((uuid string))
4569
(uuid-timestamp (string-to-uuid uuid (make-uuid-vector)))))
4570
(defmethod uuid-to-string ((uuid vector))
4571
(with-output-to-string (stream) (cl-user::format-uuid stream uuid)))
4572
(defmethod cl-user::format-uuid (stream uuid &optional colon at)
4573
(declare (ignore colon at))
4574
(flet ((output-bytes (offset count)
4575
(loop for i below count for index from offset
4576
do (format stream "~(~2,'0x~)" (aref uuid index)))))
4578
(write-char #\- stream)
4580
(write-char #\- stream)
4582
(write-char #\- stream)
4584
(write-char #\- stream)
4585
(output-bytes 10 6 )))
4586
(defmethod string-to-uuid (string (uuid vector))
4587
(setf string (uuid-string string))
4590
until (>= to (length uuid))
4591
do (if (char= (char string from) #\-)
4593
(setf (aref uuid (shiftf to (1+ to)))
4594
(parse-integer string :start from :end (incf from 2) :radix 16))))
4596
(defun string-to-uuid-vector (string)
4597
(string-to-uuid string (make-uuid-vector)))
4598
(defgeneric %encode-uuid (uuid %uuid)
4599
(:documentation "encode any of various uuid forms into a 16-byte foreign array")
4600
(:method ((uuid string) (%uuid SB-SYS:SYSTEM-AREA-POINTER))
4601
(%encode-uuid (string-to-uuid uuid (make-uuid-vector)) %uuid))
4602
(:method ((uuid vector) (%uuid SB-SYS:SYSTEM-AREA-POINTER))
4603
(loop for i below 16
4604
do (setf (cffi:mem-aref %uuid :uint8 i) (aref uuid i)))
4606
(defun %decode-uuid (%uuid uuid)
4607
(loop for i below 16
4608
do (setf (aref uuid i) (cffi:mem-aref %uuid :uint8 i)))
4610
;;; (let ((v (make-v1-uuid-array))) (equalp v (string-to-uuid (uuid-to-string v) (make-array 16 :element-type '(unsigned-byte 8)))))
4613
(defun cons-uuid-symbol ()
4614
(make-symbol (make-v1-uuid-string)))
4615
;; (cons-uuid-symbol)
4617
(defun cons-v1-uuid ()
4618
(spocq:make-uuid (concatenate 'string "urn:uuid:" (make-v1-uuid-string))))
4620
(defgeneric uuid-string (object)
4621
(:method ((uuid string))
4622
(cond ((string-equal "urn:" uuid :end2 4)
4624
((string-equal "uuid:" uuid :end2 5)
4628
(:method ((uuid spocq:uuid))
4629
(subseq (spocq:uuid-lexical-form uuid) 9)))
4631
(defgeneric v1-uuid-timestamp (uuid)
4632
(:method ((uuid string))
4633
(v1-uuid-timestamp (string-to-uuid uuid (make-uuid-vector))))
4634
(:method ((uuid vector))
4635
;; (v1-uuid-timestamp (uuid:byte-array-to-uuid uuid))
4636
(let ((timestamp 0))
4637
(setf (ldb (byte 32 0) timestamp) (de.setf.utility.codecs:buffer-get-unsigned-byte-32 uuid 0))
4638
(setf (ldb (byte 16 32) timestamp) (de.setf.utility.codecs:buffer-get-unsigned-byte-16 uuid 4))
4639
(setf (ldb (byte 12 48) timestamp) (ldb (byte 12 0) (de.setf.utility.codecs:buffer-get-unsigned-byte-16 uuid 6)))
4642
(:method ((uuid uuid:uuid))
4643
(let ((timestamp 0))
4644
(setf (ldb (byte 32 0) timestamp) (uuid::time-low uuid))
4645
(setf (ldb (byte 16 32) timestamp) (uuid::time-mid uuid))
4646
(setf (ldb (byte 12 48) timestamp) (ldb (byte 12 0) (uuid::time-high uuid)))
4649
(let ((uuid (make-array 16 :element-type '(unsigned-byte 8)
4650
:initial-contents '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))))
4651
(time (dotimes (x 1000000)
4652
(v1-uuid-timestamp uuid))))
4653
;;; buffer-get : 0.171/0bytes
4654
;;; uuid:uuid : 0.853/80Mbytes
4657
(defgeneric (setf v1-uuid-timestamp) (timestamp uuid)
4658
(:method (timestamp (uuid vector))
4659
(flet ((set-high (value) (de.setf.utility.codecs:buffer-set-unsigned-byte-16 uuid value 6))
4660
(set-mid (value) (de.setf.utility.codecs:buffer-set-unsigned-byte-16 uuid value 4))
4661
(set-low (value) (de.setf.utility.codecs:buffer-set-unsigned-byte-32 uuid value 0)))
4662
(set-high (ldb (byte 12 48) timestamp))
4663
(set-mid (ldb (byte 16 32) timestamp))
4664
(set-low (ldb (byte 32 0) timestamp)))
4666
(let ((uuid:uuid (uuid:byte-array-to-uuid uuid)))
4667
(setf (v1-uuid-timestamp uuid:uuid) timestamp)
4668
(replace uuid (uuid:uuid-to-byte-array uuid:uuid) :end1 10))
4670
(:method (timestamp (uuid uuid:uuid))
4671
(setf (uuid::time-low uuid) (ldb (byte 32 0) timestamp))
4672
(setf (uuid::time-mid uuid) (ldb (byte 16 32) timestamp))
4673
(setf (uuid::time-high uuid) (ldb (byte 12 48) timestamp))
4676
(defgeneric uuid-node (uuid)
4677
(:method ((uuid string))
4678
(uuid-node (string-to-uuid uuid (make-uuid-vector))))
4679
(:method ((uuid uuid:uuid))
4681
(:method ((uuid spocq:uuid))
4682
(uuid-node (string-to-uuid (spocq:uuid-lexical-form uuid) (make-uuid-vector))))
4683
(:method ((uuid vector))
4684
(de.setf.utility.codecs::buffer-get-unsigned-byte-48 uuid 10)))
4686
(defgeneric (setf uuid-node) (node uuid)
4687
;; does not permit changes to a uuid term
4688
(:method (node (uuid uuid:uuid))
4689
(setf (uuid::node uuid) node))
4690
(:method (node (uuid vector))
4691
(de.setf.utility.codecs::buffer-set-unsigned-byte-48 uuid node 10)))
4694
(defgeneric intern-sha256-urn (object)
4695
(:method ((lexical-form string))
4696
(intern-iri (cond ((string-equal "urn:hash:" lexical-form :end2 (min (length lexical-form) 9))
4698
((string-equal "hash:" lexical-form :end2 (min (length lexical-form) 5))
4699
(concatenate 'string "urn:" lexical-form))
4701
(concatenate 'string "urn:hash::sha256:" lexical-form))))))
4702
(defgeneric sha256-urn-hash (object)
4703
(:method ((object spocq:sha256-urn))
4704
(subseq (spocq:sha256-urn-lexical-form object) (load-time-value (length "urn:hash::sha256:"))))
4705
(:method ((object string))
4706
(ecase (length object)
4707
(#.(+ (length "urn:hash::sha256:") (/ 256 4)) (subseq object (load-time-value (length "urn:hash::sha256:"))))
4711
(defgeneric intern-file-url (object)
4712
(:method ((pathname pathname))
4713
(intern-file-url (namestring pathname)))
4714
(:method ((pathname string))
4715
(intern-iri (cond ((string-equal "file:" pathname :end2 (min (length pathname) 5))
4716
;; it need not have a host
4719
(concatenate 'string "file://" pathname))))))
4721
(defgeneric file-url-pathname (object)
4722
(:method ((url spocq:file-url))
4723
(multiple-value-bind (host path)
4724
(parse-file-url-host-and-path (spocq:iri-lexical-form url))
4725
(declare (ignore host))
4726
(assert (stringp path) ()
4727
"Invalid file url: ~s" url)
4729
;;; (file-url-pathname (intern-file-url #p"/tmp/test"))
4730
;;; (file-url-pathname (intern-file-url #p"tmp/test"))
4732
(defgeneric merge-and-intern-iri (iri-namestring &key base-iri)
4733
(:method ((iri t) &rest args)
4734
(declare (dynamic-extent args))
4735
(apply #'merge-and-intern-iri (iri-lexical-form iri) args))
4736
(:method ((iri-namestring string) &key (base-iri (base-iri)))
4737
;; base iri should always have a value
4738
(when (and base-iri (not (position #\: iri-namestring)))
4739
(let* ((puri::*parse-uri-string* #'puri::parse-iri-string)
4740
(puri::*escape-uri-string* nil) ; do not escape when parsing the components
4741
(puri (puri:merge-uris iri-namestring (iri-lexical-form base-iri))))
4742
(setf iri-namestring (with-output-to-string (stream) (puri:render-uri puri stream)))))
4743
(intern-term-aspects :uri iri-namestring nil nil)))
4745
(defun intern-blank-node (label)
4746
(intern-term-aspects :node label nil nil))
4748
(defun intern-literal (lexical-form datatype)
4749
(intern-term-aspects :literal lexical-form datatype nil))
4751
(defun intern-plain-literal (lexical-form language-tag)
4752
(intern-term-aspects :literal lexical-form nil (string language-tag)))
4755
(defun intern-term-aspects (term-type lexical-form datatype language-tag)
4756
(let ((key (list* term-type lexical-form (or datatype language-tag))))
4757
(declare (dynamic-extent key))
4758
(or (gethash key *lexical->spocq-term-registry*)
4759
(setf (gethash (copy-list key) *lexical->spocq-term-registry*)
4760
(construct-term term-type lexical-form datatype language-tag)))))
4763
(defun unintern-term-aspects (term-type lexical-form datatype language-tag)
4764
(let ((key (list* term-type lexical-form (or datatype language-tag))))
4765
(declare (dynamic-extent key))
4766
(remhash key *lexical->spocq-term-registry*)))
4768
(defun intern-term-aspects-copy (term-type lexical-form datatype language-tag)
4769
(let ((key (list* term-type lexical-form (or datatype language-tag))))
4770
(declare (dynamic-extent key))
4771
(or (gethash key *lexical->spocq-term-registry*)
4772
(let ((new-key (list* term-type (copy-seq lexical-form)
4773
(cond (datatype (copy-seq datatype))
4774
(language-tag (copy-seq language-tag))))))
4775
(setf (gethash new-key *lexical->spocq-term-registry*)
4776
(construct-term term-type lexical-form datatype language-tag))))))
4779
(defvar *site-genid-uri-prefix* )
4781
(defvar *site-genid-uri-prefix-length* )
4783
(defun site-genid-uri-prefix ()
4784
(the string (if (boundp '*site-genid-uri-prefix*)
4785
*site-genid-uri-prefix*
4786
(setq *site-genid-uri-prefix*
4787
(concatenate 'string "http://" (site-name) "/.well-known/genid/")))))
4789
(defun site-genid-uri-prefix-length ()
4790
(the (integer 0) (if (boundp '*site-genid-uri-prefix-length*)
4791
*site-genid-uri-prefix-length*
4792
(setq *site-genid-uri-prefix-length* (length (site-genid-uri-prefix))))))
4794
(defun find-vocabulary-package (iri-lexical-form)
4795
(let ((iri-lexical-form-length (length iri-lexical-form)))
4796
(flet ((test-vocabulary-uri (base-uri)
4797
(let ((base-length (length base-uri)))
4798
(cond ((<= base-length iri-lexical-form-length)
4799
;; allow exact match
4800
(string-equal iri-lexical-form base-uri :end1 base-length))
4801
((and (= base-length (1+ iri-lexical-form-length))
4802
(string-equal iri-lexical-form base-uri :end1 (1- base-length)))
4803
(case (char base-uri iri-lexical-form-length)
4805
(declare (dynamic-extent #'test-vocabulary-uri))
4806
(find-if #'test-vocabulary-uri *iri-package-names*))))
4808
(defun vocabulary-identifier (context lexical-form)
4809
(let ((base-iri (etypecase context (string context) (package (package-name context))))
4810
(package (etypecase context (string (find-package context)) (package context))))
4812
(let* ((package-length (length base-iri))
4813
(lexical-length (length lexical-form)))
4814
(cond ((<= package-length lexical-length)
4815
(let ((local-part (subseq lexical-form package-length))
4817
(when (plusp (length local-part))
4818
(setf separator (char local-part 0))
4820
((#\/ #\# #\:) (setf local-part (subseq local-part 1)))))
4821
(let ((symbol (find-symbol local-part package)))
4822
(setf (get symbol 'separator) separator)
4824
((= package-length (1+ lexical-length))
4825
(let ((separator (char base-iri lexical-length)))
4826
(when (case separator
4828
(let ((symbol (find-symbol "" package)))
4829
(setf (get symbol 'separator) separator)
4832
(defparameter *uri-scheme-scanner*
4833
(cl-ppcre:create-scanner `(:sequence :start-anchor
4834
(:register (:alternation "file"
4837
"mqtt" "mqtts" ;; construction in server;extensions;mqtt.lisp
4839
"odbc" ;; construction in odbc-uri.lisp
4840
"postgresql" ;; same
4841
;; must call them out rather then allowing '[^:]*'
4842
"urn:uuid" "urn" "uuid"
4846
(defun parse-uri-scheme (lexical-form)
4847
(multiple-value-bind (whole strings) (cl-ppcre:scan-to-strings *uri-scheme-scanner* lexical-form)
4849
(intern (string-upcase (aref strings 0)) :keyword))))
4851
(defparameter *url-authority-scanner*
4852
(cl-ppcre:create-scanner `(:sequence :start-anchor
4853
(:alternation "http" "https")
4856
(:greedy-repetition 0 nil (:inverted-char-class #\/)))
4857
(:GREEDY-REPETITION 0 NIL :EVERYTHING)
4859
;;; ietf odbc uri scheme
4860
;;; odbc:[driver]//[user]:[password]@[host]:[port]/[database]/[tables]?[query]
4862
;;; odbc://host/database/table
4864
(defparameter *file-url-scanner*
4865
(cl-ppcre:create-scanner `(:sequence :start-anchor
4867
(:greedy-repetition 0 1
4870
(:greedy-repetition 0 nil
4871
(:inverted-char-class #\/)))))
4873
(:sequence (:greedy-repetition 0 1 #\/)
4874
(:GREEDY-REPETITION 0 NIL :EVERYTHING))))))
4875
;;; (parse-file-url-host-and-path "file://asdf.qwer")
4876
;;; (parse-file-url-host-and-path "file://asdf.qwer/path/path")
4877
;;; (parse-file-url-host-and-path "file:///path/path")
4878
;;; (parse-file-url-host-and-path "file:/path/path")
4879
;;; (parse-file-url-host-and-path "file:path/path")
4881
(defparameter *url-authority+path-scanner*
4882
(cl-ppcre:create-scanner `(:sequence :start-anchor
4883
(:register (:greedy-repetition 0 1 (:alternation "http" "https")))
4885
(:register (:greedy-repetition 0 nil (:inverted-char-class #\/)))
4886
(:greedy-repetition 0 1
4888
(:greedy-repetition 0 1
4892
(:sequence (:greedy-repetition 1 nil (:inverted-char-class #\/ #\? #\& #\# #\{))
4893
(:greedy-repetition 0 1
4895
(:greedy-repetition 0 nil (:inverted-char-class #\/ #\? #\& #\# #\{))))))))
4896
(:greedy-repetition 0 1
4899
(:greedy-repetition 1 nil (:inverted-char-class #\/ #\? #\& #\# #\{)))))
4901
(:greedy-repetition 0 1 #\/)
4902
(:register (:GREEDY-REPETITION 0 NIL :EVERYTHING)))))
4904
(defparameter *url-query-scanner*
4905
(cl-ppcre:create-scanner `(:sequence :start-anchor
4906
(:alternation "http" "https")
4908
(:greedy-repetition 0 nil (:inverted-char-class #\?))
4909
(:greedy-repetition 0 1
4912
(:greedy-repetition 0 nil (:inverted-char-class #\#)))))
4913
(:GREEDY-REPETITION 0 NIL :EVERYTHING)
4915
(defparameter *repository-id-scanner*
4916
(cl-ppcre:create-scanner `(:sequence :start-anchor
4917
(:register (:greedy-repetition 1 nil (:alternation :word-char-class #\- #\_ #\.)))
4919
(:register (:greedy-repetition 1 nil (:alternation :word-char-class #\- #\_ #\.)))
4920
(:greedy-repetition 0 1
4921
(:sequence (:char-class #\? #\/) (:register (:GREEDY-REPETITION 0 NIL :EVERYTHING))))
4925
(defparameter *view-repository-id-scanner*
4926
(cl-ppcre:create-scanner `(:sequence :start-anchor
4927
(:register (:greedy-repetition 1 nil (:alternation :word-char-class #\- #\_ #\.)))
4929
(:register (:greedy-repetition 1 nil (:alternation :word-char-class #\- #\_ #\.)))
4931
(:register (:greedy-repetition 1 nil (:alternation :word-char-class #\- #\_ #\.)))
4934
"regex scanner for materialized view repositories")
4936
(defparameter *view-identifier-scanner*
4937
(cl-ppcre:create-scanner `(:sequence :start-anchor
4938
(:greedy-repetition 0 1 (:sequence
4939
(:greedy-repetition 0 1 (:sequence
4940
(:alternation "http" "https")
4942
(:greedy-repetition 1 nil (:inverted-char-class #\/))))
4944
(:register (:greedy-repetition 1 nil (:inverted-char-class #\/)))
4946
(:register (:greedy-repetition 1 nil (:inverted-char-class #\/)))
4948
(:register (:greedy-repetition 1 nil (:inverted-char-class #\.)))
4949
(:greedy-repetition 0 1 (:sequence #\. (:register (:GREEDY-REPETITION 0 NIL :EVERYTHING))))
4951
"regex scanner for view identifiers")
4953
(macrolet ((def-regex-parser (name (parameter &optional (required t)) &rest args)
4954
(let ((documentation (when (stringp (first args)) (pop args)))
4955
(scanner (pop args)))
4956
`(defgeneric ,name (,parameter &key junk-allowed)
4957
,@(when documentation `((:documentation ,documentation)))
4958
(:method ((,parameter string) &key (junk-allowed ,(not required)))
4959
(multiple-value-bind (parse registers) (cl-ppcre:scan-to-strings ,scanner ,parameter)
4962
(apply #'values (loop for element across registers collect (when (plusp (length element)) element))))
4966
(error ,(format nil "~a: invalid value: ~~s" name) ,parameter)))))
4967
(:method ((,parameter spocq:iri) &rest args)
4968
(declare (dynamic-extent args))
4969
(apply #',name (spocq:iri-lexical-form ,parameter) args))
4970
(:method ((,parameter puri:uri) &rest args)
4971
(declare (dynamic-extent args))
4972
(apply #',name (iri-lexical-form ,parameter) args))
4973
(:method ((,parameter t) &key (junk-allowed ,(not required)))
4974
(unless junk-allowed
4975
(error ,(format nil "~a: invalid value: ~~s" name) ,parameter))
4977
(def-regex-parser parse-url-authority (iri)
4978
"Return the url authority field"
4979
*url-authority-scanner*)
4980
;; (parse-url-authority "http://example.org")
4981
;; (parse-url-authority "http://example.org/")
4982
;; (parse-url-authority "http://example.org/asdf#qwer")
4984
(def-regex-parser parse-file-url-host-and-path (iri)
4985
"Return the file url host and path"
4987
;; (parse-file-url-host-and-path "file://asdf.qwer")
4988
(def-regex-parser parse-url-authority+path (iri nil)
4989
"Return as five values: protocol, authority, path, view, args"
4990
*url-authority+path-scanner*
4992
;; (parse-url-authority+path "http://fragments.dbpedia.org/{?subject,predicate,object}#x")
4993
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource{?subject,predicate,object}#x")
4994
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource/{?subject,predicate,object}#x")
4995
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource/view.csv{?subject,predicate,object}#x")
4996
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource/view.csv/{?subject,predicate,object}#x")
4997
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource/view.csv")
4998
;; (parse-url-authority+path "http://fragments.dbpedia.org/account/resource/extra/view.csv")
4999
;; (parse-url-authority+path "account/resource/view.csv") -> nil
5000
;; (parse-url-authority+path "template/cloudsearch2Did.csv?cloudsearchtype=StripID") -> nil
5001
;; (parse-url-authority+path <http://dbpedia.org/sparql>)
5002
;; (parse-url-authority+path <http://dbpedia.org/sparql/>)
5003
(def-regex-parser parse-url-query-string (iri)
5004
"Return the url quest string, if present"
5005
*url-query-scanner*)
5006
;; (parse-url-query-string "http://fragments.dbpedia.org/?revision=1234&user_id=2#x")
5007
;; (parse-url-query-string "http://fragments.dbpedia.org/#x")
5008
(def-regex-parser parse-repository-id (iri)
5009
"Return the url quest string, if present"
5010
*repository-id-scanner*)
5011
;; (parse-repository-id "account-1/resource")
5012
;; (parse-repository-id "account-1/resource/extra")
5013
(def-regex-parser parse-view-repository-id (namestring nil)
5014
"Return the components of a materialized view repository id"
5015
*view-repository-id-scanner*)
5016
;; (parse-view-repository-id "account-1/repo__some-view-name__view")
5017
(def-regex-parser parse-view-identifier (namestring nil)
5018
"Return the components of a view identifier"
5019
*view-identifier-scanner*)
5020
;; (parse-view-identifier "http://dydra.com/account-1/repo-2") -> nil
5021
;; (parse-view-identifier "http://dydra.com/account-1/repo-2/some-view-name")
5022
;; (parse-view-identifier "/account-1/repo-2/some-view-name")
5023
;; (parse-view-identifier "account-1/repo-2/some-view-name")
5024
;; (parse-view-identifier "/repo-2/some-view-name") -> nil
5025
;; (parse-view-identifier "http://dydra.com/account-1/repo-2/some-view-name.rq")
5028
(defun is-http-url (object)
5029
(is-http-url-namestring object))
5031
(defun is-http-url-namestring (object)
5032
(and (stringp object)
5033
(cl-ppcre:scan *url-authority-scanner* object)))
5035
(defun is-file-url-namestring (object)
5036
(and (stringp object)
5037
(not (null (parse-file-url-host-and-path object)))))
5039
(defun is-local-host (authority)
5040
(or (member authority (list "localhost" "127.0.0.1" (host-name) (site-name))
5041
:test #'string-equal)
5042
(member authority *localhost-authorities*
5043
:test #'string-equal)))
5045
(defgeneric iri-service-repository-id (location)
5046
(:method ((location null))
5048
(:method ((location symbol))
5049
(let ((namestring (get-symbol-uri-namestring location)))
5050
(when namestring (iri-service-repository-id namestring))))
5051
(:method ((location spocq:iri))
5052
(iri-service-repository-id (url-decode (spocq:iri-lexical-form location))))
5054
(:method ((parsed-iri puri:uri))
5055
(iri-service-repository-id (iri-lexical-form parsed-iri)))
5057
(:method ((location string))
5058
(multiple-value-bind (protocol authority path)
5059
(parse-url-authority+path location :junk-allowed t)
5060
(declare (ignore protocol))
5061
(cond ((and (or (null authority) (is-local-host authority))
5063
;; if local, return the local repository id and the parsed iri
5064
(multiple-value-bind (account repository) ;; check the syntax
5065
(parse-repository-id path :junk-allowed t)
5067
(values (make-repository-id :account-name account :repository-name repository)
5072
(multiple-value-bind (account repository) ;; check the syntax
5073
(parse-repository-id location :junk-allowed t)
5075
(values (make-repository-id :account-name account :repository-name repository)
5077
;;; (iri-service-repository-id "http://localhost/asdf/qwer/view?rev=HEAD")
5078
;;; (iri-service-repository-id "http://localhost/asdf/qwer/view")
5079
;;; (iri-service-repository-id "http://localhost/asdf/qwer?rev=HEAD")
5080
;;; (iri-service-repository-id "http://localhost/asdf/qwer")
5081
;;; (iri-service-repository-id "asdf/qwer?rev=HEAD")
5082
;;; (iri-service-repository-id "asdf/qwer/view")
5084
(defgeneric service-repository-revision (location)
5085
(:method ((location string))
5086
(let* ((location-query-parameters (parse-query-parameters (parse-url-query-string location)))
5087
(revision-id (or (getf location-query-parameters :revision-id)
5088
(getf location-query-parameters :revision)
5089
(getf location-query-parameters :revision-uuid))))
5091
(:method ((location spocq:iri))
5092
(service-repository-revision (url-decode (spocq:iri-lexical-form location)))))
5095
;;; (loop for uri in '("http:example" "urn:xxx" "urn:uuid:1234" "file:///example" "urnx") collect (parse-uri-scheme uri))
5098
(defun constrain-string-length (string)
5099
"constrain the string to be of a length less than any configured limit."
5100
(cond ((and *rdf-string-length-maximum*
5101
(> (length string) *rdf-string-length-maximum*))
5102
(log-error "string ~s exceeds length limit of ~s"
5103
(type-of string) *rdf-string-length-maximum*)
5104
(error "string ~s exceeds length limit of ~s"
5105
(type-of string) *rdf-string-length-maximum*))
5109
(defgeneric construct-term (term-type lexical-form datatype language-tag)
5110
(:documentation "Construct a term object for the given type x lexical for x (datatype + tag)"))
5112
(defmethod construct-term ((term-type (eql :uri)) lexical-form datatype language-tag)
5113
(when (or language-tag datatype)
5114
(warn "Improper attribute combination for an iri: ~s ~s ~s ~s."
5115
term-type lexical-form language-tag datatype))
5116
(construct-uri-term (parse-uri-scheme lexical-form) lexical-form))
5118
(defgeneric construct-uri-term (scheme lexical-form)
5119
(:documentation "Given the uril scheme parse and/or construct an uri of the respective type.")
5120
(:method ((scheme t) lexical-form)
5121
(flet ((as-blank-node-constant (lexical-form)
5122
"construct an uninterned constant blank node given either the literal blank node prefix
5123
or the standard genid uri prefix. we recognize them, but do not generate them.
5124
see http://www.w3.org/TR/rdf11-concepts/#section-skolemization"
5125
(cond ((string-equal "_:" lexical-form :end2 (min (length lexical-form) 2))
5126
(let ((node (spocq:make-blank-node (subseq lexical-form 2))))
5127
;; construct a new node and mark it as constant
5128
(setf (spocq:blank-node-constant-p node) t)
5130
((string-equal (site-genid-uri-prefix) lexical-form
5131
:end2 (min (length lexical-form) (site-genid-uri-prefix-length)))
5132
(let ((node (spocq:make-blank-node (subseq lexical-form (site-genid-uri-prefix-length)))))
5133
;; construct a new node and mark it as constant
5134
(setf (spocq:blank-node-constant-p node) t)
5136
(cond ((as-blank-node-constant lexical-form))
5137
((is-uuid-string lexical-form)
5138
(spocq:make-uuid (concatenate 'string "urn:uuid:" lexical-form)))
5140
(spocq:make-iri lexical-form))))))
5142
(defmethod construct-uri-term ((scheme (eql :|URN:UUID|)) lexical-form)
5143
(assert (is-uuid-string lexical-form :start 9) ()
5144
"Invalid uuid: ~s" lexical-form)
5145
(spocq:make-uuid lexical-form))
5147
(defmethod construct-uri-term ((scheme (eql :UUID)) lexical-form)
5148
(assert (is-uuid-string lexical-form :start 5) ()
5149
"Invalid uuid: ~s" lexical-form)
5150
(spocq:make-uuid (concatenate 'string "urn:" lexical-form)))
5152
(defmethod construct-uri-term ((scheme (eql :mailto)) lexical-form)
5153
(spocq:make-mailto-url lexical-form))
5155
(defmethod construct-uri-term ((scheme (eql :file)) lexical-form)
5156
(spocq:make-file-url lexical-form))
5158
(defmethod construct-uri-term ((scheme (eql :urn)) lexical-form)
5159
(let* ((length (length lexical-form)))
5160
(if (and (= length (load-time-value (+ (length "urn:hash::sha256:") (/ 256 4))))
5161
(string= "urn:hash::sha256:" lexical-form :end2 (load-time-value (length "urn:hash::sha256:"))))
5162
(spocq:make-sha256-urn lexical-form)
5163
(let ((vocabulary-package (find-vocabulary-package lexical-form)))
5164
;; if it is part of a vocabulary which is modeled as a package, intern it as a symbol
5165
;; do not create automatically, as to constrain membership except for list nodes,
5166
;; for which a non-symbol represents the term
5167
(if vocabulary-package
5168
(or ;; (de.setf.resource.implementation::uri-namestring-identifier lexical-form #'string nil)
5169
(vocabulary-identifier vocabulary-package lexical-form)
5170
;; this after vocabularies
5171
(spocq:make-iri lexical-form))
5172
(spocq:make-iri lexical-form))))))
5175
(defmethod construct-uri-term ((scheme (eql :https)) lexical-form)
5176
(construct-uri-term :http lexical-form))
5178
(defmethod construct-uri-term ((scheme (eql :http)) lexical-form)
5179
(let ((vocabulary-package (find-vocabulary-package lexical-form)))
5180
(cond (vocabulary-package
5181
;; if it is part of a vocabulary which is modeled as a package, intern it as a symbol
5182
;; do not create automatically, as to constrain membership except for list nodes,
5183
;; for which a non-symbol represents the term
5184
(or ;; (de.setf.resource.implementation::uri-namestring-identifier lexical-form #'string nil)
5185
(vocabulary-identifier vocabulary-package lexical-form)
5186
;; this after vocabularies
5187
(let* ((rdf-length (load-time-value (length "http://www.w3.org/1999/02/22-rdf-syntax-ns#")))
5188
(spin-length (load-time-value (length "http://spinrdf.org/sp#")))
5189
(create (or (not *strict-vocabulary-terms*)
5190
(and (> (length lexical-form) (1+ rdf-length))
5191
(eql (char lexical-form rdf-length) #\_)
5192
(string-equal lexical-form "http://www.w3.org/1999/02/22-rdf-syntax-ns#" :end1 rdf-length)
5193
(not (find-if (complement #'digit-char-p) lexical-form :start (1+ rdf-length))))
5194
(and (> (length lexical-form) (+ spin-length 3))
5195
(string-equal lexical-form "http://spinrdf.org/sp#arg" :end1 (+ spin-length 3))
5196
(not (find-if (complement #'digit-char-p) lexical-form :start (+ spin-length 3)))))))
5198
(cond ((or (string-equal "http:" lexical-form :end2 (min (length lexical-form) 5))
5199
(string-equal "https:" lexical-form :end2 (min (length lexical-form) 6)))
5200
(spocq:make-http-url lexical-form))
5202
(spocq:make-iri lexical-form)))
5203
(spocq.e::vocabulary-error :expression lexical-form)))))
5205
(spocq:make-http-url lexical-form)))))
5207
(defmethod construct-term ((term-type (eql :none)) lexical-form datatype language-tag)
5208
(when (or lexical-form language-tag datatype)
5209
(warn "Improper attribute combination for a null: ~s ~s ~s ~s."
5210
term-type lexical-form language-tag datatype))
5213
(defmethod construct-term ((term-type (eql :node)) lexical-form datatype language-tag)
5214
(when (or language-tag datatype)
5215
(warn "Improper attribute combination for a blank node: ~s ~s ~s ~s."
5216
term-type lexical-form language-tag datatype))
5217
(spocq:make-blank-node lexical-form))
5219
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype string) (language-tag null))
5220
(construct-term :literal lexical-form (intern-iri datatype) language-tag))
5222
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype t) (language-tag t))
5223
(warn "Improper attribute combination for a literal: ~s ~s ~s ~s."
5224
term-type lexical-form datatype language-tag)
5227
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype null) (language-tag null))
5230
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype null) (language-tag t))
5231
(construct-term :literal lexical-form nil (string language-tag)))
5233
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype null) (language-tag string))
5234
(spocq:make-plain-literal lexical-form language-tag))
5236
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype (eql |rdf|:|langString|)) (language-tag string))
5237
(spocq:make-plain-literal lexical-form language-tag))
5239
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype symbol) (language-tag null))
5240
(flet ((parse-date-time (lexical-form)
5241
(case (length lexical-form)
5242
#+(or) (20 (|-yyyy-MM-ddTHH:mm:ssZZZZ| (concatenate 'string lexical-form "00")))
5243
#+(or) (t (|-yyyy-MM-ddTHH:mm:ssZZZZ| lexical-form "00")))))
5244
(handler-case (case datatype
5245
(|xsd|:|boolean| (spocq.e:boolean lexical-form))
5246
;; must check lexical conformance (|xsd|:|dateTime| (spocq:make-date-time :lexical-form lexical-form)
5247
(|xsd|:|dateTime| (|-yyyy-MM-ddTHH:mm:ss(ZZZZZZ)?| lexical-form))
5248
;; must check lexical conformance (|xsd|:|date| (spocq:make-date :lexical-form lexical-form)
5249
(|xsd|:|date| (|-yyyy-MM-dd(ZZZZ)?| lexical-form))
5251
(|PnYnMnDTnHnMnS| lexical-form))
5252
(|xsd|:|decimal| (spocq.e:decimal lexical-form))
5253
(|xsd|:|double| (spocq.e:double lexical-form))
5254
(|xsd|:|float| (spocq.e:float lexical-form))
5255
(|xsd|:|integer| (spocq.e:integer lexical-form))
5256
(|xsd|:|string| lexical-form)
5258
(|xsd|:|dayTimeDuration| (|PnDTnHnMnS| lexical-form))
5259
(|xsd|:|gDay| (|---dd(ZZZZZZ)?| lexical-form))
5260
(|xsd|:|gMonth| (|--MM(ZZZZZZ)?| lexical-form))
5261
(|xsd|:|gMonthDay| (|--MM-dd(ZZZZZZ)?| lexical-form))
5262
(|xsd|:|gYear| (|YYYY(ZZZZZZ)?| lexical-form))
5263
(|xsd|:|gYearMonth| (|YYYY-MM(ZZZZZZ)?| lexical-form))
5265
;; must check lexical conformance (spocq:make-time :lexical-form lexical-form))
5266
(|HH:mm:ss(ZZZZ)?| lexical-form))
5267
(|xsd|:|yearMonthDuration| (|PnYnM| lexical-form))
5269
(|time|:|DateInterval| (|time|:|dateInterval| lexical-form))
5270
(|time|:|DateTimeInterval| (|time|:|dateTimeInterval| lexical-form))
5271
(|time|:|TimeInterval| (|time|:|timeInterval| lexical-form))
5273
(if (let ((class (find-class datatype nil))) (and class (subtypep class '|xsd|:|integer|)))
5274
(spocq.e:integer lexical-form)
5275
(spocq:make-unsupported-typed-literal lexical-form datatype))))
5276
(condition (condition)
5277
(spocq:make-unsupported-typed-literal lexical-form datatype condition)))))
5279
(defmethod construct-term ((term-type (eql :literal)) lexical-form (datatype spocq:iri) (language-tag null))
5280
"Iff the type is an iri object rather than a symbol, then the type is not a known native type"
5281
(spocq:make-unsupported-typed-literal lexical-form datatype))
5284
(defgeneric construct-term (term-type lexical-form datatype language-tag)
5285
(:method ((term-type (eql :uri)) lexical-form datatype language-tag)
5286
(when (or language-tag datatype)
5287
(warn "Improper attribute combination for an iri: ~s ~s ~s ~s."
5288
term-type lexical-form language-tag datatype))
5289
(let ((vocabulary-package nil))
5290
(flet ((as-blank-node-constant (lexical-form)
5291
"construct an uninterned constant blank node given either the literal blank node prefix
5292
or the standard genid uri prefix. we recognize them, but do not generate them.
5293
see http://www.w3.org/TR/rdf11-concepts/#section-skolemization"
5294
(cond ((string-equal "_:" lexical-form :end2 (min (length lexical-form) 2))
5295
(let ((node (spocq:make-blank-node (subseq lexical-form 2))))
5296
;; construct a new node and mark it as constant
5297
(setf (spocq:blank-node-constant-p node) t)
5299
((string-equal (site-genid-uri-prefix) lexical-form
5300
:end2 (min (length lexical-form) (site-genid-uri-prefix-length)))
5301
(let ((node (spocq:make-blank-node (subseq lexical-form (site-genid-uri-prefix-length)))))
5302
;; construct a new node and mark it as constant
5303
(setf (spocq:blank-node-constant-p node) t)
5305
(cond ((string-equal "urn:uuid:" lexical-form :end2 (min (length lexical-form) 9))
5306
(assert (is-uuid-string lexical-form :start 9) ()
5307
"Invalid uuid: ~s" lexical-form)
5308
(spocq:make-uuid lexical-form))
5309
((string-equal "mailto:" lexical-form :end2 (min (length lexical-form) 7))
5310
(spocq:make-mailto-url lexical-form))
5311
((as-blank-node-constant lexical-form))
5312
((setf vocabulary-package (find-vocabulary-package lexical-form))
5313
;; if it is part of a vocabulary which is modeled as a package, intern it as a symbol
5314
;; do not create automatically, as to constrain membership except for list nodes,
5315
;; for which a non-symbol represents the term
5316
(or ;; (de.setf.resource.implementation::uri-namestring-identifier lexical-form #'string nil)
5317
(vocabulary-identifier vocabulary-package lexical-form)
5318
;; this after vocabularies
5319
(when (string-equal "urn:" lexical-form :end2 (min (length lexical-form) 4))
5320
(spocq:make-iri lexical-form))
5321
(let* ((rdf-length (load-time-value (length "http://www.w3.org/1999/02/22-rdf-syntax-ns#")))
5322
(spin-length (load-time-value (length "http://spinrdf.org/sp#")))
5323
(create (or (not *strict-vocabulary-terms*)
5324
(and (> (length lexical-form) (1+ rdf-length))
5325
(eql (char lexical-form rdf-length) #\_)
5326
(string-equal lexical-form "http://www.w3.org/1999/02/22-rdf-syntax-ns#" :end1 rdf-length)
5327
(not (find-if (complement #'digit-char-p) lexical-form :start (1+ rdf-length))))
5328
(and (> (length lexical-form) (+ spin-length 3))
5329
(string-equal lexical-form "http://spinrdf.org/sp#arg" :end1 (+ spin-length 3))
5330
(not (find-if (complement #'digit-char-p) lexical-form :start (+ spin-length 3)))))))
5332
(cond ((or (string-equal "http:" lexical-form :end2 (min (length lexical-form) 5))
5333
(string-equal "https:" lexical-form :end2 (min (length lexical-form) 6)))
5334
(spocq:make-http-url lexical-form))
5336
(spocq:make-iri lexical-form)))
5337
(spocq.e::vocabulary-error :expression lexical-form)))))
5338
((or (string-equal "http:" lexical-form :end2 (min (length lexical-form) 5))
5339
(string-equal "https:" lexical-form :end2 (min (length lexical-form) 6)))
5340
(spocq:make-http-url lexical-form))
5341
((string-equal "file:" lexical-form :end2 (min (length lexical-form) 5))
5342
(spocq:make-file-url lexical-form))
5344
(spocq:make-iri lexical-form))))))
5346
(:method ((term-type (eql :none)) lexical-form datatype language-tag)
5347
(when (or lexical-form language-tag datatype)
5348
(warn "Improper attribute combination for a null: ~s ~s ~s ~s."
5349
term-type lexical-form language-tag datatype))
5352
(:method ((term-type (eql :node)) lexical-form datatype language-tag)
5353
(when (or language-tag datatype)
5354
(warn "Improper attribute combination for a blank node: ~s ~s ~s ~s."
5355
term-type lexical-form language-tag datatype))
5356
(spocq:make-blank-node lexical-form))
5358
(:method ((term-type (eql :literal)) lexical-form (datatype string) (language-tag null))
5359
(construct-term :literal lexical-form (intern-iri datatype) language-tag))
5361
(:method ((term-type (eql :literal)) lexical-form (datatype t) (language-tag t))
5362
(warn "Improper attribute combination for a literal: ~s ~s ~s ~s."
5363
term-type lexical-form datatype language-tag)
5366
(:method ((term-type (eql :literal)) lexical-form (datatype null) (language-tag null))
5369
(:method ((term-type (eql :literal)) lexical-form (datatype null) (language-tag t))
5370
(construct-term :literal lexical-form nil (string language-tag)))
5372
(:method ((term-type (eql :literal)) lexical-form (datatype null) (language-tag string))
5373
(spocq:make-plain-literal lexical-form language-tag))
5375
(:method ((term-type (eql :literal)) lexical-form (datatype symbol) (language-tag null))
5376
(flet ((parse-date-time (lexical-form)
5377
(case (length lexical-form)
5378
#+(or) (20 (|-yyyy-MM-ddTHH:mm:ssZZZZ| (concatenate 'string lexical-form "00")))
5379
#+(or) (t (|-yyyy-MM-ddTHH:mm:ssZZZZ| lexical-form "00")))))
5380
(handler-case (case datatype
5381
(|xsd|:|boolean| (spocq.e:boolean lexical-form))
5382
;; must check lexical conformance (|xsd|:|dateTime| (spocq:make-date-time :lexical-form lexical-form)
5383
(|xsd|:|dateTime| (|-yyyy-MM-ddTHH:mm:ss(ZZZZZZ)?| lexical-form))
5384
;; must check lexical conformance (|xsd|:|date| (spocq:make-date :lexical-form lexical-form)
5385
(|xsd|:|date| (|-yyyy-MM-dd(ZZZZ)?| lexical-form))
5387
(|PnYnMnDTnHnMnS| lexical-form))
5388
(|xsd|:|decimal| (spocq.e:decimal lexical-form))
5389
(|xsd|:|double| (spocq.e:double lexical-form))
5390
(|xsd|:|float| (spocq.e:float lexical-form))
5391
(|xsd|:|integer| (spocq.e:integer lexical-form))
5392
(|xsd|:|string| lexical-form)
5394
(|xsd|:|dayTimeDuration| (|PnDTnHnMnS| lexical-form))
5395
(|xsd|:|gDay| (|---dd(ZZZZZZ)?| lexical-form))
5396
(|xsd|:|gMonth| (|--MM(ZZZZZZ)?| lexical-form))
5397
(|xsd|:|gMonthDay| (|--MM-dd(ZZZZZZ)?| lexical-form))
5398
(|xsd|:|gYear| (|YYYY(ZZZZZZ)?| lexical-form))
5399
(|xsd|:|gYearMonth| (|YYYY-MM(ZZZZZZ)?| lexical-form))
5401
;; must check lexical conformance (spocq:make-time :lexical-form lexical-form))
5402
(|HH:mm:ss(ZZZZ)?| lexical-form))
5403
(|xsd|:|yearMonthDuration| (|PnYnM| lexical-form))
5405
(|time|:|DateInterval| (|time|:|dateInterval| lexical-form))
5406
(|time|:|DateTimeInterval| (|time|:|dateTimeInterval| lexical-form))
5407
(|time|:|TimeInterval| (|time|:|timeInterval| lexical-form))
5409
(if (let ((class (find-class datatype nil))) (and class (subtypep class '|xsd|:|integer|)))
5410
(spocq.e:integer lexical-form)
5411
(spocq:make-unsupported-typed-literal lexical-form datatype))))
5412
(condition (condition)
5413
(spocq:make-unsupported-typed-literal lexical-form datatype condition)))))
5415
(:method ((term-type (eql :literal)) lexical-form (datatype spocq:iri) (language-tag null))
5416
(spocq:make-unsupported-typed-literal lexical-form datatype)))
5419
(defun parse-decimal2 (string)
5420
(let* ((dot (position #\. string))
5421
(slash (position #\/ string))
5422
(minusp (eql (char string 0) #\-))
5423
(plusp (eql (char string 0) #\+))
5424
(start (if (or plusp minusp) 1 0)))
5426
(let* ((whole (parse-integer string :end dot :start start))
5427
(fraction-digits (- (length string) (1+ dot)))
5428
(fraction-value (if (plusp fraction-digits)
5429
(parse-integer string :start (1+ dot))
5431
(factor (if (plusp fraction-digits) (expt 10 fraction-digits) 1))
5432
(value (/ (+ (* factor whole) fraction-value) factor)))
5433
(if minusp (- value) value)))
5435
(let* ((numerator (parse-integer string :end slash :start start))
5436
(denominator (parse-integer string :start (1+ slash)))
5437
(value (/ numerator denominator)))
5438
(if minusp (- value) value)))
5440
(let ((value (parse-integer string :start start)))
5441
(if minusp (- value) value))))))
5443
(defun read-buffer (stream predicate)
5444
(let ((buffer (make-array 8 :element-type 'character :fill-pointer 0 :adjustable t)))
5445
(loop (let ((char (read-char stream)))
5446
(unless (funcall predicate char)
5447
(unread-char char stream)
5448
(return (copy-seq buffer)))
5449
(vector-push-extend char buffer)))))
5451
(defparameter *property-path-bounds-readtable* (copy-readtable nil))
5452
(set-macro-character #\{ #'(lambda (stream c) (declare (ignore c)) (read-delimited-list #\} stream)) nil *property-path-bounds-readtable*)
5453
(set-syntax-from-char #\, #\space *property-path-bounds-readtable*)
5454
(set-syntax-from-char #\} #\) *property-path-bounds-readtable*)
5456
(defun read-iri-or-blank-node (stream char)
5457
;;!! this will not interact well with suppressed reads
5458
(declare (ignore char))
5459
(case (peek-char nil stream)
5460
((#\space #\tab #\return #\linefeed #\))
5461
(intern "<" *package*))
5464
(intern "<=" *package*))
5466
(let* ((iri-string (read-buffer stream #'(lambda (c) (not (eql c #\>))))))
5468
(let* ((colon-position (position #\: iri-string))
5469
(prefix (when colon-position (subseq iri-string 0 colon-position))))
5470
(if (equal prefix "_")
5471
(let ((node (spocq:make-blank-node (subseq iri-string 2))))
5472
;; construct a new node and mark it as constant
5473
(setf (spocq:blank-node-constant-p node) t)
5475
(let* ((package (when prefix (find-package prefix)))
5476
(iri (or (and (member package *iri-packages*)
5477
(find-symbol (subseq iri-string (1+ colon-position)) package))
5478
(intern-iri iri-string)))
5479
(next (peek-char nil stream nil nil)))
5483
(make-bounded-property-path :element (make-property-path-verb :iri iri) :min 0 :max nil))
5486
(make-bounded-property-path :element (make-property-path-verb :iri iri) :min 0 :max 1))
5489
(make-bounded-property-path :element (make-property-path-verb :iri iri) :min 1 :max nil))
5490
(#\{ (let* ((*readtable* *property-path-bounds-readtable*)
5491
(bounds (read stream)))
5492
(destructuring-bind (min &optional (max nil)) bounds
5493
(make-bounded-property-path :element (make-property-path-verb :iri iri) :min min :max max))))
5494
(#\| (make-or-property-path :elements (list iri (read stream))))
5495
(#\/ (make-sequence-property-path :elements (list iri (read stream))))
5497
;;; nyi !<path> and ^<path>
5498
(set-packaged-macro-character #\< 'read-iri-or-blank-node)
5500
(defun read-variable (stream char)
5501
"Read a variable. If the package is distinguished handle case as per readtable.
5502
Otherwise, preserve the case"
5503
(declare (ignore char))
5504
(case (peek-char nil stream)
5507
(when (eql (peek-char nil stream) #\:)
5509
;; with colons, observe standard reader for the symbol case
5510
(let ((name-string (read-buffer stream #'(lambda (c) (not (find c '(#\space #\tab #\return #\linefeed #\page #\nul #\))
5512
(cons-symbol :? name-string)))
5514
;; without colons, take the string literally
5515
(let ((name-string (read-buffer stream #'(lambda (c) (not (find c '(#\space #\tab #\return #\linefeed #\page #\nul #\))
5517
;; but, as a sparql variable
5518
;(cons-symbol *package* (string char) name-string)
5519
(intern name-string :?)))))
5521
(set-packaged-macro-character #\? 'read-variable)
5523
(defun string-truncate (string length)
5524
(if (<= (length string) length)
5526
(let ((truncated (make-array length :fill-pointer 0 :element-type 'character))
5528
(loop for i below max
5530
do (vector-push c truncated))
5531
(when (= (length truncated) max)
5532
(dotimes (x 2) (vector-push #\. truncated)))
5534
;;; (loop for i below 7 collect (string-truncate "asdfq" i))
5536
(deftype repository-string () '(satisfies repository-string-p))
5538
(defgeneric repository-string-p (string)
5539
(:method ((object t))
5541
(:method ((string string))
5542
(let ((scanner (load-time-value (cl-ppcre:create-scanner "^[A-Za-z_][A-Za-z0-9_-]*/[A-Za-z0-9_][A-Za-z0-9_-]*$"))))
5543
(and (cl-ppcre:scan scanner string) t))))
5544
;;; (repository-string-p "jhacker/foaf")
5545
;;; (and (not (repository-string-p "jhacker/9foaf")) (not (repository-string-p "jhacker/ foaf")))
5547
(deftype email-string () '(satisfies email-string-p))
5549
(defgeneric email-string-p (string)
5550
(:method ((object t))
5552
(:method ((string string))
5553
(let ((scanner (load-time-value (cl-ppcre:create-scanner "^[A-Za-z0-9._%+-]+@(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}$"))))
5554
(and (cl-ppcre:scan scanner string) t))))
5555
;;; (email-string-p "a@x.asd")
5556
;;; (email-string-p "a@x.asd.qwer")
5557
;;; (email-string-p "a@x.a")
5559
;;; rfc 3986 via puri
5561
(defgeneric url-string-p (string &key schemes)
5562
(:method ((object t) &key &allow-other-keys)
5564
(:method ((string string) &key (schemes '("http" "https" "ftp")))
5565
(multiple-value-bind (scheme host port path query fragment userinfo)
5566
(puri::parse-uri-string string)
5567
(declare (ignore port path query fragment userinfo))
5568
(and (or (and (null scheme) (null schemes))
5569
(member scheme schemes :test #'string-equal))
5573
;;; rfc 822, 2822, 1123 in simplified form for memento
5575
(defparameter *rfc1123-date-time-scanner*
5576
(cl-ppcre:create-scanner "([a-zA-z]{3}), ([0-9]{1,2}) ([a-zA-z]{3}) ([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2}) GMT"))
5577
;;; (nth-value 1 (cl-ppcre:scan-to-strings *rfc1123-date-time-scanner* "Wed, 30 May 2007 18:47:52 GMT"))
5578
;;; (nth-value 1 (cl-ppcre:scan-to-strings *rfc1123-date-time-scanner* "Mon, 9 Mar 2020 11:25:06 GMT"))
5580
(defun parse-rfc1123 (value &key junk-allowed)
5581
"given a timestamp encoded as rfc1123, which means rfc822bis, deconstruct it, convert the elements and
5582
encode it is a universal-time"
5583
(let ((substrings (coerce (nth-value 1 (cl-ppcre:scan-to-strings *rfc1123-date-time-scanner* value)) 'list)))
5585
(destructuring-bind (wkday day month year hour minute second)
5587
(declare (ignore wkday))
5588
(encode-universal-time (parse-integer second)
5589
(parse-integer minute)
5590
(parse-integer hour)
5592
(date:decode-month-name month)
5593
(parse-integer year)
5595
(unless junk-allowed
5596
(error "Invalid rdf1123 date-time: ~s." value)))))
5597
;;; (spocq.e:date-time (parse-rfc1123 "Wed, 30 May 2007 18:47:52 GMT"))
5598
;;; (parse-rfc1123 "Wed, 30 May 2007 18:47:5 GMT" :junk-allowed t)
5599
;;; (ignore-errors (parse-rfc1123 "Wed, 30 May 2007 18:47:5z GMT"))
5601
(defgeneric encode-rfc1123 (value &optional stream)
5602
(:method ((value integer) &optional (stream nil))
5603
(multiple-value-bind (second minute hour day month year weekday)
5604
(decode-universal-time value 0)
5605
(format stream "~a, ~d ~:(~a~) ~4,'0d ~2,'0d:~2,'0d:~2,'0d GMT"
5606
(date:day-in-week-name weekday 3) day (date:month-name month 3) year hour minute second)))
5607
(:method ((value spocq:date-time) &optional stream)
5608
(encode-rfc1123 (date-time-universal-time value) stream)))
5609
;;; (encode-rfc1123 (get-universal-time))
5610
;;; (encode-rfc1123 (spocq.e:now))
5613
;;; external library management
5614
;;; this serves to relocate the libraries from whatever location they may have been
5615
;;; found at the time the system is built, to where they happen to be when the system starts.
5617
;;; to that end, the cffi registry is scanned, the libraries are unloaded and the names are saved,
5618
;;; but not the paths. then, upon restart the intended runtime locations are searched to
5620
;;; nb. both locations are present in the foreign library search path
5621
;;; to permit reloading for progressive builds as well as the eventual production start
5623
(let ((library-pathname-list ()))
5624
(defun unload-libraries (&key (verbose *load-verbose*))
5625
(when verbose (warn "unloading libraries..."))
5626
(setf library-pathname-list
5627
(loop for library in (cffi:list-foreign-libraries)
5628
collect (pathname (file-namestring (cffi:foreign-library-pathname library)))
5629
do (cffi:close-foreign-library library)
5630
when verbose do (warn "unloaded: ~s (~s)" library (cffi:foreign-library-pathname library)))))
5631
(defun reload-libraries (&key (search-path *foreign-library-search-path*))
5632
(setq cffi:*foreign-library-directories* search-path)
5633
(setq clsql-sys:*foreign-library-search-paths* search-path)
5634
(handler-bind ((style-warning #'muffle-warning))
5635
(loop for pathname in library-pathname-list
5636
do (cffi:load-foreign-library pathname)))))
5638
(pushnew 'reload-libraries sb-ext:*init-hooks*)
5642
;;; optimization components
5644
;;; alpha conversion: canonical order, variable renaming
5646
(defun anonymous-triple-preceeds (triple1 triple2)
5647
(let ((*enable-sort-precedence* t))
5648
;; sort triples according to criteria
5650
;; - standard < comparison for patterns w/o variables
5651
(flet ((term-preceeds (term1 term2)
5652
(ignore-errors (spocq.e:< term1 term2)))
5653
(term-equal (term1 term2)
5654
(ignore-errors (spocq.e:= term1 term2))))
5655
(or (< (count-if #'null triple1) (count-if #'null triple2))
5656
(and (= (count-if #'null triple1) (count-if #'null triple2))
5657
(loop for term1 in (rest triple1)
5658
for term2 in (rest triple2)
5659
if (term-preceeds term1 term2)
5661
else unless (term-equal term1 term2)
5663
finally (return nil)))))))
5665
(defparameter *abstract-triple-mode* :abstract-variables)
5667
(defun anonymize-triple (triple)
5668
(ecase *abstract-triple-mode*
5670
(destructuring-bind (op s p o . rest) triple
5671
(declare (ignore o)) ; always abstract the object value
5673
,(if (variable-p s) nil s)
5674
,(if (variable-p p) nil p)
5677
(:abstract-variables
5678
(cons (first triple) (substitute-if nil #'variable-p (rest triple))))))
5682
(defun abstract-bgp (bgp)
5683
(let* ((ordered-triples (sort (copy-list (rest bgp)) #'anonymous-triple-preceeds
5684
:key #'anonymize-triple))
5685
(variables (append (reverse (expression-variables bgp))
5686
(case *abstract-triple-mode*
5688
(remove-duplicates (mapcar #'statement-object (rest bgp))
5690
(canonical-variables (loop for i from 0 below (length variables)
5691
collect (cons-symbol :? "VAR" (prin1-to-string i))))
5692
(dictionary (mapcar #'cons variables canonical-variables)))
5693
;; (print variables)
5694
;; (pprint dictionary)
5695
(values (cons (first bgp) (sublis dictionary ordered-triples :test #'equalp))
5698
;;; (abstract-bgp '(bgp (spocq.a:|triple| ?::v1 "asdf" 2) (spocq.a:|triple| ?::v1 "asdf" 3)))
5699
;;; (abstract-bgp '(bgp (spocq.a:|triple| ?::v1 "asdf" 2) (spocq.a:|triple| ?::v1 "asde" 2)))
5700
;;; (abstract-bgp `(bgp (spocq.a:|triple| ?::v1 "asdf" 2) (spocq.a:|triple| ?::v1 "asdf" ,(puri:uri "http://test/"))))
5701
;;; (abstract-bgp `(bgp (spocq.a:|triple| ?::v1 "asdf" _::b1) (spocq.a:|triple| ?::v1 "asdf" ,(puri:uri "http://test/"))))
5704
;;; predicates used to match query components
5705
;;; the general operators require the query class definition
5707
(defun order-modifier-form-p (object)
5709
(member (first object) '(spocq.a:|desc| spocq.a:|asc|))
5712
(defun concise-bounded-description-describe-form-p (value)
5713
(if (member value '(|urn:dydra|:|simple-symmetric-concise-bounded-description|
5714
|urn:dydra|:|simple-concise-bounded-description|
5715
|urn:dydra|:|simple-inverse-concise-bounded-description|))
5718
(defun context-term-p (value)
5719
(if (member value '(|urn:dydra|:|all|
5720
|urn:dydra|:|default|
5721
|urn:dydra|:|named|))
5724
(defun undefined-variable-behavior-p (value)
5725
(if (member value '(|urn:dydra|:|error|
5726
|urn:dydra|:|warning|
5727
|urn:dydra|:|dynamicBinding|))
5731
(defun elementary-bgp-statement-form-p (object)
5732
"return true for triple pattern statements."
5733
(triple-form-p object))
5735
(defun extended-bgp-statement-form-p (object)
5736
"return true for triple pattern statements with extension operators"
5737
(and (triple-form-p object)
5738
(extension-operator-p (third object))))
5740
(defun logical-bgp-statement-form-p (form)
5741
"return true for pattern statement which are just one of the logical combinations."
5742
(or (sum-form-p form)
5746
(union-form-p form)))
5748
(defun bgp-statement-form-p (object)
5749
"return true elementary and compound logical bgp statements."
5750
(or (elementary-bgp-statement-form-p object)
5751
(logical-bgp-statement-form-p object)))
5753
(defun bgp-pattern-form-p (form)
5754
"return true for any form which serves to match or filter within a bgp."
5755
(or (bgp-statement-form-p form)
5756
(filter-form-p form)))
5758
(defun bgp-state-form-p (form)
5759
(and (triple-form-p form)
5760
(not (state-predicate-p (third form)))))
5762
(defun agp-generator-form-p (form)
5763
(and (consp form) (eq (first form) 'agp-generator)))
5765
(defun built-in-sse-form-p (form)
5767
(member (first form)
5768
'(spocq.a:|abs| spocq.a:|add| spocq.a:|avg|
5769
spocq.a:|bound| spocq.a:|cardinality| spocq.a:|ceil| spocq.a:|concat| spocq.a:|contains| spocq.a:|count|
5770
spocq.a:|datatype| spocq.a:|day|
5771
spocq.a:|encode_for_uri| spocq.a:|exists|
5773
spocq.a:|group_concat|
5775
spocq.a:|if| spocq.a:|in| spocq.a:|iri| spocq.a:|isBlank| spocq.a:|isLiteral| spocq.a:|isIRI| spocq.a:|isURI|
5776
spocq.a:|lang| spocq.a:|langMatches| spocq.a:|lcase|
5777
spocq.a:|max| spocq.a:|md5| spocq.a:|min| spocq.a:|minutes| spocq.a:|month|
5778
spocq.a:|not| spocq.a:|notin| spocq.a:|now|
5780
spocq.a:|rand| spocq.a:|regex| spocq.a:|round|
5781
spocq.a:|sameTerm| spocq.a:|sample| spocq.a:|seconds| spocq.a:|sha1| spocq.a:|sha224| spocq.a:|sha256| spocq.a:|sha384| spocq.a:|sha512|
5782
spocq.a:|str| spocq.a:|strafter| spocq.a:|strbefore| spocq.a:|strends| spocq.a:|strdt| spocq.a:|strlang| spocq.a:|strlen| spocq.a:|strstarts| spocq.a:|substr| spocq.a:|sum|
5784
spocq.a:|ucase| spocq.a:|unbound| spocq.a:|uri|
5786
spocq.a:|+| spocq.a:|!| spocq.a:|-| spocq.a:|=| spocq.a:|!=| spocq.a:|<| spocq.a:|>| spocq.a:|<=| spocq.a:|>=| spocq.a:|*| spocq.a:|/| spocq.a:|_|
5789
(defun conditional-sse-form-p (form)
5791
(member (first form)
5792
'(spocq.a:|and| spocq.a:|&&| spocq.a:|or| spocq.a:\|\| spocq.a:|!| spocq.a:|not|))))
5794
(defun arithmetic-sse-form-p (form)
5796
(member (first form)
5797
'(spocq.a:|+| spocq.a:|-| spocq.a:|*| spocq.a:|/|))))
5799
(defun construct-form-template (form)
5803
(defun functional-sse-form-p (form)
5805
(iri-p (first form))))
5807
(defun relational-sse-form-p (form)
5809
(member (first form)
5810
'(spocq.a:|=| spocq.a:|!=| spocq.a:|<| spocq.a:|>| spocq.a:|<=| spocq.a:|>=| spocq.a:|in| spocq.a:|notin| ))))
5814
;;; compute subexpressions
5816
;;; given the collected bgp forms, canonicalize them, create a subsequence concordance.
5817
;;; select the maximal, non-overlapping subsequences. (<- does not work since varying constituency
5818
;;; leads to interleaved patterns.)
5819
;;; replace those with named sub-bgps
5821
;;; for each pattern, record the containing bgps,
5822
;;; invert that and for each bgp combo ;;; record the included patterns. do this also for single pattern occurrences.
5823
;;; trim this collection by removing patterns - both single and combination elements, which also appear in a
5824
;;; larger combination.
5825
;;; replace combinations wherever they appear with a sub-bgp. single elements are left unchanged.
5827
(defparameter *pattern-concordance* nil)
5829
(defstruct bgpse bgp-set pattern-set)
5831
(defun bgpse-bgp-cardinality (bgpse) (length (bgpse-bgp-set bgpse)))
5832
(defun bgpse-pattern-cardinality (bgpse) (length (bgpse-pattern-set bgpse)))
5833
(defun bgpse-add-pattern (bgpse pattern) (pushnew pattern (bgpse-pattern-set bgpse) :test #'equal))
5834
(defun bgpse-add-bgp (bgpse bgp) (pushnew bgp (bgpse-bgp-set bgpse) :test #'equal))
5836
(defparameter *bgpse-factors*
5837
`((1 . bgpse-bgp-cardinality)
5838
(1 . bgpse-pattern-cardinality))
5839
"An a-list of numeric factors and function which compute the preference for a given bgp subexpression
5840
for a particular criteria. The global spec includes just two factors, evenly weighted, for the bgp set
5841
cardinality and pattern set cardinality. When supplied in connection with an active query - for which the
5842
repository can serve as a source of selectivity, an aditional component is included to include the
5845
(defun bgpse-priority (bgpse)
5846
(loop for (factor . function) in *bgpse-factors*
5847
sum (* factor (funcall function bgpse))))
5849
(defun rewrite-common-bgps (bgps &key (sort-key #'bgpse-priority) ((:factors *bgpse-factors*) *bgpse-factors*)
5851
(let ((canonical-bgps (mapcar #'abstract-bgp bgps))
5852
(pattern-2-bgps (make-hash-table :test 'equalp))
5853
(bgps-2-patterns (make-hash-table :test 'equalp))
5854
(combinations ()) ; alist of (bgps . patterns) of where the patterns appear
5855
(sorted-combinations ()) ; that alist sorted by sort-key
5856
(minimum-combinations ()) ; the minimum set sufficient to match all combinations
5858
(labels ((register-patterns (bgp)
5859
(dolist (pattern (rest bgp))
5860
(push bgp (gethash pattern pattern-2-bgps))))
5861
(combine-patterns (pattern bgps)
5862
(push pattern (gethash bgps bgps-2-patterns))))
5865
(pprint-sse canonical-bgps))
5866
(dolist (bgp canonical-bgps)
5867
(register-patterns bgp))
5868
(maphash #'combine-patterns pattern-2-bgps)
5869
(maphash #'(lambda (bgps patterns)
5870
;; if the patterns appear in more than one bgp, cinlude them in the process
5872
(push (make-bgpse :bgp-set bgps :pattern-set patterns) combinations)))
5874
(setf sorted-combinations (sort combinations #'> :key sort-key))
5875
(setf minimum-combinations (remove-if #'(lambda (bgpse)
5876
(let ((predecessors (ldiff combinations (member bgpse sorted-combinations))))
5877
(every #'(lambda (p)
5878
(some #'(lambda (p-bgpse) (member p (bgpse-pattern-set p-bgpse)))
5880
(bgpse-pattern-set bgpse))))
5881
sorted-combinations :from-end t))
5882
;; (aprint minimum-combinations)
5883
(loop for bgpse in minimum-combinations
5884
for bgps = (bgpse-bgp-set bgpse)
5885
for patterns = (bgpse-pattern-set bgpse)
5886
do (dolist (bgp bgps)
5887
(unless (null (set-difference patterns (rest bgp) :test #'equalp))
5888
(error "~%~%mismatch? :~%~s~%~s"
5890
;; (format *trace-output* "~%bgp before : ~s~%sub : ~s" bgp patterns)
5892
`((spocq.a::|sub-bgp| ,@patterns)
5893
,@(set-difference (rest bgp) patterns :test #'equalp)))
5894
;;(format *trace-output* "~%bgp after : ~s" bgp)
5898
(defstruct pprint-variable symbol)
5899
(defstruct pprint-triple terms)
5901
(defmethod print-object ((object pprint-variable) (stream t))
5902
(format stream "?~a" (pprint-variable-symbol object)))
5903
(defmethod print-object ((object pprint-triple) (stream t))
5904
(let ((*print-pretty* nil))
5905
(format stream "{~{~s~^ ~}}" (pprint-triple-terms object))))
5907
(defparameter *pprint-sse-package* (or (find-package :sse-package) (make-package :sse-package :use nil)))
5908
(loop for symbol being each external-symbol of :spocq.a
5909
do (import symbol *pprint-sse-package*) (export symbol *pprint-sse-package*))
5910
(loop for symbol being each external-symbol of :spocq.e
5911
do (unless (find-symbol (string symbol) *pprint-sse-package*)
5912
(import symbol *pprint-sse-package*) (export symbol *pprint-sse-package*)))
5913
(loop for symbol being each external-symbol of :common-lisp
5914
unless (find-symbol (string symbol) *pprint-sse-package*)
5915
do (import symbol *pprint-sse-package*) (export symbol *pprint-sse-package*))
5916
(loop for symbol being each external-symbol of :spocq.i
5917
unless (find-symbol (string symbol) *pprint-sse-package*)
5918
do (import symbol *pprint-sse-package*) (export symbol *pprint-sse-package*))
5919
(import 'cl:nil *pprint-sse-package*)
5920
(export 'cl:nil *pprint-sse-package*)
5922
(defun pprint-sse (form &optional (stream *standard-output*))
5923
(let ((*package* *pprint-sse-package*)
5924
(*readtable* *sse-readtable*))
5925
(labels ((make-pprint-object (term)
5926
(cond ((variable-p term)
5927
(make-pprint-variable :symbol term))
5928
((spocq:boolean-p term)
5929
(if (spocq:boolean-value term) 'spocq.a:|true| 'spocq.a:|false|))
5930
((triple-form-p term)
5931
(make-pprint-triple :terms (mapcar #'make-pprint-object (rest term))))
5932
((and (symbolp term) (iri-p term))
5933
(spocq:make-iri (iri-lexical-form term)))
5936
(setf form (map-tree #'make-pprint-object form))
5937
(write form :stream stream :pretty t :right-margin 120 :miser-width 20 :case :downcase))
5940
;;; (pprint (mapcar #'abstract-bgp (query-bgps #p"P-LIBRARY:org;datagraph;spocq;src;test;rspec;tests;data-sp2b;q7.sparql")))
5946
(defstruct (stative-verb (:include verb)))
5947
(defstruct (property-path (:include stative-verb)))
5948
(defstruct (property-path-verb (:include property-path))
5952
(defstruct (unary-property-path (:include property-path))
5954
(defstruct (nary-property-path (:include property-path))
5957
(defstruct (and-property-path (:include nary-property-path)))
5958
(defstruct (bounded-property-path (:include unary-property-path))
5961
(defstruct (inverted-property-path (:include unary-property-path)))
5962
(defstruct (negated-property-path (:include unary-property-path)))
5963
(defstruct (or-property-path (:include nary-property-path)))
5964
(defstruct (sequence-property-path (:include nary-property-path)))
5965
(defstruct (zero-length-property-path (:include unary-property-path)))
5967
(defstruct (active-verb (:include verb))
5971
(defstruct (view-verb (:include active-verb))
5972
"Encapsulate a view reference as a statement pattern predicate
5973
Include the parameter and result dimensions"
5978
(defstruct (function-verb (:include active-verb))
5979
"Encapsulate a function reference as a statement pattern predicate
5980
Include the parameter and result dimensions.
5981
Either function call/return or continuation passsing serves as the linkage mechanism,
5982
depending on the wrapped function type."
5985
(defgeneric implies-bounded-property-path (verb)
5986
(:method ((verb t)) nil)
5987
(:method ((verb bounded-property-path)) t)
5988
(:method ((verb unary-property-path)) (implies-bounded-property-path (unary-property-path-element verb)))
5989
(:method ((verb nary-property-path)) (some #'implies-bounded-property-path (nary-property-path-elements verb))))
5991
;;; active-verb operators
5993
(defclass function-verb-function (standard-generic-function)
5995
(:metaclass c2mop:funcallable-standard-class))
5997
(defmethod make-load-form ((object view-verb) &optional env)
5998
(declare (ignore env))
5999
(values `(make-view-verb :url ,(view-verb-url object)
6000
:parameters ',(view-verb-parameters object)
6001
:results ',(view-verb-results object))
6004
(defmethod make-load-form ((object function-verb) &optional env)
6005
(declare (ignore env))
6006
(values `(make-function-verb :function ',(function-verb-function object)
6007
:parameters ',(function-verb-parameters object)
6008
:results ',(function-verb-results object))
6011
(defgeneric invoke-active-verb (active-verb repository-handle continuation &rest arguments)
6012
(:documentation "invoke the verb's implementation arranging to pass the arguments to the
6013
invocation and pass the results to the continuation.")
6015
(:method ((verb symbol) transaction continuation &rest arguments)
6016
"A function verb invokes the respective function directly and apply the continuation to the result."
6017
(declare (dynamic-extent arguments))
6018
(cond ((fboundp verb)
6019
(apply #'invoke-active-verb (symbol-function verb) transaction continuation arguments))
6020
(t ; shouldn't happen
6021
(log-warn "invoke-active-verb: unbound function: ~s" verb)
6022
(error 'undefined-function :name verb))))
6024
(:method ((verb function-verb) transaction continuation &rest arguments)
6025
"A function verb invokes the respective function directly and apply the continuation to the result."
6026
(declare (dynamic-extent arguments))
6027
(apply #'invoke-active-verb (function-verb-function verb) transaction continuation arguments))
6029
(:method ((function function) transaction continuation &rest arguments)
6030
"Once resolved to a simple function, apply it, collect the results and continue with those.
6031
This is intended for feneral operators, for which the transaction does not appear among the arguments."
6032
(declare (dynamic-extent arguments))
6033
(multiple-value-call continuation (apply function arguments)))
6035
(:method ((function function-verb-function) transaction continuation &rest arguments)
6036
"A function-verb function is invoked by supplying both the continuation and the transaction
6037
as well as the actual arguments, to allow it to govern invocation of the continuation to (repeated) results."
6038
(declare (dynamic-extent arguments))
6039
(apply function continuation transaction arguments))
6041
(:method ((verb view-verb) transaction continuation &rest arguments)
6042
"Each verb instance - that is, each call site, must generate its own execution graph.
6043
In order to accomplish that, each generates its own query to mediate the sub-select.
6044
This query is initialized once, which compiles its initialization function.
6045
For each invocation, that function is invoked anew to return a new solution generator as the
6046
head of a new execution graph.
6047
The invocation establishes the sip arguments as dynamic bindings, initiates the query and
6048
passes each result solution to the continuation in turn - with necessary dimension mapping.
6050
(adapted from run-sip-service-step and compute-pattern-graph-names)"
6051
(declare (dynamic-extent arguments))
6052
(let ((query (view-verb-query verb)))
6054
(let ((service-repository (service-repository (view-verb-url verb))))
6055
(unless (equalp (repository-id service-repository) (repository-id *repository*))
6056
(error "inter-repository view: ~s -- ~s"
6057
(repository-id service-repository) (repository-id *repository*)))
6058
(let* ((view-name (repository-resource-view-name service-repository (view-verb-url verb)))
6059
(query-text (or (authorized-repository-view service-repository view-name (task-agent *task*))
6060
(spocq.e:resource-not-found-error :identifier (view-verb-url verb))))
6061
(query-expression (parse-sparql query-text)))
6062
(setf query (make-query :sse-expression query-expression
6063
:id (make-service-task-id)
6064
:dynamic-bindings nil
6065
:repository-id (repository-id service-repository)
6068
:sparql-expression query-text
6069
:agent (system-agent)
6071
:response-content-type mime:application/sparql-results+json))))
6072
(initialize-task query)
6073
(setf (view-verb-query verb) query))
6074
(with-task-environment (:task query)
6075
(with-open-repository ((task-repository query))
6076
(let* ((generator (funcall (task-initialization-function query) query))
6077
(expression (solution-generator-expression generator))
6078
(channel (solution-generator-channel generator))
6079
(continuation-dimensions (view-verb-results verb))
6080
(result-dimensions (solution-generator-dimensions generator))
6081
(bindings (cons result-dimensions arguments))
6082
(result-map (view-verb-result-map verb))
6085
(setf result-map (loop for dim in result-dimensions collect (position dim continuation-dimensions)))
6086
(setf (view-verb-result-map verb) result-map))
6087
(setf (task-dynamic-bindings query) bindings)
6088
(query-run-in-thread query expression)
6089
(do-pages (solutions channel)
6090
(loop for index from 0 below (array-dimension solutions 0)
6091
with args = (make-list (length result-map))
6092
do (loop for args-list on args by #'cdr
6093
for position in result-map
6094
do (setf (first args-list) (aref solutions index position))
6095
finally (apply continuation args))
6100
( ;; testing subquery execution time: x10 = ca 170ms
6101
(let* ((text "select count(*) where {?s ?p ?o}")
6102
(repository "james/test")
6103
(query (make-query :sse-expression (parse-sparql text)
6104
:id (make-service-task-id)
6105
:dynamic-bindings nil
6106
:repository-id repository
6109
:sparql-expression text
6110
:agent (system-agent)
6112
:response-content-type mime:application/sparql-results+json)))
6113
(initialize-task query)
6114
(flet ((run-query ()
6115
(with-task-environment (:query query)
6116
(with-open-repository ((task-repository query))
6118
(let* ((generator (funcall (task-initialization-function query) query))
6119
(expression (solution-generator-expression generator))
6120
(channel (solution-generator-channel generator))
6122
(query-run-in-thread query expression)
6123
(do-pages (solutions channel)
6124
(setf results (append results (term-value-field solutions))))
6126
(time (loop for i below 10
6127
collect (list i (run-query))))))
6129
(time (loop for i below 10
6130
collect (list i (test-sparql "select count(*) where {?s ?p ?o}" :repository-id "james/test"))))
6133
;;; property path operators
6135
(defun property-path-element (path) (unary-property-path-element path))
6136
(defun property-path-elements (path) (nary-property-path-elements path))
6137
(defun property-path-max (path) (bounded-property-path-max path))
6138
(defun property-path-min (path) (bounded-property-path-min path))
6140
;; define load forms as required for tests
6141
(defmethod make-load-form ((object property-path-verb) &optional env)
6142
(declare (ignore env))
6143
(values `(make-property-path-verb :iri ,(property-path-verb-iri object))
6146
(defmethod make-load-form ((object bounded-property-path) &optional env)
6147
(declare (ignore env))
6148
(values `(make-bounded-property-path :min ,(bounded-property-path-min object)
6149
:max ,(bounded-property-path-max object)
6150
:element ,(bounded-property-path-element object))
6153
(defun make-list-property-path ()
6154
(make-sequence-property-path :elements (list (make-bounded-property-path :element (make-property-path-verb :iri |rdf|:|rest|) :min 0 :max nil)
6155
(make-property-path-verb :iri |rdf|:|first|))))
6157
(defmethod expression-variables ((path unary-property-path))
6158
(expression-variables (unary-property-path-element path)))
6160
(defmethod expression-variables ((path nary-property-path))
6161
(expression-variables (nary-property-path-elements path)))
6164
(defmethod expression-predicates ((path property-path-verb))
6165
(list (property-path-verb-iri path)))
6167
(defmethod expression-predicates ((path unary-property-path))
6168
(expression-predicates (unary-property-path-element path)))
6170
(defmethod expression-predicates ((path nary-property-path))
6171
(reduce #'union (nary-property-path-elements path) :key #'expression-predicates))
6174
(defmethod print-object :around ((object property-path) stream)
6177
(defmethod print-object ((object property-path-verb) stream)
6178
(format stream "<~/format-sse-iri-namestring/>" (property-path-verb-iri object)))
6180
(defmethod print-object ((object and-property-path) stream)
6181
(format stream "(~{~s~^&~})" (property-path-elements object)))
6183
(defmethod print-object ((object bounded-property-path) stream)
6184
(princ (property-path-element object) stream)
6185
(let ((min (property-path-min object))
6186
(max (property-path-max object)))
6187
(cond ((and (eql min 0) (null max))
6188
(write-char #\* stream))
6189
((and (eql min 0) (eql max 1))
6190
(write-char #\? stream))
6191
((and (eql min 1) (null max))
6192
(write-char #\+ stream))
6194
(format stream "{~@[~a~],~@[~a~]}" min max)))))
6196
(defmethod print-object ((object inverted-property-path) stream)
6197
(format stream "^~s" (property-path-element object)))
6199
(defmethod print-object ((object negated-property-path) stream)
6200
(format stream "!~s" (property-path-element object)))
6202
(defmethod print-object ((object or-property-path) stream)
6203
(format stream "(~{~s~^|~})" (property-path-elements object)))
6205
(defmethod print-object ((object sequence-property-path) stream)
6206
(format stream "(~{~s~^/~})" (property-path-elements object)))
6208
(defmethod print-object ((object zero-length-property-path) stream)
6209
(format stream "~s{0,0}" (property-path-element object)))
6216
(define-declaration spocq.e:base-dimensions (declaration &optional env)
6217
"Specify the dimensions expected to be available in the dynamic extent of the
6218
body evaluation. Intended for bgp, such that they are compiled to accept such a source
6219
and provide a propagation channel to be linked with that source during reduction."
6221
(destructuring-bind (tag . dimensions) declaration
6222
(assert (every #'symbolp dimensions))
6223
(let ((old (when env (declaration-information 'spocq.e:base-dimensions env))))
6224
(values :declare (cons tag (cons dimensions old))))))
6226
(define-declaration spocq.e::cardinality (declaration &optional env)
6227
"Specify the estimated cardinality for some form.
6228
Used in particular when compiling bgps.
6229
It is not actually passed trhough the compiler, just from the macroexpansion
6230
to the step which compiles the bgp."
6232
(declare (ignore env))
6233
(destructuring-bind (tag cardinality) declaration
6234
(values :declare (list tag cardinality))))
6236
(define-declaration spocq.e:dimensions (declaration &optional env)
6237
"Specify the dimensions available within an extant reduction context:
6238
- filter operators internal to a bgp, which need to know how to extract their arguments.
6241
(destructuring-bind (tag . dimensions) declaration
6242
(let ((old (when env (declaration-information 'spocq.e:dimensions env))))
6243
(values :declare (cons tag (cons dimensions old))))))
6245
(defmacro spocq.e::with-dimensions (dimensions &body body)
6246
`(locally (declare (spocq.e::dimensions ,@dimensions)) ,@body))
6248
(define-declaration spocq.e:join-dimensions (declaration &optional env)
6249
"Assert the dimensions effective for a join operation. Used by
6250
- filter operators internal to the join, which need to know how to extract their arguments,
6251
- bgp compilation to know which dimenions to project."
6253
(declare (ignore env))
6254
(destructuring-bind (tag join left right) declaration
6255
(values :declare (list tag join left right))))
6257
(define-declaration spocq.e::join-scope (declaration &optional env)
6258
"Assert a new join scope - each adds to any outer scope."
6259
(destructuring-bind (tag scope) declaration
6260
(let ((old (when env (declaration-information 'spocq.e::join-scope env))))
6261
(values :declare (list* tag scope old)))))
6263
(defmacro spocq.e::with-join-scope (scope &body body)
6264
`(locally (declare (spocq.e::join-scope ,scope)) ,@body))
6266
(define-declaration spocq.e::processing-mode (declaration &optional env)
6267
"Assert a processing mode.
6268
:synchronous causes the match to happen at the point where the result is to be used.
6269
:asynchronous permits reordered execution and solution propagation among agp instances.
6270
:collated applies to match/scan behaviour of collates statement patterns.
6271
The former is the special case for exist forms."
6272
(declare (ignore env))
6273
(destructuring-bind (tag mode) declaration
6274
(assert (member mode '(:asynchronous :synchronous :collated)) ()
6275
"Invalid processing mode: ~s." mode)
6276
(values :declare (cons tag mode))))
6278
(defmacro spocq.e::with-processing-mode (mode &body body)
6279
`(locally (declare (spocq.e::processing-mode ,mode)) ,@body))
6281
(define-declaration spocq.e:projection-dimensions (declaration &optional env)
6282
"Specify the dimensions intended to be projected.
6283
Used to limit the dimensions in intermediate forms, once they are no longer required.
6284
Overrides any specifications from a outer scope."
6285
(declare (ignore env))
6286
(destructuring-bind (tag . dimensions) declaration
6287
(assert (every #'symbolp dimensions))
6288
(values :declare (cons tag dimensions))))
6290
(define-declaration spocq.e:reference-dimensions (declaration &optional env)
6291
"Specify the dimensions referenced in some form.
6292
Used to indicate field dimensions required by the expression - eg a filter or a binding.
6293
Augments any specifications from a outer scope."
6294
(destructuring-bind (tag . new-dimensions) declaration
6295
(let ((old (when env (declaration-information 'spocq.e::reference-dimensions env))))
6296
(values :declare (list* tag (union-dimensions new-dimensions old))))))
6297
(defmacro spocq.e:with-reference-dimensions (dimensions &body body)
6299
`(locally (declare (spocq.e:reference-dimensions ,@dimensions)) ,@body)
6304
(define-declaration spocq.e:temporal-attribute (declaration &optional env)
6305
"Capture a temporal attribute which has been articulated in some reference to
6306
a pattern variable. Make it available for the BGP evaluation process to extract it from
6307
the statement revision map."
6308
(destructuring-bind (tag . form) declaration
6309
(let ((old (when env (declaration-information 'spocq.e:temporal-attribute env))))
6310
(values :declare (cons tag (cons form old))))))
6312
(define-declaration spocq.e:version-constraint (declaration &optional env)
6313
"Capture a temporal constraint which has been articulated in some reference to
6314
a pattern variable. Make it available for the BGP evaluation process to apply it to
6315
the statement matching process."
6316
(destructuring-bind (tag form) declaration
6317
(let ((old (when env (declaration-information 'spocq.e:version-constraint env))))
6318
;; allow nil to shadow outer declarations in a sub-select
6319
(values :declare (cons tag (when form (cons form old)))))))
6321
(defmacro spocq.e:with-version-constraint (constraint &body body)
6322
`(locally (declare (spocq.e:version-constraint ,constraint)) ,@body))
6325
(define-declaration spocq.e::sort-order (declaration &optional env)
6326
"Communicate the sort precedence to the current evaluation tree.
6327
For a given quera, there can be at most one set of solution modifiers and of them
6328
at most one order clause."
6329
(destructuring-bind (tag variables) declaration
6330
(let ((old (when env (declaration-information 'spocq.e::sort-order env))))
6331
;; there should be at most one order clause in a query
6333
(log-warn "Shadowing order precedence: ~s . ~s"
6335
(values :declare (cons tag variables)))))
6337
(defmacro spocq.e::with-sort-order (variables &body body)
6338
(assert (every #'variable-p variables) ()
6339
"Invalid sort order: ~s" variables)
6340
`(locally (declare (spocq.e::sort-order ,variables)) ,@body))
6344
;;; stream utilities
6346
(defgeneric read-file (file)
6347
(:method ((file pathname))
6348
(with-open-file (stream file :direction :input :element-type '(unsigned-byte 8))
6349
(read-stream stream :length (file-length stream))))
6350
(:method ((file string))
6351
(read-file (pathname file))))
6353
(defgeneric read-stream (stream &key length eof-p limit)
6355
"read the content of a STREAM into a string, with an initial buffer size, LENGTH.
6356
the result string is a simplestring, which has been extended or truncated as appropriate.
6357
a null byte terminates the input")
6359
(:method ((stream stream) &key (length 1024) (eof-p nil) (limit nil))
6360
(let ((buffer (make-array length :adjustable t :fill-pointer 0 :element-type 'character))
6361
(decoder (content-encoding-byte-decoder (content-encoding :utf-8)))
6364
(flet ((utf8-byte-reader (ignore)
6365
(declare (ignore ignore))
6366
(let ((byte (read-byte stream nil nil)))
6367
(cond ((or (null byte) (zerop byte))
6368
(return-from :read-bytes))
6369
((and limit (> (incf count) limit))
6370
(error "read-stream: limit exceeded: ~s" limit))
6373
(declare (dynamic-extent #'utf8-byte-reader))
6374
(loop (vector-push-extend (funcall decoder #'utf8-byte-reader nil) buffer))))
6375
(log-debug "read-stream: ~s." buffer)
6376
(when (and eof-p (zerop (length buffer)))
6377
(error 'end-of-file :stream stream))
6378
(subseq buffer 0 (length buffer)))))
6380
(defun call-with-input-from-vector (operator data &key ((:agent *agent*) *agent*))
6381
(funcall operator (make-instance 'DE.SETF.UTILITY.IMPLEMENTATION::VECTOR-INPUT-STREAM
6384
(defmacro with-input-from-vector ((stream vector &rest args) &body body)
6385
(let ((op (gensym)))
6386
`(flet ((,op (,stream) ,@body))
6387
(declare (dynamic-extent #',op))
6388
(call-with-input-from-vector #',op ,vector ,@args))))
6390
(defun test-query-loop (query &key (count 1) (task-id nil)
6391
(repository-id (error "repositor-id is required."))
6392
(response-content-type mime:application/sparql-results+json)
6393
(request-content-type mime:application/sparql-query)
6397
(unless *run-state* (initialize-spocq))
6398
(let ((input-string (format nil "((:TASK-ID ~s)
6400
(:RESPONSE-CONTENT-TYPE ~s)
6401
(:REQUEST-CONTENT-TYPE ~s)
6407
(or task-id (make-internal-task-id))
6409
(string (type-of response-content-type))
6410
(string (type-of request-content-type))
6414
(*agent* (unless (or api-key agent-id) (system-agent)))
6417
(with-input-from-vector (*standard-input* input-string)
6418
(let ((*request-processor* nil))
6421
(let ((*standard-output* stream)) (main-query-loop) (finish-output stream))
6422
(with-output-to-string (*standard-output*) (main-query-loop)))))))
6426
;;; basic http streamed request support
6428
(defmacro with-http-request-stream ((request-stream response-stream location &rest options) &body body)
6429
(let ((op (gensym "with-http-request-stream-")))
6430
`(flet ((,op (,request-stream ,response-stream)
6431
(declare (ignorable ,request-stream ,response-stream))
6433
(declare (dynamic-extent #',op))
6434
(call-with-http-request-stream #',op ,location ,@options))))
6437
(defgeneric http-method-key (key)
6438
(:method ((key string))
6439
(http-method-key (or (find-symbol (string-upcase key) :keyword) (error "invalid http request method: ~s" key))))
6440
(:method ((key (eql :post)))
6442
(:method ((key (eql :put)))
6444
(:method ((key (eql :get)))
6447
(error "invalid http request method: ~s" key)))
6450
(defgeneric call-with-http-request-stream (operation location &key method content-type accept content-length follow
6452
(:documentation "arrange for a connection to the given http location and invoke the
6453
given operator passing the request or response stream respective the http method.
6454
as implemented, the stream is uni-directional")
6455
(:method (operation (location string) &key (method "GET") content-type accept content-length content-pathname (follow t))
6456
(let* ((method-key (http-method-key method))
6457
(process (run-program "/usr/bin/curl" ;; "/opt/dydra/lib/exec/curl"
6458
`("-s" "-X" ,(string method-key)
6460
;; "--trace-ascii" "/dev/stdout"
6461
,@(when follow '("-L"))
6462
,@(when content-pathname `("--data-binary" ,(format nil "@~a" (namestring content-pathname))))
6463
,@(when content-type `("-H" ,(format nil "Content-Type: ~a" (mime:mime-type-namestring content-type))))
6464
,@(when content-length `("-H" ,(format nil "Content-Length: ~a" content-length)))
6465
,@(when accept `("-H" ,(format nil "Accept: ~a" (mime:mime-type-namestring accept))))
6470
(request-stream (run-program-input process))
6471
(response-stream (run-program-output process)))
6472
(unwind-protect (multiple-value-prog1 (funcall operation request-stream response-stream)
6473
(sb-ext:process-wait process))
6474
(run-program-close process))))
6476
(:method (operation (location spocq:iri) &rest args)
6477
(apply #'call-with-http-request-stream operation (spocq:iri-lexical-form location) args)))
6479
(defgeneric file-type-media-type (type &optional error-p)
6480
(:documentation "Return the expected or declared mime type for a given pathname type.
6481
Permit url, pathname and string arguments
6482
LOCATION : (or pathname string url stream)
6483
VALUE : mime:mime-type")
6485
(:method ((location pathname) &optional (error-p t))
6486
"Given a pathname, use its file type"
6487
(file-type-media-type (pathname-type location) error-p))
6489
(:method ((location spocq:iri) &optional (error-p t))
6490
(file-type-media-type (puri:uri location) error-p))
6492
(:method ((location puri:uri) &optional (error-p t))
6493
(let* ((path (puri:uri-path location))
6494
(type (when path (first (last (split-string path "."))))))
6496
(file-type-media-type type error-p))
6498
(error "media type not found: ~a" location))
6501
(:method ((file-type string) &optional (error-p t))
6502
(cond ((gethash file-type (file-type-media-types)))
6504
(error "media type not found: ~a" file-type))
6507
;;; (file-type-media-type #p"test.nq")
6509
(defparameter *file-type-media-types* nil)
6510
(defun file-type-media-types ()
6511
(or *file-type-media-types*
6512
(setq *file-type-media-types*
6513
(compute-file-type-media-types))))
6514
(defun compute-file-type-media-types ()
6515
(let ((cache (make-hash-table :test 'equalp)))
6516
(labels ((cache-type (class)
6517
(let* ((name (class-name class))
6518
(media-type (when (boundp name) (symbol-value name)))
6519
(type (when (typep media-type '(and mime:mime-type (not de.setf.utility::delegate-mime-type)))
6520
(de.setf.utility.implementation::get-mime-type-file-type media-type))))
6521
(when (and (stringp type) (null (gethash type cache)))
6522
(setf (gethash type cache) media-type))
6523
(loop for subclass in (c2mop:class-direct-subclasses class)
6524
do (cache-type subclass)))))
6525
(cache-type (find-class 'mime:mime-type)))
6528
(:documentation "extension functions"
6530
"Extension operators are implemented as static native functions which a query expression designates
6531
with iri which are represented as symbols. The compiler recognizes an extension operator and generates
6532
code to invoke it respective its context iff the iri's vocabulary is present as one of the packages in the
6533
*iri-packages* list. There are two supported contexts:
6534
- pattern statement predicates : the bgp compiler compiles the statement match into an out-of-line call
6535
to funcall-extension
6536
- non-built-in operators : the form is compiled as a native function call, for which the standard
6537
expansion maps solution field references into interned model terms. In this context a macro-expansion
6538
can elect to suppress the mapping for operators which are better implemented directly in terms of term
6541
The partiallibraries exist
6542
- virtuoso/gis : algebra;operators;dydra-operators
6543
- jena/ARQ operators : algebra;operators;jena-operators
6544
nb. in this case, the definitions [http://jena.sourceforge.net/ARQ/library-propfunc.html#functions]
6545
require that a logical operator serve both contexts. in once case it contributes bindings to the
6546
bgp match process and in the other it returns a boolean as a filter predicate.")
6549
(defgeneric extension-operator-p (object)
6550
(:documentation "Return true if the given OBJECT is a symbol in a package which is present
6551
in the *iri-packages* list.")
6553
(:method ((object symbol))
6554
(when (and (find (symbol-package object) *iri-packages*)
6556
(not (macro-function object)))
6558
(:method ((object t))
6561
(defgeneric funcall-extension (repository-handle context subject property-extension object continuation)
6562
(:documentation "Invoke a predicate extension operator. The base implementation is specialized for
6563
symbol terms, to invoke the term's function on the repository, the pattern's terms - (context, subject,
6564
property (the term itself), object) and a continuation. The function produces results by invoking the
6565
continuation on the respectively matched context, subject, property, and object.
6566
The term value domain is term numbers.
6567
There are other implementation specific to given operators, eg. arq:member, which work differently.")
6569
(:method (repository-handle context subject (property symbol) object continuation)
6570
(funcall property repository-handle context subject property object continuation)))
6572
(defgeneric delegate-to-path-query (continuation transaction context subject operator object)
6573
(:argument-precedence-order operator transaction continuation context subject object)
6574
(:method ((continuation t) (transaction t) context subject (predicate property-path-verb) object)
6575
;; extract the solution generator from the operator
6576
;; if this is present in the body, bind that
6577
;; if the subject and/or predicate are bound but were present in the antecedant, bind them
6578
;; initiate the generator in its own thread
6579
;; iterate over results and pass them to the continuaton
6580
(let* ((predicate-term-number (intern-property-path predicate))
6581
(generator (property-path-verb-solution-generator predicate)))
6583
(let ((query (property-path-verb-query predicate)))
6584
(assert (consp query) ()
6585
"Invalid path verb query: ~s" query)
6586
(let ((query-function (spocq-compile query)))
6587
(setf generator (setf (property-path-verb-solution-generator predicate)
6588
(funcall query-function))))))
6589
(let ((channel (solution-generator-channel generator)))
6590
(assert (= (length (solution-generator-dimensions generator)) 2) ()
6591
"Invalid path generator: ~s: ~s" predicate generator)
6592
(query-run-in-thread *task* generator)
6593
(do-pages (page channel)
6594
(loop for i below (page-length page)
6595
do (funcall continuation context (aref page i 0) predicate-term-number (aref page i 1))))))))
6600
(defgeneric state-predicate-p (predicate)
6601
(:method ((predicate (eql '|urn:dydra|:|event|)))
6602
"predicate for event bindings in collated patterns"
6604
(:method ((predicate t))
6607
(defun plist-difference (plist keys)
6608
(loop for (key value) on plist by #'cddr
6609
unless (member key keys)
6613
(defun plist-merge (plist &rest args)
6614
(declare (dynamic-extent args))
6615
(loop for (key value) on args by #'cddr
6616
unless (getf key plist)
6617
do (setf plist (list* key value plist)))
6620
;;; (plist-difference '(:a 1 :b 2) '(:a)) (plist-difference '() '(:c))
6623
;;; syslog trace output
6625
(defclass syslog-stream (SB-GRAY:FUNDAMENTAL-STREAM)
6626
((buffer :initform (make-array 128 :element-type 'character :fill-pointer 0 :adjustable t)
6627
:reader stream-buffer)))
6629
(defmethod stream-force-output ((s syslog-stream))
6630
(let ((buffer (stream-buffer s)))
6631
(when (plusp (length buffer))
6633
(setf (fill-pointer buffer) 0))))
6635
(defmethod stream-finish-output ((s syslog-stream))
6636
(stream-force-output s))
6638
(defmethod stream-write-char ((s syslog-stream) c)
6639
(let ((buffer (stream-buffer s)))
6641
(#\n (vector-push-extend #\\ buffer)
6642
(vector-push-extend #\n buffer)
6643
(stream-force-output s))
6644
(t (vector-push-extend c buffer))) buffer))
6646
(defmethod stream-write-string ((s syslog-stream) line &optional (start 0) (end (length line)))
6647
(stream-force-output s)
6648
(log-warn! (subseq line start end)))
6650
(defmethod stream-write-sequence ((s syslog-stream) (string string) &optional (start 0) (end (length string)))
6651
(stream-write-string s string start end))
6654
(defun conditional-delete-file (pathname)
6655
"Delete the designated file if it exists."
6656
(when (and pathname (probe-file pathname))
6657
(delete-file pathname)))
6659
(defgeneric resource-pathname-element (resource)
6660
(:method ((element string)) element)
6661
(:method ((element null)) nil)
6665
(defun tmp-import-pathname (&rest components)
6666
(declare (dynamic-extent components))
6667
(loop with string-components = (mapcar #'resource-pathname-element components)
6668
for pathname = (make-pathname :name (format nil "import~{~@[-~a~]~}-~a"
6670
(make-v1-uuid-string))
6671
:defaults (import-root-pathname))
6672
for i from 0 below 10
6673
unless (probe-file pathname)
6675
;;; (tmp-import-pathname "asdf" "qwer")
6677
(defun tmp-export-pathname (&rest components)
6678
(declare (dynamic-extent components))
6679
(loop with string-components = (mapcar #'resource-pathname-element components)
6680
for pathname = (make-pathname :name (format nil "export~{~@[-~a~]~}-~a"
6682
(make-v1-uuid-string))
6683
:defaults (import-root-pathname))
6684
for i from 0 below 10
6685
unless (probe-file pathname)
6688
(defun file-hash-pathname (pathname)
6689
"return a relative pathname to identify a given file by content hash."
6690
(let* ((hash (make-sha256-digest pathname))
6691
(prefix (subseq hash 0 2)))
6692
(make-pathname :name hash :directory `(:relative ,prefix))))
6694
(defgeneric mime-type-url (object &key if-does-not-exist)
6695
(:documentation "lookup the respective url for a media type from those registered in the 'mime' package.
6696
Intended to be combined with <https://w3id.org/rdfp/mediaType> to record ldp media types.")
6697
(:method ((mime-type mime:mime-type) &rest args)
6698
(declare (dynamic-extent args))
6699
(apply #'mime-type-url (string-downcase (type-of mime-type)) args))
6700
(:method ((string string) &key (if-does-not-exist :error))
6701
(or (find-symbol string (load-time-value (find-package "mime")))
6702
(ecase if-does-not-exist
6704
(:error (spocq.e:resource-not-found-error :identifier string))))))
6706
(defgeneric url-mime-type (object)
6707
(:method ((non-url t))
6709
(:method ((mime-type-url symbol))
6710
(when (eq (symbol-package mime-type-url) (load-time-value (find-package "mime")))
6711
(find-symbol (symbol-name mime-type-url) (load-time-value (find-package :mime)))))
6712
(:method ((mime-type-url spocq:iri))
6713
(let ((name (spocq:iri-lexical-form mime-type-url))
6714
(prefix (load-time-value (package-name (find-package "mime")))))
6715
(when (string-equal name prefix :end1 (min (length name) (length prefix)))
6716
(mime:mime-type (subseq name (length prefix)) :if-does-not-exist nil))))
6717
(:method ((string string))
6718
(find-symbol string (load-time-value (find-package "mime")))))
6720
(defparameter *sparql-query-prototype* "
6722
from <urn:dydra:all>
6723
from named <http://example.org/rdf>
6727
values ?location { <http://example.com> 'http://example.com' }
6728
service ?location { ?ss ?sp ?so }
6738
(defgeneric unescape-mysql-string (string)
6739
(:method ((string null))
6741
(:method ((string string))
6742
(flet ((unescape-mysql-character (target-string start end match-start match-end reg-starts reg-ends)
6743
(declare (ignore start end match-end reg-starts reg-ends))
6744
(let ((char (char target-string (1+ match-start))))
6746
(#\b #.(make-string 1 :initial-element #\backspace))
6747
(#\n #.(make-string 1 :initial-element #\newline))
6748
(#\r (make-string 1 :initial-element #\return))
6749
(#\t (make-string 1 :initial-element #\tab))
6750
(t (make-string 1 :initial-element char))))))
6751
(cl-ppcre:regex-replace-all (load-time-value (cl-ppcre:create-scanner "\\\\."))
6753
#'unescape-mysql-character))))
6758
(defmacro form-s (field bindings &environment env)
6759
`(with-dimensions ,(mapcar #'first bindings)
6760
(run-form-s ,field ',bindings)))
6762
(defmacro form-p (field projection &environment env)
6763
`(with-dimensions ,projection
6764
(run-form-p ,field ',projection)))
6766
(defmacro form-e (field &environment env)
6767
`(run-form-e ,field ',(declaration-information 'dimensions env)))
6770
(defun run-form-e (field dimensions)
6771
(list 'as-form-e field dimensions))
6773
(defun run-form-p (field projection)
6774
(list 'as-form-e projection field))
6776
(defun run-form-s (field bindings)
6777
(list 'as-form-s bindings field))
6779
(pprint (form-s (form-p (form-e '(?::s ?::p ?::o)) (?::s ?::p)) ((?::x 1) (?::s ?::s))))
6781
(aprint (rewrite-common-bgps (query-bgps #p"P-LIBRARY:org;datagraph;spocq;src;test;rspec;tests;data-sp2b;q7.sparql")
6782
:factors' ((0 . bgpse-bgp-cardinality) (1 . bgpse-pattern-cardinality))))
6784
(defgeneric compute-subexpression-statistics (query)
6785
(:method ((original-bgps cons))
6786
(let ((reduced-bgps (rewrite-common-bgps original-bgps))
6787
(original-patterns (reduce #'append original-bgps :key #'rest))
6790
(dolist (bgp reduced-bgps)
6792
(dolist (elt (rest bgp))
6795
(unless (member elt new-bgps :test #'equalp)
6797
(setf new-patterns (append (rest elt) new-patterns))))
6799
(push elt new-patterns)))))
6800
(let ((original-bgp-count (length original-bgps))
6801
(new-bgp-count (length new-bgps))
6802
(original-pattern-count (length original-patterns))
6803
(new-pattern-count (length new-patterns)))
6804
(values (if (plusp original-pattern-count) (float (/ new-pattern-count original-pattern-count) 0.0))
6805
(if (plusp original-bgp-count) (float (/ new-bgp-count original-bgp-count)) 0.0)
6806
new-pattern-count original-pattern-count
6807
new-bgp-count original-bgp-count))))
6809
(:method ((source pathname))
6810
(if (wild-pathname-p source)
6811
(let ((original-bgp-count 0)
6813
(original-pattern-count 0)
6814
(new-pattern-count 0)
6818
(dolist (pathname (directory source))
6820
(multiple-value-bind (pat-pct bgp-pct npc opc nbc obc) (compute-subexpression-statistics pathname)
6823
(incf pat-pct-total pat-pct)
6824
(incf bgp-pct-total bgp-pct)
6825
(incf new-pattern-count npc)
6826
(incf original-pattern-count opc)
6827
(incf new-bgp-count nbc)
6828
(incf original-bgp-count obc))
6829
(format *trace-output* "~&~2$ x~d ~2$ x~d : ~s"
6830
pat-pct opc bgp-pct obc pathname))))
6831
(if (plusp query-count)
6832
(values (/ pat-pct-total query-count)
6833
(/ bgp-pct-total query-count)
6834
(float (/ new-pattern-count query-count)) (float (/ original-pattern-count query-count))
6835
(float (/ new-bgp-count query-count)) (float (/ original-bgp-count query-count)))
6836
(values 0.0 0.0 0.0 0.0)))
6837
(compute-subexpression-statistics (query-bgps source)))))
6839
(compute-subexpression-statistics #p"P-LIBRARY:org;datagraph;spocq;src;test;rspec;tests;data-sp2b;q*.sparql")
6840
(compute-subexpression-statistics #p"P-LIBRARY:org;datagraph;spocq;src;test;rspec;tests;data-r2;**;*.rq")
6842
(defun time-sublis (count)
6843
(let* ((uris (loop for i from 0 below 32
6844
collect (puri:uri (format nil "http://example/~a" i))))
6845
(field (loop for i from 0 below 1024
6847
for uri = (nth x uris)
6848
collect `(spocq.a:|triple| ,uri ,uri ,uri)))
6849
(map (loop for i from 0
6851
collect (cons uri i)))
6853
(time (dotimes (x count)
6854
(incf total (length (sublis map field)))))))