On 2/1/2019 12:39 AM, hashim muqtadir wrote:
> If order to use a struct across namespaces, the module that
> defines the struct must be required into each namespace that uses the
> struct.

Yes, I suspect there's some weird interaction between namespaces too, hence the topic, but that's still pretty vague. After all, the thing defining the structs is the sql package, and my modules/namespaces are importing that.

No.  Look back at your code ... you defined the struct in your own unnamed module just before the definition of  "select/f"


> Eval almost always is the wrong answer.

I wanted to pass a list to select, but select is a macro, and I didn't get how to define a macro on top of that to do what I wanted, since the macro doesn't know that one of the things being passed into it evaluates to a list, so it can't splice that list into select, hence, eval.

Macros can construct calls to other macros or functions.  SQL is too complicated to give a representative example, but you can look at this AT-style time macro.  This is a late stage debugging version with a lot of visible output - cleaned up a bit, the final version is part of a scheduler I wrote.  You are free to do with it whatever you like.


> I recommend that you take a look at section 3.7 of the documentation, "Dynamic Statement Composition and SQL Injection”, which I believe does exactly what you want. Specifically, you want to dynamically construct a select statement.

I did look at that. And up until yesterday I thought "this isn't helping, I need to inject multiple select-expr's!".

And today, I had a look again and thought, hey, what if I use a string that has a comma in it, and, hey, that works!

i.e. the following works:

    #lang racket
    (require sql)
    (select (ScalarExpr:INJECT (unquote "mew, two")) #:from pokemon)

It gives me `(sql-statement "SELECT mew, two FROM pokemon")`

So now I know all I have to do is comma-join my list to form a string, and inject that.

What I do _not_ know if that's how INJECT was meant to be used, or whether I should build a separate inject macro that uses unquote-splice instead that comma-joins the list and does some form of escaping on the columns. My first guess is that properly verifying the items in the list to be single expressions is pretty difficult, so it sounds like a bad idea, and since one select-expr is allowed to yield multiple columns (because the "db" library that will actually get the results doesn't need any info from the query, I think), my approach should be fine.

I haven't looked at the SQL package  [didn't know about it until you mentioned it].


I use my own [really complex] macro for writing free form SQL.  It allows arbitrary Racket code (that produces a string) to be interspliced among the SQL, and the result all is reduced to a string to pass to the DB library.  It's not release material though:  it's good enough for me, but the error reporting is poor and the "language" it accepts is not quite SQL but rather a hybrid of SQL and Racket..

