r2960: *** empty log message ***
[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.3 2002/10/09 23:03:41 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 (nth 3 line)))
55             (sui (parse-ui (nth 5 line)))
56             (lrl (parse-integer (nth 7 line))))
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 3 line)))
67         (unless (gethash sab sab-srl-hash)  ;; if haven't stored
68           (setf (gethash sab sab-srl-hash) (aref 6 line))))))
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
118 (defstruct (umls-file)
119   "Record for each UMLS File"
120   fil table des fmt cls rws bts fields colstructs)
121
122 (defstruct (umls-col)
123   "Record for each UMLS Column in each file"
124   col des ref min av max fil sqltype
125   dty ;; new in 2002 umls: suggested SQL datatype
126   parsefunc quotechar datatype custom-value-func)
127
128 ;;; SQL datatypes symbols
129 ;;; sql-u - Unique identifier
130 ;;; sql-s - Small integer (16-bit)
131 ;;; sql-i - Integer (32-bit)
132 ;;; sql-l - Big integer (64-bit)
133 ;;; sql-f - Floating point
134
135 (defconstant +col-datatypes+
136     '(("AV" sql-f) ("BTS" sql-i) ("CLS" sql-i) ("COF" sql-i) ("CUI1" sql-u)
137       ("CUI2" sql-u) ("CUI" sql-u) ("CXN" sql-s) ("FR" sql-i) ("LRL" sql-s)
138       ("LUI" sql-u) ("MAX" sql-s) ("MIN" sql-s) ("RANK" sql-s) ("REF" sql-s)
139       ("RNK" sql-s) ("RWS" sql-i) ("SRL" sql-s) ("SUI" sql-u) ("TUI" sql-u)
140       ;;; Custom columns
141       ("KCUISUI" sql-l) ("KCUILUI" sql-l) ("KCUILRL" sql-i) ("KLUILRL" sql-i)
142       ("KSRL" sql-i) ("KLRL" sql-i)
143       ;;; LEX columns
144       ("EUI" sql-u) ("EUI2" sql-u)
145       ;;; Semantic net columns
146       ("UI" sql-u) ("UI2" sql-u) ("UI3" sql-u)) 
147     "SQL data types for each non-string column")
148
149 (defconstant +custom-tables+
150     nil
151   #+ignore
152   '(("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")
153     ("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"))
154   "Custom tables to create")
155
156 (defconstant +custom-cols+
157     '(("MRCON" "KPFSTR" "TEXT" 1024
158                (lambda (x) (pfstr-hash (parse-ui (nth 0 x)))))
159       ("MRCON" "KCUISUI" "BIGINT" 0
160        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 5 x))))))
161       ("MRCON" "KCUILUI" "BIGINT" 0
162        (lambda (x) (format nil "~d" (make-cuilui (parse-ui (nth 0 x)) (parse-ui (nth 3 x))))))
163       ("MRCON" "KCUILRL" "INTEGER" 0
164        (lambda (x) (format nil "~d" (cui-lrl (parse-ui (nth 0 x))))))
165       ("MRCON" "KLUILRL" "INTEGER" 0
166        (lambda (x) (format nil "~d" (lui-lrl (parse-ui (nth 3 x))))))
167       ("MRLO" "KLRL" "INTEGER" 0
168        (lambda (x) (format nil "~d" 
169                     (if (zerop (length (nth 4 x)))
170                         (cui-lrl (parse-ui (nth 0 x)))
171                       (cuisui-lrl (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 4 x))))))))
172       ("MRSTY" "KLRL" "INTEGER" 0
173        (lambda (x) (format nil "~d" (cui-lrl (parse-ui (nth 0 x))))))
174       ("MRCOC" "KLRL" "INTEGER" 0
175        (lambda (x) (format nil "~d" 
176                     (max (cui-lrl (parse-ui (nth 0 x)))
177                          (kmrcl:aif (cui-lrl (parse-ui (nth 1 x))) kmrcl::it 0)))))
178       ("MRSAT" "KSRL" "INTEGER" 0
179        (lambda (x) (format nil "~d" (sab-srl (nth 5 x)))))
180       ("MRREL" "KSRL" "INTEGER" 0
181        (lambda (x) (format nil "~d" (sab-srl (nth 4 x)))))
182       ("MRRANK" "KSRL" "INTEGER" 0
183        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
184       ("MRDEF" "KSRL" "INTEGER" 0
185        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
186       ("MRCXT" "KSRL" "INTEGER" 0
187        (lambda (x) (format nil "~d" (sab-srl (nth 2 x)))))
188       ("MRATX" "KSRL" "INTEGER" 0
189        (lambda (x) (format nil "~d" (sab-srl (nth 1 x)))))
190       ("MRXW.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       ("MRXW.NONENG" "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       ("MRXNW.ENG" "KLRL" "INTEGER" 0
199        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
200                                                  (parse-ui (nth 2 x))
201                                                  (parse-ui (nth 4 x)))))))
202       ("MRXNS.ENG" "KLRL" "INTEGER" 0
203        (lambda (x) (format nil "~d" (cuisui-lrl (make-cuisui 
204                                                  (parse-ui (nth 2 x))
205                                                  (parse-ui (nth 4 x)))))))
206       ("MRREL" "KPFSTR2" "TEXT" 1024
207        (lambda (x) (pfstr-hash (parse-ui (nth 2 x)))))
208       ("MRCOC" "KPFSTR2" "TEXT" 1024
209        (lambda (x) (pfstr-hash (parse-ui (nth 1 x)))))
210       ("MRCXT" "KCUISUI" "BIGINT" 0 
211        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 1 x))))))
212       ("MRSAT" "KCUILUI" "BIGINT" 0
213        (lambda (x) (format nil "~d" (make-cuilui (parse-ui (nth 0 x)) (parse-ui (nth 1 x))))))
214       ("MRSAT" "KCUISUI" "BIGINT" 0
215        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 2 x))))))
216       ("MRSO" "KCUISUI" "BIGINT" 0
217        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 0 x)) (parse-ui (nth 2 x))))))
218       ("MRXW.ENG" "KCUISUI" "BIGINT" 0
219        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
220       ("MRXNW.ENG" "KCUISUI" "BIGINT" 0
221        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
222       ("MRXNS.ENG" "KCUISUI" "BIGINT" 0
223        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x))))))
224       ("MRXW.NONENG" "LAT" "CHAR" 3 (lambda (x) (nth 0 x)))
225       ("MRXW.NONENG" "WD"  "CHAR" 200  (lambda (x) (nth 1 x)))
226       ("MRXW.NONENG" "CUI" "INTEGER" 0 (lambda (x) (nth 2 x)))
227       ("MRXW.NONENG" "LUI" "INTEGER" 0 (lambda (x) (nth 3 x)))
228       ("MRXW.NONENG" "SUI" "INTEGER" 0 (lambda (x) (nth 4 x)))
229       ("MRXW.NONENG" "KCUISUI" "BIGINT" 0 
230        (lambda (x) (format nil "~d" (make-cuisui (parse-ui (nth 2 x)) (parse-ui (nth 4 x)))))))
231   "Custom columns to create.(filename, col, sqltype, value-func).")
232
233 (defconstant +index-cols+
234     '(("CUI" "MRATX") ("CUI1" "MRCOC") ("CUI" "MRCON") ("LUI" "MRCON") 
235       ("LRL" "MRCON")
236       ("SUI" "MRCON") ("CUI" "MRCXT") ("CUI" "MRDEF") ("CUI" "MRLO")
237       ("CUI1" "MRREL") ("CUI" "MRSAT") ("LUI" "MRSAT") ("SUI" "MRSAT")
238       ("CUI" "MRSO") ("SAB" "MRSO") ("SRL" "MRSO") ("CUI" "MRSTY")
239       ("TUI" "MRSTY") ("CUI" "MRXNS_ENG") ("NSTR" "MRXNS_ENG" 10)
240       ("CUI" "MRXNW_ENG") ("NWD" "MRXNW_ENG") ("WD" "MRXW_ENG")
241       ("KCUISUI" "MRCON") ("KCUILUI" "MRCON") ("KCUILRL" "MRCON")
242       ("KLUILRL" "MRCON") ("KCUISUI" "MRCXT") 
243       ("KCUISUI" "MRSO") ("KCUISUI" "MRSAT")  ("KCUILUI" "MRSAT")
244       ("KCUISUI" "MRXW_ENG") ("KCUISUI" "MRXNW_ENG") 
245       ("KCUISUI" "MRXNS_ENG") ("KCUISUI" "MRXW_NONENG")
246       ("KSRL" "MRATX") ("KSRL" "MRCXT") ("KSRL" "MRDEF") ("KSRL" "MRRANK") 
247       ("KSRL" "MRREL") ("KSRL" "MRSAT") ("KLRL" "MRCOC") 
248       ("KLRL" "MRLO") ("KLRL" "MRSTY") ("KLRL" "MRXW_ENG") ("KLRL" "MRXNW_ENG")
249       ("KLRL" "MRXNS_ENG") ("KLRL" "MRXW_NONENG")
250       ;; LEX indices
251       ("EUI" "LRABR") ("EUI2" "LRABR") ("EUI" "LRAGR") ("EUI" "LRCMP") ("EUI" "LRMOD")
252       ("EUI" "LRNOM") ("EUI2" "LRNOM") ("EUI" "LRPRN") ("EUI" "LRPRP") ("EUI" "LRSPL")
253       ("EUI" "LRTRM") ("EUI" "LRTYP") ("EUI" "LRWD") ("WRD" "LRWD")
254       ("BAS" "LRABR") 
255       ;; Semantic NET indices
256       ("UI" "SRSTRE1") ("UI2" "SRSTRE1") ("UI3" "SRSTRE1") 
257       ("STY_RL" "SRDEF") ("RT" "SRDEF") ("STY_RL" "SRSTR") ("STY_RL2" "SRSTR")
258       ("RL" "SRSTR"))
259   "Columns in files to index")
260
261
262 (defconstant +custom-index-cols+
263   nil
264   #+ignore
265   '(("CUI" "MRCONFULL") ("SAB" "MRCONFULL") ("TUI" "MRCONFULL"))
266   "Indexes to custom tables")
267
268 ;; File & Column functions
269
270 (defun init-umls (&optional (alwaysclear nil))
271 "Initialize all UMLS file and column structures if not already initialized"
272   (when (or alwaysclear (null *umls-files*))
273     (init-umls-cols)
274     (init-umls-files)
275     (init-field-lengths)))
276
277 (defun init-umls-cols ()
278   (setq *umls-cols* (append 
279                      (init-meta-cols)
280                      (init-custom-cols)
281                      (init-generic-cols "LRFLD")
282                      (init-generic-cols "SRFLD"))))
283
284 (defun init-meta-cols ()
285 "Initialize all umls columns"  
286   (let ((cols '()))
287     (with-umls-file (line "MRCOLS")
288       (destructuring-bind (col des ref min av max fil dty) line
289         (let ((c (make-umls-col       
290                   :col col
291                   :des des
292                   :ref ref
293                   :min (parse-integer min)
294                   :av (read-from-string av)
295                   :max (parse-integer max)
296                   :fil fil
297                   :dty dty  ;; new in 2002 UMLS
298                   :sqltype "VARCHAR"    ; default data type
299                   :parsefunc #'add-sql-quotes
300                   :custom-value-func nil
301                   :quotechar "'")))
302           (add-datatype-to-col c (datatype-for-col col))
303           (push c cols))))
304     (nreverse cols)))
305
306 (defun init-custom-cols ()
307 "Initialize umls columns for custom columns"  
308   (let ((cols '()))
309     (dolist (customcol +custom-cols+)
310       (let ((c (make-umls-col :col (nth 1 customcol)
311                               :des ""
312                               :ref 0
313                               :min 0
314                               :max (nth 3 customcol)
315                               :av 0
316                               :dty nil
317                               :fil (nth 0 customcol)
318                               :sqltype (nth 2 customcol)
319                               :parsefunc #'add-sql-quotes
320                               :custom-value-func (nth 4 customcol)
321                               :quotechar "'")))
322         (add-datatype-to-col c (datatype-for-col (nth 1 customcol)))
323         (push c cols)))
324     (nreverse cols)))
325
326 (defun escape-column-name (name)
327   (substitute #\_ #\/ name))
328
329 (defun init-generic-cols (col-filename)
330 "Initialize for generic (LEX/NET) columns"  
331   (let ((cols '()))
332     (with-umls-file (line col-filename)
333       (destructuring-bind (nam des ref fil) line
334         (setq nam (escape-column-name nam))
335         (dolist (file (delimited-string-to-list fil #\,))
336           (let ((c (make-umls-col             
337                   :col nam
338                   :des des
339                   :ref ref
340                   :min nil
341                   :av nil
342                   :max nil
343                   :fil file
344                   :dty nil
345                   :sqltype "VARCHAR"    ; default data type
346                   :parsefunc #'add-sql-quotes
347                   :custom-value-func nil
348                   :quotechar "'")))
349             (add-datatype-to-col c (datatype-for-col nam))
350             (push c cols)))))
351     (nreverse cols)))
352
353 (defun init-umls-files ()
354   (setq *umls-files* (append
355                       (init-generic-files "MRFILES") 
356                       (init-generic-files "LRFIL") 
357                       (init-generic-files "SRFIL")))
358   ;; need to separate this since init-custom-files depends on *umls-files*
359   (setq *umls-files* (append *umls-files* (init-custom-files))))
360
361
362 (defun umls-field-string-to-list (fmt)
363   "Converts a comma delimited list of fields into a list of field names. Will
364 append a unique number (starting at 2) onto a column name that is repeated in the list"
365   (let ((field-list (delimited-string-to-list (escape-column-name fmt) #\,))
366         (col-count (make-hash-table :test 'equal)))
367     (dotimes (i (length field-list))
368       (declare (fixnum i))
369       (let ((col (nth i field-list)))
370         (multiple-value-bind (key found) (gethash col col-count)
371           (if found
372               (let ((next-id (1+ key)))
373                 (setf (nth i field-list) (concatenate 'string 
374                                                     col
375                                                     (format nil "~D" next-id)))
376                 (setf (gethash col col-count) next-id))
377             (setf (gethash col col-count) 1)))))
378     field-list))
379
380 (defun init-generic-files (files-filename)
381 "Initialize all LEX file structures"  
382   (let ((files '()))
383   (with-umls-file (line files-filename)
384     (destructuring-bind (fil des fmt cls rws bts) line
385       (let ((f (make-umls-file 
386                 :fil fil
387                 :table (substitute #\_ #\. fil)
388                 :des des
389                 :fmt (escape-column-name fmt)
390                 :cls (parse-integer cls)
391                 :rws (parse-integer rws)
392                 :bts (parse-integer bts)
393                 :fields (concatenate 'list
394                           (umls-field-string-to-list fmt)
395                           (custom-colnames-for-filename fil)))))
396         (setf (umls-file-colstructs f) (umls-cols-for-umls-file f))
397         (push f files))))
398   (nreverse files)))
399
400 (defun init-custom-files ()
401   (let ((ffile (make-umls-file :fil "MRXW.NONENG"
402                                :des "Custom NonEnglish Index"
403                                :table "MRXW_NONENG"
404                                :cls 5
405                                :rws 0
406                                :bts 0
407                                :fields (umls-file-fields (find-umls-file "MRXW.ENG")))))
408     (setf (umls-file-colstructs ffile)
409       (umls-cols-for-umls-file ffile))
410     (list ffile)))
411
412 (defun datatype-for-col (colname)
413 "Return datatype for column name"  
414   (car (cdr (find colname +col-datatypes+ :key #'car :test #'string-equal))))
415
416 (defun add-datatype-to-col (col datatype)
417 "Add data type information to column"
418   (setf (umls-col-datatype col) datatype)
419   (case datatype
420     (sql-u (setf (umls-col-sqltype col) "INTEGER"
421                  (umls-col-parsefunc col) #'parse-ui
422                  (umls-col-quotechar col) ""))
423     (sql-s (setf (umls-col-sqltype col) "SMALLINT" 
424                  (umls-col-parsefunc col) #'parse-integer
425                  (umls-col-quotechar col) ""))
426     (sql-l (setf (umls-col-sqltype col) "BIGINT" 
427                  (umls-col-parsefunc col) #'parse-integer
428                  (umls-col-quotechar col) ""))
429     (sql-i (setf (umls-col-sqltype col) "INTEGER" 
430                  (umls-col-parsefunc col) #'parse-integer
431                  (umls-col-quotechar col) ""))
432     (sql-f (setf (umls-col-sqltype col) "NUMERIC" 
433                  (umls-col-parsefunc col) #'read-from-string
434                  (umls-col-quotechar col) ""))
435     (t                       ; Default column type, optimized text storage
436      (setf (umls-col-parsefunc col) #'add-sql-quotes 
437            (umls-col-quotechar col) "'")
438      (when (and (umls-col-max col) (umls-col-av col))
439        (if (> (umls-col-max col) 255)
440            (setf (umls-col-sqltype col) "TEXT")
441          (if (< (- (umls-col-max col) (umls-col-av col)) 4) 
442              (setf (umls-col-sqltype col) "CHAR") ; if average bytes wasted < 4
443            (setf (umls-col-sqltype col) "VARCHAR")))))))
444
445
446