Com isso, tem-se um REPL que possibilita a execução de consultas SQL e código Scheme. A implementação usa o próprio REPL do sistema Chicken e alguns eggs como: postgresql (para acesso so Postgres), readline (para edição de linhas de comando, histórico) e stty (para configuração do terminal na leitura de senhas).
O programa
db-repl.scm
usa como argumentos não interativos (opcionais) o usuário do banco de dados, o nome do host e a base de dados, os quais devem ser fornecidos no seguinte formato:<usuario>@<host>/<base de dados>
Quando executado, o programa pede para o usuário digitar a senha.
A associação do REPL de Chicken com a base de dados é feita através da definição de comandos do REPL (
toplevel-command
). Na implementação mostrada abaixo, são definidos três comandos:tables
: mostras as tabelas da base de dados.table
: mostra estrutura da tabela dada como argumento (nome e tipo das colunas e se podem ou não ser nulas).-
: executa a consulta SQL dada como argumento.
Exemplos:
csi -s db-repl.scm mario@localhost/sgpc
Senha: *****
#;1> ,tables
perms
news
users_sites
ticket_comments
ticket_attachments
obj_type
acervos
autores
videos
audios
images
objects
texts
scanners
users
wiki
sites
tickets
#;1> ,table news
news_id integer NO
user_id integer NO
site_id integer NO
timestamp timestamp without time zone YES
title character varying(100) NO
news text YES
#;1> ,- select * from news
(#(2
1
1
#(2008 7 30 19 37 54 271625)
"Teste."
"teste
=== titulo")
#(3
1
1
#(2008 7 30 19 40 27 180765)
"Outra notícia!"
"Aqui vai o texto da notícia.
[[image:http://subversion.tigris.org/branding/images/logo.gif|Logo]]"))
Obviamente, os comandos para acesso ao banco de dados disponibilizados através do REPL podem ser estendidos. O texto PostgreSQL INFORMATION_SCHEMA fornece várias dicas de como extrair informações de bases de dados do Postgres.
Nesta implementação, o parâmetro
pg-repl:conn
armazena o objeto que representa a conexão com o banco de dados, de forma que ele pode ser usado pelos procedimentos do egg postgresql para a execução de consultas:
#;1> (define query "select * from news")
#;2> (vector-ref (car (pg:query-tuples query (pg-repl:conn))) 3)
#(2008 7 30 19 37 54 271625)
O código do programa (
db-repl.scm
) está a seguir:(use utils postgresql readline stty regex (srfi 13))
(define pg-repl:conn (make-parameter #f))
(define (pg-repl:query . query)
(pg:query-tuples
(string-intersperse (map ->string query) "")
(pg-repl:conn)))
(toplevel-command
'-
(lambda ()
(pp (pg-repl:query
(with-output-to-string
(cut print (read-line)))))))
(toplevel-command
'table
(lambda ()
(let* ((table (string-trim-both (read-line)))
(cols
(pg-repl:query
"select column_name,data_type,"
"character_maximum_length,is_nullable "
"from information_schema.columns "
"where table_name = '" table "'")))
(if (null? cols)
(print "Tabela \"" table "\" nao existe.")
(for-each
(lambda (f)
(let ((colname (vector-ref f 0))
(type (vector-ref f 1))
(size (let ((size (vector-ref f 2)))
(if (pg:sql-null-object? size)
""
(conc "(" size ")"))))
(nullable (vector-ref f 3)))
colname
(make-string
(- 30 (string-length colname)))
type size
(make-string
(- 30 (string-length (conc type size))))
nullable)))
cols)))))
(toplevel-command
'tables
(lambda ()
(for-each
(lambda (item)
(print (vector-ref item 0)))
(pg-repl:query
"select table_name from information_schema.tables "
"where table_type = 'BASE TABLE' "
"and table_schema not in "
"('pg_catalog', 'information_schema')"))))
(define (pg-repl:usage #!optional exit-code)
(print (program-name) " [<user>@<server>/<database>]")
(when exit-code (exit exit-code)))
(let ((args (command-line-arguments))
(user "postgres")
(host "localhost")
(db "template")
(passwd #f))
;; Restaura o terminal em caso de termino via C-c
(set-signal-handler! signal/int
(lambda (_) (stty '(echo))))
(unless (or (null? args) (equal? "\"\"" (car args)))
(let* ((cred (string-trim-both
(car args)
(cut memq <> '(#\space #\newline #\")))) ;"
(@tokens (string-split cred "@"))
(/tokens (if (null? @tokens)
'()
(string-split (cadr @tokens) "/"))))
(if (and (null? @tokens) (null? /tokens))
(pg-repl:usage 1)
(begin
(unless (null? @tokens)
(set! user (car @tokens)))
(unless (null? /tokens)
(set! host (car /tokens))
(set! db (cadr /tokens)))))))
(display "Senha: ")
(pg-repl:conn
(pg:connect
`((user . ,user)
(dbname . ,db)
(host . ,host)
(password . ,(with-stty '(not echo) read-line)))))
(current-input-port (make-gnu-readline-port))
(gnu-history-install-file-manager
(string-append (or (getenv "HOME") ".")
"/.csi.history"))
(newline)
(repl))
2 comentários:
o que é um repl? :P
Read-Eval-Print-Loop.:-)
http://en.wikipedia.org/wiki/REPL
Postar um comentário