I've been meaning to go back to it.  I'd like to fix it so I can write standard SQL instead of the hybrid language  (SQL's meaning for single quotes, brackets, etc.) , but I never seem to get around to it.  If Ryan's SQL library is good enough, maybe I just retire my own code instead.

George


--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
#lang racket/base

(require
  (for-syntax racket/base
              racket/format
              )

  racket/base
  racket/match
  racket/list
  racket/format
  racket/dict
  racket/date

  )


;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#|

allows free form date/time descriptions in
the style of the Unix "at" command. converts
the description into seconds since the Unix
epoch: 1970-01-01 at 00:00hrs.

the resulting seconds value may be converted
into a usable date structure, or passed to
an alarm event for scheduling.

special words:
  "now" = current date/time
  "today"      - at 00hrs
  "tomorrow"   - at 00hrs
  "this-month" - at 00hrs on the 1st
  "next-month" - at 00hrs on the 1st

dates must be entered in yyyy-mm-dd format.

times may be entered in 12hr or 24hr format.
minutes, seconds, and AM/PM are optional.
AM/PM is ignored if time is unambiguous.

"+ <integer> <unit>" adds a given offset
to the accumulated date/time value. units
may be seconds, minutes, hours, days, or weeks.

units and their typical abbreviations are
recognized in both singular and plural forms.

|#
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


;======================================
;
;  macro interface - generates call to
;  parsing function at runtime
;
;======================================

(define-syntax (at stx)
  (let* [
         (input   (cdr (syntax->datum stx)))
         (input   (map ~a input))
         (fnspec  (list 'apply '+ (list* 'at-time input)))
        ]

    (eprintf "=> ~s~n" fnspec)
    (datum->syntax stx fnspec)
    ))


;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


;======================================
;
;  parse a time/date description
;
;======================================


(define (at-time . input)
  (let loop [
             (input  input)
             (output '())
            ]
    (eprintf "-> ~s~n" input)
    (match input
      
      ; done
      ([? empty?]
       (eprintf "=> ~s~n" (reverse output))
       (map cdr output)
       )


      ; current time
      ([list "now" _ ___ ]
       (loop (cdr input) (cons (cons 'now (current-seconds)) output)))


      ; unit offset
      ([list "+" (pregexp px-integer [list n]) mul _ ___]
       (let [
             (m (dict-ref multipliers mul #f))
             (n (string->number n))
            ]
         (if m
             (loop (cdddr input) (cons (cons 'offset (* n m)) output))
             (error 'at "unknown time unit" mul))
         ))


      ; date (at 00:00:00)
      ([list (pregexp px-date [list _ yy mm dd]) _ ___]
       (let* [
              (yy (if yy (string->number yy) 1970))
              (mm (if mm (string->number mm) 1))
              (dd (if dd (string->number dd) 1))
              (result (find-seconds 0 0 0 dd mm yy))
             ]
         (eprintf ".. date ~a-~a-~a~n" yy mm dd)
         (loop (cdr input) (cons (cons 'date result) output))
         ))


      ; time (either 12hr or 24hr format)
      ([list (pregexp px-time [list _ hh _ mm _ ss a/p]) _ ___]
       (let* [
              (hh (if hh (string->number hh) 0))
              (hh (cond
                    ([and (= hh 12) a/p (string-ci=? a/p "am")] 0)
                    ([and (< hh 12) a/p (string-ci=? a/p "pm")] (+ 12 hh))
                    (else hh)))                 
              (mm (if mm (string->number mm) 0))
              (ss (if ss (string->number ss) 0))
              ; translate as absolute offset from epoch
              (result (find-seconds ss mm hh 1 1 1970 #f))
             ]
         (eprintf ".. time ~a:~a:~a~n" hh mm ss)
         (loop (cdr input) (cons (cons 'time result) output))
         ))


      ; today (at 00:00:00)
      ([list "today" _ ___]
       (let* [
              (today  (current-date))
              (result (find-seconds 0 0 0 [date-day today][date-month 
today][date-year today]))
             ]
         (loop (cdr input) (cons (cons 'today result) output))
         ))


      ; tomorrow (at 00:00:00)
      ([list "tomorrow" _ ___]
       (let* [
              (today  (current-date))
              (result (find-seconds 0 0 0 [date-day today][date-month 
today][date-year today]))
              (result (+ result (dict-ref multipliers "day")))
             ]
         (loop (cdr input) (cons (cons 'tomorrow result) output))
         ))
      

      ; PM - offset (time < 12:00)
      ([list (or "pm" "PM") _ ___]
       (let* [
              (hour12 (* 12 60 60))
              (time (assoc 'time output))
              (ok   (and time (< (cdr time) hour12)))
             ]
         (if ok
             (loop (cdr input) (cons (cons 'pm hour12) output))
             (loop (cdr input) output))
         ))


      ; AM - offset (12:00 <= time < 13:00)
      ([list (or "am" "AM") _ ___]
       (let* [
              (hour12 (* 12 60 60))
              (hour13 (* 13 60 60))
              (time (assoc 'time output))              
              (ok   (and time (or (= (cdr time) hour12) (< hour12 (cdr time) 
hour13)))) 
             ]
         (if ok
             (loop (cdr input) (cons (cons 'am (- hour12)) output))
             (loop (cdr input) output))
         ))


      ; this-month (1st day at 00:00:00)
      ([list "this-month" _ ___]
       (let* [
              (today  (current-date))
              (result (find-seconds 0 0 0 1 [date-month today][date-year 
today]))
             ]
         (loop (cdr input) (cons (cons 'this-month result) output))
         ))

      
      ; next-month (1st day at 00:00:00)
      ([list "next-month" _ ___]
       (let*-values
           [
            ((today)  (current-date))
            ((month year) (month/year [date-month today] [date-year today] +1))
            ((result) (find-seconds 0 0 0 1 month year))
           ]
         (loop (cdr input) (cons (cons 'next-month result) output))
         ))

      
      (else
       (loop (cdr input) output))
      )
    ))
    

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


(define multipliers
  (let* [
         (second 1)
         (minute (* 60 second))
         (hour   (* 60 minute))
         (day    (* 24 hour  ))
         (week   (*  7 day   ))
         (myhash (make-custom-hash member))
        ]
    (dict-set*! myhash
      '("sec" "secs" "second" "seconds") second
      '("min" "mins" "minute" "minutes") minute
      '("hr"  "hrs"  "hour"   "hours"  ) hour
      '(             "day"    "days"   ) day
      '(             "week"   "weeks"  ) week
      )
    myhash))


(define px-time 
#px"(2[0-3]|[01]?[0-9])(:([0-5][0-9])(:([0-5][0-9]))?)?(am|AM|pm|PM)?" )
(define px-date #px"([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])")
(define px-integer #px"[+-]?[[:digit:]]+")


;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


;======================================
;
;  compute ending month & year
;
;======================================

(define (month/year month year offset)
  
  (let* [
         ; offset month for modular arithmetic
         (month  (- month 1))
           
         ; compute ending year
         (year   (let [
                       (op     (if (>= offset 0) + -))
                       (offset (abs offset))
                      ]
                   (if [eq? op +] 
                       (let [(left (- 11 month))]
                         (if (<= offset left)
                             year
                             (+ year (ceiling (/ (- offset left) 12)))
                             ))
                       ;[eq? op -]
                       (if (<= offset month)
                           year
                           (- year (ceiling (/ (- offset month) 12)))
                           )
                       )
                   ))
         
         ; compute ending month
         (month  (modulo (+ month offset) 12))
         (month  (+ month 1))
        ]
    ; return month and year
    (values month year)
    ))


;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Reply via email to