diff --git a/src/package.lisp b/src/package.lisp index 1b1fe4a..990b24c 100644 --- a/src/package.lisp +++ b/src/package.lisp @@ -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) diff --git a/src/parsers/command-csv.lisp b/src/parsers/command-csv.lisp index fe27474..5fad5b1 100644 --- a/src/parsers/command-csv.lisp +++ b/src/parsers/command-csv.lisp @@ -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 #\\ #\"))) diff --git a/src/sources/common/project-fields.lisp b/src/sources/common/project-fields.lisp index 7069c11..7567425 100644 --- a/src/sources/common/project-fields.lisp +++ b/src/sources/common/project-fields.lisp @@ -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" diff --git a/src/utils/transforms.lisp b/src/utils/transforms.lisp index fd06cdc..27b6edc 100644 --- a/src/utils/transforms.lisp +++ b/src/utils/transforms.lisp @@ -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 diff --git a/test/Makefile b/test/Makefile index a6cc71a..4c613b8 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 \ diff --git a/test/csv-temp.load b/test/csv-temp.load new file mode 100644 index 0000000..d1b8254 --- /dev/null +++ b/test/csv-temp.load @@ -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 \ No newline at end of file diff --git a/test/regress/expected/csv-temp.out b/test/regress/expected/csv-temp.out new file mode 100644 index 0000000..0311b70 --- /dev/null +++ b/test/regress/expected/csv-temp.out @@ -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