#lang scheme
(require "query.ss"
(except-in "query.ss"
make-q:rename*
make-q:union
make-q:intersection
make-q:difference
make-q:product
make-q:projection
make-q:selection)
"prop.ss"
"schema.ss")
(define (hash-image ht)
(for/hash ([(from to) (in-hash ht)])
(values to from)))
(define (merge-renaming fst snd)
(define fst-image (hash-image fst))
(for/fold ([merged fst])
([(old new) (in-hash snd)])
(if (hash-has-key? fst-image old)
(hash-set merged (hash-ref fst-image old) new)
(hash-set merged old new))))
(define pull-up/once
(match-lambda
[(and (struct q:projection (schema (struct q:selection (prop r))))
(? (lambda (q)
(schema-subset? (prop-schema (q:selection-prop (q:projection-r q)))
(q:projection-schema q)))))
(make-q:selection prop (make-q:projection schema r))]
[(struct q:difference ((struct q:selection (A R))
(struct q:selection (A P))))
(make-q:selection A (make-q:difference R P))]
[(struct q:difference ((struct q:selection (A R)) P))
(make-q:selection A (make-q:difference R P))]
[(struct q:union ((struct q:selection (A R))
(struct q:selection (A P))))
(make-q:selection A (make-q:union R P))]
[(struct q:intersection ((struct q:selection (A R))
(struct q:selection (A P))))
(make-q:selection A (make-q:intersection R P))]
[(struct q:intersection ((struct q:selection (A R)) P))
(make-q:selection A (make-q:intersection R P))]
[(struct q:intersection (R (struct q:selection (A P))))
(make-q:selection A (make-q:intersection R P))]
[(struct q:union ((struct q:selection (A R))
(struct q:selection (B R))))
(make-q:selection (make-prop:or A B) R)]
[(struct q:selection (A (struct q:selection (B R))))
(make-q:selection (make-prop:and A B) R)]
[q
q]))
(define simplify/once
(match-lambda
[(struct q:rename* (snd-old->new (struct q:rename* (fst-old->new inner-q))))
(make-q:rename* (merge-renaming fst-old->new snd-old->new) inner-q)]
[(and (struct q:projection (snd-schema (struct q:projection (fst-schema q))))
(? (lambda (q)
(schema-subset? (q:projection-schema q)
(q:projection-schema (q:projection-r q))))))
(make-q:projection snd-schema q)]
[(struct q:selection (A (struct q:selection (A R))))
(make-q:selection A R)]
[(struct q:selection ((? prop-trivial?) R))
R]
[q
q]))
(define push-down/once
(match-lambda
[(struct q:rename* (old->new (struct q:union (r s))))
(make-q:union (make-q:rename* old->new r)
(make-q:rename* old->new s))]
[(struct q:rename* (old->new (struct q:difference (r s))))
(make-q:difference (make-q:rename* old->new r)
(make-q:rename* old->new s))]
[(struct q:rename* (old->new (struct q:intersection (r s))))
(make-q:intersection (make-q:rename* old->new r)
(make-q:rename* old->new s))]
[(struct q:projection (schema (struct q:union (r s))))
(make-q:union (make-q:projection schema r)
(make-q:projection schema s))]
[(struct q:projection (schema (struct q:difference (r s))))
(make-q:difference (make-q:projection schema r)
(make-q:projection schema s))]
[(struct q:projection (schema (struct q:intersection (r s))))
(make-q:intersection (make-q:projection schema r)
(make-q:projection schema s))]
[(struct q:selection (prop (struct q:projection (schema r))))
(make-q:projection schema (make-q:selection prop r))]
[(struct q:selection (A (struct q:product (R P))))
(define-values (B C D) (prop-breakup A (query-schema R) (query-schema P)))
(make-q:selection D (make-q:product (make-q:selection B R) (make-q:selection C P)))]
[(struct q:selection (A (struct q:difference (R P))))
(make-q:difference (make-q:selection A R)
(make-q:selection A P))]
[(struct q:selection (A (struct q:union (R P))))
(make-q:union (make-q:selection A R)
(make-q:selection A P))]
[(struct q:selection (A (struct q:intersection (R P))))
(make-q:intersection (make-q:selection A R)
(make-q:selection A P))]
[q
q]))
(define (bottom-up f q)
(match q
[(struct q:relation (id))
q]
[(struct q:singleton (schema))
q]
[(struct q:union (r s))
(define r-after (bottom-up f r))
(define s-after (bottom-up f s))
(make-q:union (f r-after) (f s-after))]
[(struct q:difference (r s))
(define r-after (bottom-up f r))
(define s-after (bottom-up f s))
(make-q:difference (f r-after) (f s-after))]
[(struct q:intersection (r s))
(define r-after (bottom-up f r))
(define s-after (bottom-up f s))
(make-q:intersection (f r-after) (f s-after))]
[(struct q:product (r s))
(define r-after (bottom-up f r))
(define s-after (bottom-up f s))
(make-q:product (f r-after) (f s-after))]
[(struct q:projection (schema r))
(define r-after (bottom-up f r))
(make-q:projection schema (f r-after))]
[(struct q:selection (prop r))
(define r-after (bottom-up f r))
(make-q:selection prop (f r-after))]
[(struct q:rename* (old->new r))
(define r-after (bottom-up f r))
(make-q:rename* old->new (f r-after))]))
(define (top-down f q)
(match (f q)
[(struct q:relation (id))
q]
[(struct q:singleton (schema))
q]
[(struct q:union (r s))
(make-q:union (top-down f r) (top-down f s))]
[(struct q:difference (r s))
(make-q:difference (top-down f r) (top-down f s))]
[(struct q:intersection (r s))
(make-q:intersection (top-down f r) (top-down f s))]
[(struct q:product (r s))
(make-q:product (top-down f r) (top-down f s))]
[(struct q:projection (schema r))
(make-q:projection schema (top-down f r))]
[(struct q:selection (prop r))
(make-q:selection prop (top-down f r))]
[(struct q:rename* (old->new r))
(make-q:rename* old->new (top-down f r))]))
(define (optimize-query q)
(bottom-up (compose simplify/once pull-up/once simplify/once)
(top-down (compose simplify/once push-down/once simplify/once) q)))
(provide/contract
[optimize-query (query? . -> . query?)]
[simplify/once (query? . -> . query?)]
[pull-up/once (query? . -> . query?)]
[push-down/once (query? . -> . query?)])