mirror of
https://github.com/dimitri/pgloader.git
synced 2025-08-07 06:47:00 +02:00
Improve user code parsing, fix #297.
To be able to use "t" (or "nil") as a column name, pgloader needs to be able to generate lisp code where those symbols are available. It's simple enough in that a Common Lisp package that doesn't :use :cl fullfills the condition, so intern user symbols in a specially crafted package that doesn't :use :cl. Now, we still need to be able to run transformation code that is using the :cl package symbols and the pgloader.transforms functions too. In this commit we introduce a heuristic to pick symbols either as functions from pgloader.transforms or anything else in pgloader.user-symbols. And so that user code may use NIL too, we provide an override mechanism to the intern-symbol heuristic and use it only when parsing user code, not when producing Common Lisp code from the parsed load command.
This commit is contained in:
parent
fe812061c4
commit
598c860cf5
@ -155,7 +155,7 @@
|
||||
(:use #:cl
|
||||
#:pgloader.params #:pgloader.utils #:pgloader.connection
|
||||
#:pgloader.schema)
|
||||
(:import-from #:pgloader.transforms #:precision #:scale)
|
||||
(:import-from #:pgloader.transforms #:precision #:scale #:intern-symbol)
|
||||
(:import-from #:pgloader.parse-date
|
||||
#:parse-date-string
|
||||
#:parse-date-format)
|
||||
|
@ -266,7 +266,7 @@
|
||||
(defrule sexp-symbol (and (symbol-first-character-p character)
|
||||
(* (symbol-character-p character)))
|
||||
(:lambda (schars)
|
||||
(pgloader.transforms:intern-symbol (text schars))))
|
||||
(pgloader.transforms:intern-symbol (text schars) '(("nil" . cl:nil)))))
|
||||
|
||||
(defrule sexp-string-char (or (not-doublequote character) (and #\\ #\")))
|
||||
|
||||
|
@ -26,8 +26,8 @@
|
||||
(field-name-as-symbol (field-name-or-list)
|
||||
"we need to deal with symbols as we generate code"
|
||||
(typecase field-name-or-list
|
||||
(list (pgloader.transforms:intern-symbol (car field-name-or-list)))
|
||||
(t (pgloader.transforms:intern-symbol field-name-or-list))))
|
||||
(list (intern-symbol (car field-name-or-list)))
|
||||
(t (intern-symbol field-name-or-list))))
|
||||
|
||||
(process-field (field-name-or-list)
|
||||
"Given a field entry, return a function dealing with nulls for it"
|
||||
|
@ -7,6 +7,44 @@
|
||||
|
||||
(in-package :pgloader.transforms)
|
||||
|
||||
;;;
|
||||
;;; This package is used to generate symbols in the CL code that
|
||||
;;; project-fields produces, allowing to use symbols such as t. It's
|
||||
;;; important that the user-symbols package doesn't :use cl.
|
||||
;;;
|
||||
(defpackage #:pgloader.user-symbols (:use))
|
||||
|
||||
(defun intern-symbol (symbol-name &optional (overrides '()))
|
||||
"Return a symbol in either PGLOADER.TRANSFORMS if it exists there
|
||||
already (it's a user provided function) or a PGLOADER.USER-SYMBOLS
|
||||
package.
|
||||
|
||||
OVERRIDES is an alist of symbol . value, allowing called to force certain
|
||||
values: the classic example is how to parse the \"nil\" symbol-name.
|
||||
Given OVERRIDES as '((nil . nil)) the returned symbol will be cl:nil
|
||||
rather than pgloader.user-symbols::nil."
|
||||
(let ((overriden (assoc symbol-name overrides :test #'string-equal)))
|
||||
(if overriden
|
||||
(cdr overriden)
|
||||
|
||||
(multiple-value-bind (symbol status)
|
||||
(find-symbol (string-upcase symbol-name)
|
||||
(find-package "PGLOADER.TRANSFORMS"))
|
||||
;; pgloader.transforms package (:use :cl) so we might find variable
|
||||
;; names in there that we want to actually intern in
|
||||
;; pgloader.user-symbols so that users may use e.g. t as a column name...
|
||||
;; so only use transform symbol when it denotes a function
|
||||
(cond
|
||||
((and status (fboundp symbol)) symbol) ; a transform function
|
||||
|
||||
(t
|
||||
(intern (string-upcase symbol-name)
|
||||
(find-package "PGLOADER.USER-SYMBOLS"))))))))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Some optimisation stanza
|
||||
;;;
|
||||
(declaim (inline intern-symbol
|
||||
zero-dates-to-null
|
||||
date-with-no-separator
|
||||
@ -25,14 +63,6 @@
|
||||
sql-server-uniqueidentifier-to-uuid
|
||||
sql-server-bit-to-boolean))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Some tools for reading expressions in the parser, and evaluating them.
|
||||
;;;
|
||||
(defun intern-symbol (symbol-name)
|
||||
(intern (string-upcase symbol-name)
|
||||
(find-package "PGLOADER.TRANSFORMS")))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Transformation functions
|
||||
|
@ -16,6 +16,7 @@ REGRESS= allcols.load \
|
||||
csv-keep-extra-blanks.load \
|
||||
csv-non-printable.load \
|
||||
csv-nulls.load \
|
||||
csv-temp.load \
|
||||
csv-trim-extra-blanks.load \
|
||||
csv.load \
|
||||
copy.load \
|
||||
|
29
test/csv-temp.load
Normal file
29
test/csv-temp.load
Normal file
@ -0,0 +1,29 @@
|
||||
--
|
||||
-- See https://github.com/dimitri/pgloader/issues/297
|
||||
--
|
||||
-- The "t" field would be "temperature", for instance.
|
||||
--
|
||||
|
||||
LOAD CSV
|
||||
FROM inline (a, b, nil, t)
|
||||
INTO postgresql:///pgloader?temp(a,b,nil,t)
|
||||
|
||||
WITH fields terminated by ';'
|
||||
|
||||
BEFORE LOAD DO
|
||||
$$ drop table if exists temp; $$,
|
||||
$$ CREATE TABLE temp
|
||||
(
|
||||
a integer,
|
||||
b timestamp without time zone,
|
||||
nil real,
|
||||
t real
|
||||
);
|
||||
$$;
|
||||
|
||||
|
||||
100;2015-01-01 00:00:00;-6;10
|
||||
101;2015-01-02 00:00:00;-2.1;12.5
|
||||
102;2015-01-03 00:00:00;3.4;5.5
|
||||
103;2015-01-04 00:00:00;4.7;-2.3
|
||||
104;2015-01-05 00:00:00;0.4;0
|
5
test/regress/expected/csv-temp.out
Normal file
5
test/regress/expected/csv-temp.out
Normal file
@ -0,0 +1,5 @@
|
||||
100 2015-01-01 00:00:00 -6 10
|
||||
101 2015-01-02 00:00:00 -2.1 12.5
|
||||
102 2015-01-03 00:00:00 3.4 5.5
|
||||
103 2015-01-04 00:00:00 4.7 -2.3
|
||||
104 2015-01-05 00:00:00 0.4 0
|
Loading…
Reference in New Issue
Block a user