From ed8022ce643e1fa07a8d0a9d124df792f3990793 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Wed, 30 Jul 2014 16:32:35 +0200 Subject: [PATCH] SQLite: transform default values to their PostgreSQL representation. When default values are used in SQLite they are of course using their SQLite representation, which might not be compatible with the PostgreSQL target data type we're casting to. Make it so that the default values are transformed too, as we already do in the MySQL case. See #100. --- src/sources/sqlite.lisp | 64 +++++++++++++++++++++++++--------------- test/sqlite/sqlite.db | Bin 57344 -> 61440 bytes 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/sources/sqlite.lisp b/src/sources/sqlite.lisp index bc49a41..7e3aef5 100644 --- a/src/sources/sqlite.lisp +++ b/src/sources/sqlite.lisp @@ -7,6 +7,19 @@ (defvar *sqlite-db* nil "The SQLite database connection handler.") +;; +;; The SQLite drive we use maps the CFFI data type mapping functions and +;; gets back proper CL typed objects, where we only want to deal with text. +;; +(defvar *sqlite-to-pgsql* + '(("float" . pgloader.transforms::float-to-string) + ("real" . pgloader.transforms::float-to-string) + ("double precision" . pgloader.transforms::float-to-string) + ("numeric" . pgloader.transforms::float-to-string) + ("text" . nil) + ("bytea" . pgloader.transforms::byte-vector-to-bytea)) + "Transformation functions to use when migrating from SQLite to PostgreSQL.") + ;;; ;;; SQLite tools connecting to a database ;;; @@ -32,6 +45,32 @@ (t sqlite-type-name)))) +(defun transformation-function (pgsql-type-name) + "Return the transformation function to use to switch a SQLite value to a + PostgreSQL value of type PGSQL-TYPE-NAME." + (let* ((type-name + (cond ((and (<= 7 (length pgsql-type-name)) + (string-equal "numeric" pgsql-type-name :end2 7)) + "numeric") + (t pgsql-type-name))) + (transform (assoc type-name *sqlite-to-pgsql* :test #'string=))) + (if transform + (cdr transform) + (compile nil (lambda (c) (when c (format nil "~a" c))))))) + +(defun format-pgsql-default-value (col) + "Return the PostgreSQL representation for the default value of COL." + (declare (type coldef col)) + (let ((default (coldef-default col))) + (cond + ((null default) "NULL") + ((string= "NULL" default) default) + ((string= "CURRENT_TIMESTAMP" default) default) + (t + ;; apply the transformation function to the default value + (let ((fn (transformation-function (cast (coldef-type col))))) + (if fn (funcall fn default) (format nil "'~a'" default))))))) + (defmethod format-pgsql-column ((col coldef) &key identifier-case) "Return a string representing the PostgreSQL column definition." (let* ((column-name @@ -41,7 +80,7 @@ "~a~:[~; not null~]~@[ default ~a~]" (cast (coldef-type col)) (coldef-nullable col) - (coldef-default col)))) + (format-pgsql-default-value col)))) (format nil "~a ~22t ~a" column-name type-definition))) (defun list-tables (&optional (db *sqlite-db*)) @@ -131,28 +170,7 @@ (loop for field in fields collect (let ((coltype (cast (coldef-type field)))) - ;; - ;; The SQLite drive we use maps the CFFI data type - ;; mapping functions and gets back proper CL typed - ;; objects, where we only want to deal with text. - ;; - (cond ((or (string-equal "float" coltype) - (string-equal "real" coltype) - (string-equal "double precision" coltype) - (and (<= 7 (length coltype)) - (string-equal "numeric" coltype :end2 7))) - #'pgloader.transforms::float-to-string) - - ((string-equal "text" coltype) - nil) - - ((string-equal "bytea" coltype) - #'pgloader.transforms::byte-vector-to-bytea) - - (t - (compile nil (lambda (c) - (when c - (format nil "~a" c))))))))))))) + (transformation-function coltype)))))))) ;;; Map a function to each row extracted from SQLite ;;; diff --git a/test/sqlite/sqlite.db b/test/sqlite/sqlite.db index 7a6b7071cdc970a2d15b555995525b45196dde77..997fffcdf39e4b1f536022d32d27f93bfc6ebfd0 100644 GIT binary patch delta 221 zcmZoTz})bFd4jZ{7y|+=Ci2fP08Th|$7F_U%e_{aV=C9|u1pxl%7DWI6