79119fd9639192d15c498a00b4b6fe739256ab93
[umlisp.git] / parse-2002.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: umlisp -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          parse-2002.lisp
6 ;;;; Purpose:       Parsing and SQL insertion routines for UMLisp which may
7 ;;;;                change from year to year
8 ;;;; Programmer:    Kevin M. Rosenberg
9 ;;;; Date Started:  Apr 2000
10 ;;;;
11 ;;;; $Id: parse-2002.lisp,v 1.6 2003/05/06 06:09:29 kevin Exp $
12 ;;;;
13 ;;;; This file, part of UMLisp, is
14 ;;;;    Copyright (c) 2000-2002 by Kevin M. Rosenberg, M.D.
15 ;;;;
16 ;;;; UMLisp users are granted the rights to distribute and use this software
17 ;;;; as governed by the terms of the GNU General Public License.
18 ;;;; *************************************************************************
19
20 (in-package :umlisp)
21 (declaim (optimize (speed 3) (safety 1) (compilation-speed 0) (debug 3)))
22
23 ;;; Pre-read data for custom fields into hash tables
24 (defvar *parse-hash-init?* nil)
25
26 (eval-when (:compile-toplevel :load-toplevel :execute)
27 (let ((pfstr-hash nil)      ;;; Preferred concept strings by CUI
28       (cui-lrl-hash nil)    ;;; LRL by CUI
29       (lui-lrl-hash nil)    ;;; LRL by LUI
30       (cuisui-lrl-hash nil) ;;; LRL by CUISUI
31       (sab-srl-hash nil))   ;;; SRL by SAB
32   
33   (defun make-parse-hash-table ()
34     (if pfstr-hash
35         (progn
36           (clrhash pfstr-hash)
37           (clrhash cui-lrl-hash)
38           (clrhash lui-lrl-hash)
39           (clrhash cuisui-lrl-hash)
40           (clrhash sab-srl-hash))
41       (setf
42           pfstr-hash (make-hash-table :size 800000)
43           cui-lrl-hash (make-hash-table :size 800000)
44           lui-lrl-hash (make-hash-table :size 1500000)
45           cuisui-lrl-hash (make-hash-table :size 1800000)
46           sab-srl-hash (make-hash-table :size 100 :test 'equal))))
47     
48   (defun binit-hash-table (&optional (force-read nil))
49     (when (or force-read (not *parse-hash-init?*))
50       (make-parse-hash-table)
51       (setq *parse-hash-init?* t))
52     (with-buffered-umls-file (line "MRCON")
53       (let ((cui (parse-ui (aref line 0)))
54             (lui (parse-ui (aref line 3)))
55             (sui (parse-ui (aref line 5)))
56             (lrl (parse-integer (aref line 7))))
57         (unless (gethash cui pfstr-hash)  ;; if haven't stored pfstr for cui
58           (if (and (string-equal (aref line 1) "ENG") ; LAT
59                    (string-equal (aref line 2) "P") ; ts
60                    (string-equal (aref line 4) "PF")) ; stt
61               (setf (gethash cui pfstr-hash) (aref line 6))))
62         (set-lrl-hash cui lrl cui-lrl-hash)
63         (set-lrl-hash lui lrl lui-lrl-hash)
64         (set-lrl-hash (make-cuisui cui sui) lrl cuisui-lrl-hash)))
65     (with-buffered-umls-file (line "MRSO")
66       (let ((sab (aref line 3)))
67         (unless (gethash sab sab-srl-hash)  ;; if haven't stored
68           (setf (gethash sab sab-srl-hash) (aref line 6))))))
69   
70   (defun init-hash-table (&optional (force-read nil))
71     (when (or force-read (not *parse-hash-init?*))
72       (make-parse-hash-table)
73       (setq *parse-hash-init?* t))
74     (with-umls-file (line "MRCON")
75       (let ((cui (parse-ui (nth 0 line)))
76             (lui (parse-ui (nth 3 line)))
77             (sui (parse-ui (nth 5 line)))
78             (lrl (parse-integer (nth 7 line))))
79         (unless (gethash cui pfstr-hash)  ;; if haven't stored pfstr for cui
80           (if (and (string-equal (nth 1 line) "ENG") ; LAT
81                    (string-equal (nth 2 line) "P") ; ts
82                    (string-equal (nth 4 line) "PF")) ; stt
83               (setf (gethash cui pfstr-hash) (nth 6 line))))
84         (set-lrl-hash cui lrl cui-lrl-hash)
85         (set-lrl-hash lui lrl lui-lrl-hash)
86         (set-lrl-hash (make-cuisui cui sui) lrl cuisui-lrl-hash)))
87     (with-umls-file (line "MRSO")
88       (let ((sab (nth 3 line)))
89         (multiple-value-bind (val found) (gethash sab sab-srl-hash)
90           (declare (ignore val))
91           (unless found
92             (setf (gethash sab sab-srl-hash) (parse-integer (nth 6 line))))))))
93   
94   (defun pfstr-hash (cui)
95     (gethash cui pfstr-hash))
96   
97   (defun cui-lrl (cui)
98     (gethash cui cui-lrl-hash))
99   
100   (defun lui-lrl (lui)
101     (gethash lui lui-lrl-hash))
102   
103   (defun cuisui-lrl (cuisui)
104     (gethash cuisui cuisui-lrl-hash))
105   
106   (defun sab-srl (sab)
107     (kmrcl:aif (gethash sab sab-srl-hash) kmrcl::it 0))
108 )) ;; closure
109
110 (defun set-lrl-hash (key lrl hash)
111   "Set the least restrictive level in hash table"
112   (multiple-value-bind (hash-lrl found) (gethash key hash)
113     (if (or (not found) (< lrl hash-lrl))
114         (setf (gethash key hash) lrl))))
115
116 ;; UMLS file and column structures
117 ;;; SQL datatypes symbols
118 ;;; sql-u - Unique identifier
119 ;;; sql-s - Small integer (16-bit)
120 ;;; sql-i - Integer (32-bit)
121 ;;; sql-l - Big integer (64-bit)
122 ;;; sql-f - Floating point
123
124 (defparameter +col-datatypes+
125     '(("AV" sql-f) ("BTS" sql-i) ("CLS" sql-i) ("COF" sql-i) ("CUI1" sql-u)
126       ("CUI2" sql-u) ("CUI" sql-u) ("CXN" sql-s) ("FR" sql-i) ("LRL" sql-s)
127       ("LUI" sql-u) ("MAX" sql-s) ("MIN" sql-s) ("RANK" sql-s) ("REF" sql-s)
128       ("RNK" sql-s) ("RWS" sql-i) ("SRL" sql-s) ("SUI" sql-u) ("TUI" sql-u)
129       ;;; Custom columns
130       ("KCUISUI" sql-l) ("KCUILUI" sql-l) ("KCUILRL" sql-i) ("KLUILRL" sql-i)
131       ("KSRL" sql-i) ("KLRL" sql-i)
132       ;;; LEX columns
133       ("EUI" sql-u) ("EUI2" sql-u)
134       ;;; Semantic net columns
135       ("UI" sql-u) ("UI2" sql-u) ("UI3" sql-u)
136       ;; New fields for 2002AD
137       ("RCUI" sql-u) ("VCUI" sql-u) ("CFR" sql-i) ("TFR" sql-i)
138       ) 
139     "SQL data types for each non-string column")
140
141 (defparameter +custom-tables+
142     nil
143   #+ignore
144   '(("MRCONSO" "SELECT m.CUI, m.LAT, m.TS, m.LUI, m.STT, m.SUI, m.STR, m.LRL, s.SAB, s.TTY, s.SCD, s.SRL FROM MRCON m, MRSO s WHERE m.CUI=s.CUI AND m.LUI=s.LUI AND m.SUI=s.SUI")
145     ("MRCONFULL" "SELECT m.CUI, m.LAT, m.TS, m.LUI, m.STT, m.SUI, m.STR, m.LRL, s.SAB, s.TTY, s.SCD, s.SRL, t.TUI FROM MRCON m, MRSO s, MRSTY t WHERE m.CUI=s.CUI AND m.LUI=s.LUI AND m.SUI=s.SUI AND m.CUI=t.CUI AND s.CUI=t.CUI"))
146   "Custom tables to create")
147
148 (defparameter +custom-cols+
149     '(("MRCON" "KPFSTR" "TEXT" 1024
150                (lambda (x) (pfstr-hash (parse-ui (nth 0 x)))))
151       ("MRCON" "KCUISUI" "BIGINT" 0
152        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 5 x))))))
153       ("MRCON" "KCUILUI" "BIGINT" 0
154        (lambda (x) (format nil "~d" (make-cuilui (parse-ui (nth 0 x)) (parse-ui (nth 3 x))))))
155       ("MRCON" "KCUILRL" "INTEGER" 0
156        (lambda (x) (format nil "~d" (cui-lrl (parse-ui (nth 0 x))))))
157       ("MRCON" "KLUILRL" "INTEGER" 0
158        (lambda (x) (format nil "~d" (lui-lrl (parse-ui (nth 3 x))))))
159       ("MRLO" "KLRL" "INTEGER" 0
160        (lambda (x) (format nil "~d" 
161                     (if (zerop (length (nth 4 x)))
162                         (cui-lrl (parse-ui (nth 0 x)))
163                       (cuisui-lrl (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 4 x))))))))
164       ("MRSTY" "KLRL" "INTEGER" 0
165        (lambda (x) (format nil "~d" (cui-lrl (parse-ui (nth 0 x))))))
166       ("MRCOC" "KLRL" "INTEGER" 0
167        (lambda (x) (format nil "~d" 
168                     (max (cui-lrl (parse-ui (nth 0 x)))
169                          (kmrcl:aif (cui-lrl (parse-ui (nth 1 x))) kmrcl::it 0)))))
170       ("MRSAT" "KSRL" "INTEGER" 0
171        (lambda (x) (format nil "~d" (sab-srl (nth 5 x)))))
172       ("MRREL" "KSRL" "INTEGER" 0
173        (lambda (x) (format nil "~d" (sab-srl (nth 4 x)))))
174       ("MRRANK" "KSRL" "INTEGER" 0
175        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
176       ("MRDEF" "KSRL" "INTEGER" 0
177        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
178       ("MRCXT" "KSRL" "INTEGER" 0
179        (lambda (x) (format nil "~d" (sab-srl (nth 2 x)))))
180       ("MRATX" "KSRL" "INTEGER" 0
181        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
182       ("MRXW.ENG" "KLRL" "INTEGER" 0
183        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
184                                                  (parse-ui (nth 2 x))
185                                                  (parse-ui (nth 4 x)))))))
186       ("MRXW.NONENG" "KLRL" "INTEGER" 0
187        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
188                                                  (parse-ui (nth 2 x))
189                                                  (parse-ui (nth 4 x)))))))
190       ("MRXNW.ENG" "KLRL" "INTEGER" 0
191        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
192                                                  (parse-ui (nth 2 x))
193                                                  (parse-ui (nth 4 x)))))))
194       ("MRXNS.ENG" "KLRL" "INTEGER" 0
195        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
196                                                  (parse-ui (nth 2 x))
197                                                  (parse-ui (nth 4 x)))))))
198       ("MRREL" "KPFSTR2" "TEXT" 1024
199        (lambda (x) (pfstr-hash (parse-ui (nth 2 x)))))
200       ("MRCOC" "KPFSTR2" "TEXT" 1024
201        (lambda (x) (pfstr-hash (parse-ui (nth 1 x)))))
202       ("MRCXT" "KCUISUI" "BIGINT" 0 
203        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 1 x))))))
204       ("MRSAT" "KCUILUI" "BIGINT" 0
205        (lambda (x) (format nil "~d" (make-cuilui (parse-ui (nth 0 x)) (parse-ui (nth 1 x))))))
206       ("MRSAT" "KCUISUI" "BIGINT" 0
207        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 2 x))))))
208       ("MRSO" "KCUISUI" "BIGINT" 0
209        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 2 x))))))
210       ("MRXW.ENG" "KCUISUI" "BIGINT" 0
211        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
212       ("MRXNW.ENG" "KCUISUI" "BIGINT" 0
213        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
214       ("MRXNS.ENG" "KCUISUI" "BIGINT" 0
215        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
216       ("MRXW.NONENG" "LAT" "CHAR" 3 (lambda (x) (nth 0 x)))
217       ("MRXW.NONENG" "WD"  "CHAR" 200  (lambda (x) (nth 1 x)))
218       ("MRXW.NONENG" "CUI" "INTEGER" 0 (lambda (x) (parse-ui (nth 2 x))))
219       ("MRXW.NONENG" "LUI" "INTEGER" 0 (lambda (x) (parse-ui (nth 3 x))))
220       ("MRXW.NONENG" "SUI" "INTEGER" 0 (lambda (x) (parse-ui (nth 4 x))))
221       ("MRXW.NONENG" "KCUISUI" "BIGINT" 0 
222        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x)))))))
223   "Custom columns to create.(filename, col, sqltype, value-func).")
224
225 (defparameter +index-cols+
226     '(("CUI" "MRATX") ("CUI1" "MRCOC") ("CUI" "MRCON") ("LUI" "MRCON") 
227       ("LRL" "MRCON")
228       ("SUI" "MRCON") ("CUI" "MRCXT") ("CUI" "MRDEF") ("CUI" "MRLO")
229       ("CUI1" "MRREL") ("CUI" "MRSAT") ("LUI" "MRSAT") ("SUI" "MRSAT")
230       ("CUI" "MRSO") ("SAB" "MRSO") ("SRL" "MRSO") ("CUI" "MRSTY")
231       ("TUI" "MRSTY") ("CUI" "MRXNS_ENG") ("NSTR" "MRXNS_ENG" 10)
232       ("CUI" "MRXNW_ENG") ("NWD" "MRXNW_ENG") ("WD" "MRXW_ENG")
233       ("KCUISUI" "MRCON") ("KCUILUI" "MRCON") ("KCUILRL" "MRCON")
234       ("KLUILRL" "MRCON") ("KCUISUI" "MRCXT") 
235       ("KCUISUI" "MRSO") ("KCUISUI" "MRSAT")  ("KCUILUI" "MRSAT")
236       ("KCUISUI" "MRXW_ENG") ("KCUISUI" "MRXNW_ENG") 
237       ("KCUISUI" "MRXNS_ENG") ("KCUISUI" "MRXW_NONENG")
238       ("KSRL" "MRATX") ("KSRL" "MRCXT") ("KSRL" "MRDEF") ("KSRL" "MRRANK") 
239       ("KSRL" "MRREL") ("KSRL" "MRSAT") ("KLRL" "MRCOC") 
240       ("KLRL" "MRLO") ("KLRL" "MRSTY") ("KLRL" "MRXW_ENG") ("KLRL" "MRXNW_ENG")
241       ("KLRL" "MRXNS_ENG") ("KLRL" "MRXW_NONENG")
242       ;; LEX indices
243       ("EUI" "LRABR") ("EUI2" "LRABR") ("EUI" "LRAGR") ("EUI" "LRCMP") ("EUI" "LRMOD")
244       ("EUI" "LRNOM") ("EUI2" "LRNOM") ("EUI" "LRPRN") ("EUI" "LRPRP") ("EUI" "LRSPL")
245       ("EUI" "LRTRM") ("EUI" "LRTYP") ("EUI" "LRWD") ("WRD" "LRWD")
246       ("BAS" "LRABR") 
247       ;; Semantic NET indices
248       ("UI" "SRSTRE1") ("UI2" "SRSTRE1") ("UI3" "SRSTRE1") 
249       ("STY_RL" "SRDEF") ("RT" "SRDEF") ("STY_RL" "SRSTR") ("STY_RL2" "SRSTR")
250       ("RL" "SRSTR")
251       ("SRL" "MRSAB") ("RSAB" "MRSAB") ("VSAB" "MRSAB") ("RCUI" "MRSAB")
252       ("VCUI" "MRSAB") ("LAT" "MRSAB"))
253   "Columns in files to index")
254
255 (defparameter +custom-index-cols+
256   nil
257   #+ignore
258   '(("CUI" "MRCONFULL") ("SAB" "MRCONFULL") ("TUI" "MRCONFULL"))
259   "Indexes to custom tables")
260
261 ;; File & Column functions
262
263 (defun init-umls (&optional (alwaysclear nil))
264 "Initialize all UMLS file and column structures if not already initialized"
265   (when (or alwaysclear (null *umls-files*))
266     (init-umls-cols)
267     (init-umls-files)
268     (init-field-lengths)))
269
270 (defun init-umls-cols ()
271   (setq *umls-cols* (append 
272                      (init-meta-cols)
273                      (init-custom-cols)
274                      (init-generic-cols "LRFLD")
275                      (init-generic-cols "SRFLD"))))
276
277 (defun init-meta-cols ()
278 "Initialize all umls columns"  
279   (let ((cols '()))
280     (with-umls-file (line "MRCOLS")
281       (destructuring-bind (col des ref min av max fil dty) line
282         (let ((c (make-umls-col       
283                   :col col
284                   :des des
285                   :ref ref
286                   :min (parse-integer min)
287                   :av (read-from-string av)
288                   :max (parse-integer max)
289                   :fil fil
290                   :dty dty  ;; new in 2002 UMLS
291                   :sqltype "VARCHAR"    ; default data type
292                   :parsefunc #'add-sql-quotes
293                   :custom-value-func nil
294                   :quotechar "'")))
295           (add-datatype-to-col c (datatype-for-col col))
296           (push c cols))))
297     (nreverse cols)))
298
299 (defun init-custom-cols ()
300 "Initialize umls columns for custom columns"  
301   (let ((cols '()))
302     (dolist (customcol +custom-cols+)
303       (let ((c (make-umls-col :col (nth 1 customcol)
304                               :des ""
305                               :ref 0
306                               :min 0
307                               :max (nth 3 customcol)
308                               :av 0
309                               :dty nil
310                               :fil (nth 0 customcol)
311                               :sqltype (nth 2 customcol)
312                               :parsefunc #'add-sql-quotes
313                               :custom-value-func (nth 4 customcol)
314                               :quotechar "'")))
315         (add-datatype-to-col c (datatype-for-col (nth 1 customcol)))
316         (push c cols)))
317     (nreverse cols)))
318
319 (defun escape-column-name (name)
320   (substitute #\_ #\/ name))
321
322 (defun init-generic-cols (col-filename)
323 "Initialize for generic (LEX/NET) columns"  
324   (let ((cols '()))
325     (with-umls-file (line col-filename)
326       (destructuring-bind (nam des ref fil) line
327         (setq nam (escape-column-name nam))
328         (dolist (file (delimited-string-to-list fil #\,))
329           (let ((c (make-umls-col             
330                   :col nam
331                   :des des
332                   :ref ref
333                   :min nil
334                   :av nil
335                   :max nil
336                   :fil file
337                   :dty nil
338                   :sqltype "VARCHAR"    ; default data type
339                   :parsefunc #'add-sql-quotes
340                   :custom-value-func nil
341                   :quotechar "'")))
342             (add-datatype-to-col c (datatype-for-col nam))
343             (push c cols)))))
344     (nreverse cols)))
345
346 (defun init-umls-files ()
347   (setq *umls-files* (append
348                       (init-generic-files "MRFILES") 
349                       (init-generic-files "LRFIL") 
350                       (init-generic-files "SRFIL")))
351   ;; need to separate this since init-custom-files depends on *umls-files*
352   (setq *umls-files* (append *umls-files* (init-custom-files))))
353
354
355 (defun umls-field-string-to-list (fmt)
356   "Converts a comma delimited list of fields into a list of field names. Will
357 append a unique number (starting at 2) onto a column name that is repeated in the list"
358   (let ((field-list (delimited-string-to-list (escape-column-name fmt) #\,))
359         (col-count (make-hash-table :test 'equal)))
360     (dotimes (i (length field-list))
361       (declare (fixnum i))
362       (let ((col (nth i field-list)))
363         (multiple-value-bind (key found) (gethash col col-count)
364           (if found
365               (let ((next-id (1+ key)))
366                 (setf (nth i field-list) (concatenate 'string 
367                                                     col
368                                                     (format nil "~D" next-id)))
369                 (setf (gethash col col-count) next-id))
370             (setf (gethash col col-count) 1)))))
371     field-list))
372
373 (defun init-generic-files (files-filename)
374 "Initialize all LEX file structures"  
375   (let ((files '()))
376   (with-umls-file (line files-filename)
377     (destructuring-bind (fil des fmt cls rws bts) line
378       (let ((f (make-umls-file 
379                 :fil fil
380                 :table (substitute #\_ #\. fil)
381                 :des des
382                 :fmt (escape-column-name fmt)
383                 :cls (parse-integer cls)
384                 :rws (parse-integer rws)
385                 :bts (parse-integer bts)
386                 :fields (concatenate 'list
387                           (umls-field-string-to-list fmt)
388                           (custom-colnames-for-filename fil)))))
389         (setf (umls-file-colstructs f) (umls-cols-for-umls-file f))
390         (push f files))))
391   (nreverse files)))
392
393 (defun init-custom-files ()
394   (let ((ffile (make-umls-file :fil "MRXW.NONENG"
395                                :des "Custom NonEnglish Index"
396                                :table "MRXW_NONENG"
397                                :cls 5
398                                :rws 0
399                                :bts 0
400                                :fields (umls-file-fields (find-umls-file "MRXW.ENG")))))
401     (setf (umls-file-colstructs ffile)
402       (umls-cols-for-umls-file ffile))
403     (list ffile)))
404
405 (defun datatype-for-col (colname)
406 "Return datatype for column name"  
407   (car (cdr (find colname +col-datatypes+ :key #'car :test #'string-equal))))
408
409 (defun add-datatype-to-col (col datatype)
410 "Add data type information to column"
411   (setf (umls-col-datatype col) datatype)
412   (case datatype
413     (sql-u (setf (umls-col-sqltype col) "INTEGER"
414                  (umls-col-parsefunc col) #'parse-ui
415                  (umls-col-quotechar col) ""))
416     (sql-s (setf (umls-col-sqltype col) "SMALLINT" 
417                  (umls-col-parsefunc col) #'parse-integer
418                  (umls-col-quotechar col) ""))
419     (sql-l (setf (umls-col-sqltype col) "BIGINT" 
420                  (umls-col-parsefunc col) #'parse-integer
421                  (umls-col-quotechar col) ""))
422     (sql-i (setf (umls-col-sqltype col) "INTEGER" 
423                  (umls-col-parsefunc col) #'parse-integer
424                  (umls-col-quotechar col) ""))
425     (sql-f (setf (umls-col-sqltype col) "NUMERIC" 
426                  (umls-col-parsefunc col) #'read-from-string
427                  (umls-col-quotechar col) ""))
428     (t                       ; Default column type, optimized text storage
429      (setf (umls-col-parsefunc col) #'add-sql-quotes 
430            (umls-col-quotechar col) "'")
431      (when (and (umls-col-max col) (umls-col-av col))
432        (if (> (umls-col-max col) 255)
433            (setf (umls-col-sqltype col) "TEXT")
434          (if (< (- (umls-col-max col) (umls-col-av col)) 4) 
435              (setf (umls-col-sqltype col) "CHAR") ; if average bytes wasted < 4
436            (setf (umls-col-sqltype col) "VARCHAR")))))))
437
438
439