3

I need to generate the following:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
     (clojureql.core/where (apply and (= :tableA.id :tableB.id)
                      [(apply or [(clojureql.predicates/in :tableA.id [1 2])
                                  (clojureql.predicates/in :tableA.id [3 4])])])))

But the "apply or" form needs to be "passed" from another function or macro. Here's what I mean:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
     (clojureql.core/where (apply and (= :tableA.id :tableB.id)
                      [(my-or-f)])))

where this function is:

(defn my-or-f []
    (apply or [(clojureql.predicates/in :tableA.id [1 2])
         (clojureql.predicates/in :tableA.id [3 4])]))

however this function throws this exception:

#<CompilerException java.lang.Exception: Can't take value of a macro: #'clojure.core/or (NO_SOURCE_FILE:2)>

I've also tried using a macro. It compiles but it throws the same exception when I try running the query using this macro

(defmacro my-or-f []
    `(apply or [(clojureql.predicates/in :tableA.id [1 2])
         (clojureql.predicates/in :tableA.id [3 4])]))

Is there another way that I could use the "apply or"?

Thanks.

Ken Redler
  • 23,863
  • 8
  • 57
  • 69
Ron P
  • 225
  • 2
  • 7

2 Answers2

4

The issue is that macros are not functions - they run at compile time and are passed the actual syntax (lists and symbols, primarily), so you can't apply them at runtime to runtime datastructures. Thus, (apply or my-list) throws an error. You have a few options:

(def my-list (list false false 4 false 5))

(some identity my-list) -- returns 4
(reduce #(or %1 %2) my-list) -- returns 4

In practice, you probably want some -- it stops as soon as it reaches an element that returns true, while reduce always traverses the entire list.

Note: you will also have the same problem with your (apply and ...) -- (every? identity ...) will do what you want.

In this case, however, why do you need to use apply at all? This should work equally well:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
                     (clojureql.core/where (and (= :tableA.id :tableB.id)
                                                (my-or-f))))

(defn my-or-f []
  (or (clojureql.predicates/in :tableA.id [1 2])
      (clojureql.predicates/in :tableA.id [3 4])))

One last suggestion - when you require clojureql.predicates, if you replace clojureql.predicates with [clojurecl.predicates :as qlp], you can use qlp in place of clojureql.predicates in the rest of the file, which makes the above (if we also replace clojureql.core with qlc):

(qlc/join (qlc/table db :tableA) (qlc/table db :tableA)
          (qlc/where (and (= :tableA.id :tableB.id)
                          (my-or-f))))

(defn my-or-f []
  (or (qlp/in :tableA.id [1 2])
      (qlp/in :tableA.id [3 4])))

which is much easier to read.

For the purposes of ClojureQL, try (apply clojureql.predicates/or* ...) and (apply clojureql.predicates/and* ...). clojureql's where replaces any and and or it sees with clojureql.predicates/and* and clojureql.or*, so doing this replacement yourself should work. Both and* and or* are functions, not macros, so there shouldn't be any issues.

Retief
  • 3,199
  • 17
  • 16
  • Retief, I've tried your suggestion however this doesn't work with the clojureql library. here's the sql that it generates: SELECT tableA.*,tableA.* FROM tableA JOIN tableA ON ((tableA.id = tableB.id) AND tableA.id IN (1,2)) but the sql that I need to generate (from my first working example) is: SELECT tableA.*,tableA.* FROM tableA JOIN tableA ON ((tableA.id = tableB.id) AND (tableA.id IN (1,2) OR tableA.id IN (3,4))) – Ron P Feb 08 '12 at 17:34
  • The issue is that clojureQL/where is a macro that walks the source tree that you pass it, looking for math, `and` and `or`. In a normal environment, my code would work fine, but this doesn't work for clojureQL. There's probably a workaround, but unfortunately, I don't actually use clojureQL, so I don't know it. One thing that might work is to take my final suggestion (with no list operations) and make my-or-f a macro. – Retief Feb 08 '12 at 17:45
  • @RonP check my edit - I found the information by checking through the clojureql source [here](https://github.com/LauJensen/clojureql) – Retief Feb 08 '12 at 19:10
1

If the amount of parameters you are giving or is known, this is probably the easiest solution:

(apply #(or %1 %2) [nil 10]) ; => 10
ponzao
  • 20,684
  • 3
  • 41
  • 58