mirror of
https://github.com/dimitri/pgloader.git
synced 2025-08-08 07:16:58 +02:00
In order to share more code in between the different source types, finally have a go at the quite horrible mess of anonymous data structures floating around. Having a catalog and schema instances not only allows for code cleanup, but will also allow to implement some bug fixes and wishlist items such as mapping tables from a schema to another one. Also, supporting database sources having a notion of "schema" (in between "catalog" and "table") should get easier, including getting on-par with MySQL in the MS SQL support (materialized views has been asked for already). See #320, #316, #224 for references and a notion of progress being made. In passing, also clean up the copy-databases methods for database source types, so that they all use a fetch-metadata generic function and a prepare-pgsql-database and a complete-pgsql-database generic function. Actually, a single method does the job here. The responsibility of introspecting the source to populate the internal catalog/schema representation is now held by the fetch-metadata generic function, which in turn will call the specialized versions of list-all-columns and friends implementations. Once the catalog has been fetched, an explicit CAST call is then needed before we can continue. Finally, the fields/columns/transforms slots in the copy objects are still being used by the operative code, so the internal catalog representation is only used up to starting the data copy step, where the copy class instances are then all that's used. This might be refactored again in a follow-up patch.
121 lines
4.8 KiB
Common Lisp
121 lines
4.8 KiB
Common Lisp
;;;
|
|
;;; Tools to handle MS SQL data type casting rules
|
|
;;;
|
|
|
|
(in-package :pgloader.mssql)
|
|
|
|
(defparameter *mssql-default-cast-rules*
|
|
`((:source (:type "char") :target (:type "text" :drop-typemod t))
|
|
(:source (:type "nchar") :target (:type "text" :drop-typemod t))
|
|
(:source (:type "varchar") :target (:type "text" :drop-typemod t))
|
|
(:source (:type "nvarchar") :target (:type "text" :drop-typemod t))
|
|
(:source (:type "xml") :target (:type "text" :drop-typemod t))
|
|
|
|
(:source (:type "bit") :target (:type "boolean")
|
|
:using pgloader.transforms::sql-server-bit-to-boolean)
|
|
|
|
(:source (:type "uniqueidentifier") :target (:type "uuid")
|
|
:using pgloader.transforms::sql-server-uniqueidentifier-to-uuid)
|
|
|
|
(:source (:type "hierarchyid") :target (:type "bytea")
|
|
:using pgloader.transforms::byte-vector-to-bytea)
|
|
|
|
(:source (:type "geography") :target (:type "bytea")
|
|
:using pgloader.transforms::byte-vector-to-bytea)
|
|
|
|
(:source (:type "tinyint") :target (:type "smallint"))
|
|
|
|
(:source (:type "float") :target (:type "float")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "real") :target (:type "real")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "double") :target (:type "double precision")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "numeric") :target (:type "numeric")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "decimal") :target (:type "numeric")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "money") :target (:type "numeric")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "smallmoney") :target (:type "numeric")
|
|
:using pgloader.transforms::float-to-string)
|
|
|
|
(:source (:type "binary") :target (:type "bytea")
|
|
:using pgloader.transforms::byte-vector-to-bytea)
|
|
|
|
(:source (:type "varbinary") :target (:type "bytea")
|
|
:using pgloader.transforms::byte-vector-to-bytea)
|
|
|
|
(:source (:type "datetime") :target (:type "timestamptz"))
|
|
(:source (:type "datetime2") :target (:type "timestamptz")))
|
|
"Data Type Casting to migrate from MSSQL to PostgreSQL")
|
|
|
|
;;;
|
|
;;; Specific implementation of schema migration, see the API in
|
|
;;; src/pgsql/schema.lisp
|
|
;;;
|
|
(defstruct (mssql-column
|
|
(:constructor make-mssql-column
|
|
(schema table-name name type
|
|
default nullable identity
|
|
character-maximum-length
|
|
numeric-precision
|
|
numeric-precision-radix
|
|
numeric-scale
|
|
datetime-precision
|
|
character-set-name
|
|
collation-name)))
|
|
schema table-name name type default nullable identity
|
|
character-maximum-length
|
|
numeric-precision numeric-precision-radix numeric-scale
|
|
datetime-precision
|
|
character-set-name collation-name)
|
|
|
|
(defmethod mssql-column-ctype ((col mssql-column))
|
|
"Build the ctype definition from the full mssql-column information."
|
|
(let ((type (mssql-column-type col)))
|
|
(cond ((and (string= type "int")
|
|
(mssql-column-identity col))
|
|
"bigserial")
|
|
|
|
((member type '("float" "real") :test #'string=)
|
|
;; see https://msdn.microsoft.com/en-us/library/ms173773.aspx
|
|
;; scale is supposed to be nil, and useless in PostgreSQL, so we
|
|
;; just ignore it
|
|
(format nil "~a(~a)" type (mssql-column-numeric-precision col)))
|
|
|
|
((member type '("decimal" "numeric" ) :test #'string=)
|
|
;; https://msdn.microsoft.com/en-us/library/ms187746.aspx
|
|
(cond ((null (mssql-column-numeric-precision col))
|
|
type)
|
|
(t
|
|
(format nil "~a(~a,~a)"
|
|
type
|
|
(mssql-column-numeric-precision col)
|
|
(or (mssql-column-numeric-scale col) 0)))))
|
|
|
|
(t type))))
|
|
|
|
(defmethod cast ((field mssql-column))
|
|
"Return the PostgreSQL type definition from given MS SQL column definition."
|
|
(with-slots (schema table-name name type default nullable)
|
|
field
|
|
(declare (ignore schema)) ; FIXME
|
|
(let* ((ctype (mssql-column-ctype field))
|
|
(pgcol
|
|
(apply-casting-rules table-name name type ctype default nullable nil)))
|
|
;; the MS SQL driver smartly maps data to the proper CL type, but the
|
|
;; pgloader API only wants to see text representations to send down the
|
|
;; COPY protocol.
|
|
(unless (column-transform pgcol)
|
|
(setf (column-transform pgcol)
|
|
(lambda (val) (if val (format nil "~a" val) :null))))
|
|
pgcol)))
|
|
|