Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * Authors: M. Ivanova, M. Kersten, N. Nes
15 : *
16 : * This module contains primitives for accessing data in FITS file format.
17 : */
18 :
19 : #include "monetdb_config.h"
20 : #include <glob.h>
21 :
22 : /* clash with GDK? */
23 : #undef ttype
24 : #include <fitsio.h>
25 : #include <fitsio2.h>
26 : #include <longnam.h>
27 :
28 : #include "fits.h"
29 : #include "mutils.h"
30 : #include "sql_mvc.h"
31 : #include "sql_scenario.h"
32 : #include "sql_execute.h"
33 : #include "sql.h"
34 : #include "mal_exception.h"
35 :
36 : #define FITS_INS_COL "INSERT INTO sys.fits_columns(id, name, type, units, number, table_id) \
37 : VALUES(%d,'%s','%s','%s',%d,%d);"
38 : #define ATTACHDIR "call sys.fitsattach('%s');"
39 :
40 : static void
41 0 : FITSinitCatalog(mvc *m)
42 : {
43 0 : sql_schema *sch;
44 0 : sql_table *fits_tp, *fits_fl, *fits_tbl, *fits_col;
45 0 : sql_column *col = NULL;
46 :
47 0 : sch = mvc_bind_schema(m, "sys");
48 :
49 0 : fits_fl = mvc_bind_table(m, sch, "fits_files");
50 0 : if (fits_fl == NULL) {
51 0 : mvc_create_table(&fits_fl, m, sch, "fits_files", tt_table, 0, SQL_PERSIST, 0, 2, 0);
52 0 : mvc_create_column_(&col, m, fits_fl, "id", "int", 32);
53 0 : mvc_create_column_(&col, m, fits_fl, "name", "varchar", 80);
54 : }
55 :
56 0 : fits_tbl = mvc_bind_table(m, sch, "fits_tables");
57 0 : if (fits_tbl == NULL) {
58 0 : mvc_create_table(&fits_tbl, m, sch, "fits_tables", tt_table, 0, SQL_PERSIST, 0, 8, 0);
59 0 : mvc_create_column_(&col, m, fits_tbl, "id", "int", 32);
60 0 : mvc_create_column_(&col, m, fits_tbl, "name", "varchar", 80);
61 0 : mvc_create_column_(&col, m, fits_tbl, "columns", "int", 32);
62 0 : mvc_create_column_(&col, m, fits_tbl, "file_id", "int", 32);
63 0 : mvc_create_column_(&col, m, fits_tbl, "hdu", "int", 32);
64 0 : mvc_create_column_(&col, m, fits_tbl, "date", "varchar", 80);
65 0 : mvc_create_column_(&col, m, fits_tbl, "origin", "varchar", 80);
66 0 : mvc_create_column_(&col, m, fits_tbl, "comment", "varchar", 80);
67 : }
68 :
69 0 : fits_col = mvc_bind_table(m, sch, "fits_columns");
70 0 : if (fits_col == NULL) {
71 0 : mvc_create_table(&fits_col, m, sch, "fits_columns", tt_table, 0, SQL_PERSIST, 0, 6, 0);
72 0 : mvc_create_column_(&col, m, fits_col, "id", "int", 32);
73 0 : mvc_create_column_(&col, m, fits_col, "name", "varchar", 80);
74 0 : mvc_create_column_(&col, m, fits_col, "type", "varchar", 80);
75 0 : mvc_create_column_(&col, m, fits_col, "units", "varchar", 80);
76 0 : mvc_create_column_(&col, m, fits_col, "number", "int", 32);
77 0 : mvc_create_column_(&col, m, fits_col, "table_id", "int", 32);
78 : }
79 :
80 0 : fits_tp = mvc_bind_table(m, sch, "fits_table_properties");
81 0 : if (fits_tp == NULL) {
82 0 : mvc_create_table(&fits_tp, m, sch, "fits_table_properties", tt_table, 0, SQL_PERSIST, 0, 5, 0);
83 0 : mvc_create_column_(&col, m, fits_tp, "table_id", "int", 32);
84 0 : mvc_create_column_(&col, m, fits_tp, "xtension", "varchar", 80);
85 0 : mvc_create_column_(&col, m, fits_tp, "bitpix", "int", 32);
86 0 : mvc_create_column_(&col, m, fits_tp, "stilvers", "varchar", 80);
87 0 : mvc_create_column_(&col, m, fits_tp, "stilclas", "varchar", 80);
88 : }
89 0 : }
90 :
91 : static int
92 0 : fits2mtype(int t, int rep)
93 : {
94 0 : if (rep > 1) {
95 : return TYPE_blob;
96 : }
97 0 : switch (t) {
98 : case TBIT:
99 : case TLOGICAL:
100 : return TYPE_bit;
101 : case TBYTE:
102 : case TSBYTE:
103 : return TYPE_bte;
104 : case TSTRING:
105 : return TYPE_str;
106 : case TUSHORT:
107 : case TSHORT:
108 : return TYPE_sht;
109 : case TUINT:
110 : case TINT:
111 : return TYPE_int;
112 : case TLONG:
113 : case TULONG:
114 : case TLONGLONG:
115 : return TYPE_lng;
116 : case TFLOAT:
117 : return TYPE_flt;
118 : case TDOUBLE:
119 : return TYPE_dbl;
120 : /* missing */
121 : case TCOMPLEX:
122 : case TDBLCOMPLEX:
123 : return -1;
124 : }
125 : return -1;
126 : }
127 :
128 : static int
129 0 : fits2subtype(sql_subtype *tpe, int t, long rep, long wid) /* type long used by fits library */
130 : {
131 0 : if (rep > 1) {
132 0 : sql_find_subtype(tpe, "blob", (unsigned int)rep*wid, 0);
133 0 : return 1;
134 : }
135 0 : switch (t) {
136 0 : case TBIT:
137 : case TLOGICAL:
138 0 : sql_find_subtype(tpe, "boolean", 0, 0);
139 0 : break;
140 0 : case TBYTE:
141 : case TSBYTE:
142 0 : sql_find_subtype(tpe, "char", 1, 0);
143 0 : break;
144 0 : case TSTRING:
145 0 : sql_find_subtype(tpe, "varchar", (unsigned int)wid, 0);
146 0 : break;
147 0 : case TUSHORT:
148 : case TSHORT:
149 0 : sql_find_subtype(tpe, "smallint", 16, 0);
150 0 : break;
151 0 : case TUINT:
152 : case TINT:
153 0 : sql_find_subtype(tpe, "int", 32, 0);
154 0 : break;
155 0 : case TULONG:
156 : case TLONG:
157 : case TLONGLONG:
158 0 : sql_find_subtype(tpe, "bigint", 64, 0);
159 0 : break;
160 0 : case TFLOAT:
161 0 : sql_find_subtype(tpe, "real", 32, 0);
162 0 : break;
163 0 : case TDOUBLE:
164 0 : sql_find_subtype(tpe, "double", 51, 0);
165 0 : break;
166 : /* missing */
167 : case TCOMPLEX:
168 : case TDBLCOMPLEX:
169 : return -1;
170 : }
171 : return 1;
172 : }
173 :
174 0 : str FITSexportTable(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
175 : {
176 0 : str msg = MAL_SUCCEED;
177 0 : str tname = *getArgReference_str(stk, pci, 1);
178 0 : mvc *m = NULL;
179 0 : sql_trans *tr;
180 0 : sql_schema *sch;
181 0 : sql_table *tbl, *column, *tables = NULL;
182 0 : sql_column *col;
183 0 : oid rid = oid_nil;
184 0 : str type, name, *colname, *tform;
185 0 : fitsfile *fptr;
186 0 : char filename[BUFSIZ];
187 0 : size_t nrows = 0;
188 0 : long optimal; /* type long used by fits library */
189 0 : rids * rs;
190 :
191 0 : lng tm0, texportboolean=0, texportchar=0, texportstring=0, texportshort=0, texportint=0, texportlng=0, texportfloat=0, texportdouble=0;
192 0 : size_t numberrow = 0, dimension = 0;
193 0 : int cc = 0, status = 0, j = 0, columns, *fid, block = 0;
194 0 : int boolcols = 0, charcols = 0, strcols = 0, shortcols = 0, intcols = 0, lngcols = 0, floatcols = 0, doublecols = 0;
195 0 : int hdutype;
196 :
197 0 : char *charvalue, *readcharrows;
198 0 : str strvalue; char **readstrrows;
199 0 : short *shortvalue, *readshortrows;
200 0 : int *intvalue, *readintrows;
201 0 : lng *lngvalue, *readlngrows;
202 0 : float *realvalue, *readfloatrows;
203 0 : double *doublevalue, *readdoublerows;
204 0 : _Bool *boolvalue, *readboolrows;
205 :
206 0 : if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != MAL_SUCCEED)
207 : return msg;
208 0 : if ((msg = checkSQLContext(cntxt)) != MAL_SUCCEED)
209 : return msg;
210 :
211 0 : tr = m->session->tr;
212 0 : sqlstore *store = tr->store;
213 0 : sch = mvc_bind_schema(m, "sys");
214 :
215 : /* First step: look if the table exists in the database. If the table is not in the database, the export function cannot continue */
216 :
217 0 : tbl = mvc_bind_table(m, sch, tname);
218 0 : if (tbl == NULL) {
219 0 : msg = createException (MAL, "fits.exporttable", SQLSTATE(FI000) "Table %s is missing.\n", tname);
220 0 : return msg;
221 : }
222 :
223 :
224 0 : columns = ol_length((*tbl).columns);
225 0 : colname = (str *) GDKmalloc(columns * sizeof(str));
226 0 : tform = (str *) GDKmalloc(columns * sizeof(str));
227 0 : if (colname == NULL || tform == NULL) {
228 0 : GDKfree(colname);
229 0 : GDKfree(tform);
230 0 : throw(MAL, "fits.exporttable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
231 : }
232 :
233 0 : TRC_DEBUG(FITS, "Number of columns: %d\n", columns);
234 :
235 0 : tables = mvc_bind_table(m, sch, "_tables");
236 0 : col = mvc_bind_column(m, tables, "name");
237 0 : rid = store->table_api.column_find_row(m->session->tr, col, tname, NULL);
238 :
239 0 : col = mvc_bind_column(m, tables, "id");
240 0 : fid = (int*) store->table_api.column_find_value(m->session->tr, col, rid);
241 :
242 0 : column = mvc_bind_table(m, sch, "_columns");
243 0 : col = mvc_bind_column(m, column, "table_id");
244 :
245 0 : rs = store->table_api.rids_select(m->session->tr, col, (void *) fid, (void *) fid, NULL);
246 0 : if (!rs)
247 0 : throw(MAL, "fits.exporttable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
248 0 : GDKfree(fid);
249 :
250 0 : while ((rid = store->table_api.rids_next(rs)), !is_oid_nil(rid))
251 : {
252 0 : col = mvc_bind_column(m, column, "name");
253 0 : name = (char *) store->table_api.column_find_value(m->session->tr, col, rid);
254 0 : colname[j] = toLower(name);
255 0 : GDKfree(name);
256 :
257 0 : col = mvc_bind_column(m, column, "type");
258 0 : type = (char *) store->table_api.column_find_value(m->session->tr, col, rid);
259 :
260 0 : if (strcmp(type,"boolean")==0) tform[j] = "1L";
261 :
262 0 : if (strcmp(type,"char")==0) tform[j] = "1S";
263 :
264 0 : if (strcmp(type,"varchar")==0) tform[j] = "8A";
265 :
266 0 : if (strcmp(type,"smallint")==0) tform[j] = "1I";
267 :
268 0 : if (strcmp(type,"int")==0) tform[j] = "1J";
269 :
270 0 : if (strcmp(type,"bigint")==0) tform[j] = "1K";
271 :
272 0 : if (strcmp(type,"real")==0) tform[j] = "1E";
273 :
274 0 : if (strcmp(type,"double")==0) tform[j] = "1D";
275 0 : GDKfree(type);
276 :
277 0 : j++;
278 : }
279 :
280 0 : col = mvc_bind_column(m, tbl, colname[0]);
281 :
282 0 : nrows = store->storage_api.count_col(tr, col, 0);
283 0 : assert(nrows <= (size_t) GDK_oid_max);
284 :
285 0 : snprintf(filename,BUFSIZ,"\n%s.fit",tname);
286 0 : TRC_INFO(FITS, "Filename: %s\n", filename);
287 :
288 0 : MT_remove(filename);
289 :
290 0 : status=0;
291 :
292 0 : fits_create_file(&fptr, filename, &status);
293 0 : fits_create_img(fptr, USHORT_IMG, 0, NULL, &status);
294 0 : fits_close_file(fptr, &status);
295 0 : fits_open_file(&fptr, filename, READWRITE, &status);
296 :
297 0 : fits_movabs_hdu(fptr, 1, &hdutype, &status);
298 0 : fits_create_tbl( fptr, BINARY_TBL, 0, columns, colname, tform, NULL, tname, &status);
299 :
300 0 : for (cc = 0; cc < columns; cc++)
301 : {
302 0 : char * columntype;
303 0 : col = mvc_bind_column(m, tbl, colname[cc]);
304 0 : columntype = col -> type.type->base.name;
305 :
306 0 : if (strcmp(columntype,"boolean")==0)
307 : {
308 0 : boolcols++; dimension = 0; block = 0;
309 0 : fits_get_rowsize(fptr,&optimal,&status);
310 0 : readboolrows = (_Bool *) GDKmalloc (sizeof(_Bool) * optimal);
311 :
312 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
313 : {
314 0 : boolvalue = (_Bool*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
315 0 : readboolrows[dimension] = *boolvalue;
316 0 : GDKfree(boolvalue);
317 0 : dimension++;
318 :
319 0 : if (dimension == (size_t) optimal)
320 : {
321 0 : dimension = 0;
322 0 : tm0 = GDKusec();
323 0 : fits_write_col(fptr, TLOGICAL, cc+1, (optimal*block)+1, 1, optimal, readboolrows, &status);
324 0 : texportboolean += GDKusec() - tm0;
325 0 : GDKfree(readboolrows);
326 0 : readboolrows = (_Bool *) GDKmalloc (sizeof(_Bool) * optimal);
327 0 : block++;
328 : }
329 : }
330 0 : tm0 = GDKusec();
331 0 : fits_write_col(fptr, TLOGICAL, cc+1, (optimal*block)+1, 1, dimension, readboolrows, &status);
332 0 : texportboolean += GDKusec() - tm0;
333 0 : GDKfree(readboolrows);
334 : }
335 :
336 0 : if (strcmp(columntype,"char")==0)
337 : {
338 0 : charcols++; dimension = 0; block = 0;
339 0 : fits_get_rowsize(fptr,&optimal,&status);
340 0 : readcharrows = (char *) GDKmalloc (sizeof(char) * optimal);
341 :
342 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
343 : {
344 0 : charvalue = (char*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
345 0 : readcharrows[dimension] = *charvalue;
346 0 : GDKfree(charvalue);
347 0 : dimension++;
348 :
349 0 : if (dimension == (size_t) optimal)
350 : {
351 0 : dimension = 0;
352 0 : tm0 = GDKusec();
353 0 : fits_write_col(fptr, TBYTE, cc+1, (optimal*block)+1, 1, optimal, readcharrows, &status);
354 0 : texportchar += GDKusec() - tm0;
355 0 : GDKfree(readcharrows);
356 0 : readcharrows = (char *) GDKmalloc (sizeof(char) * optimal);
357 0 : block++;
358 : }
359 : }
360 0 : tm0 = GDKusec();
361 0 : fits_write_col(fptr, TBYTE, cc+1, (optimal*block)+1, 1, dimension, readcharrows, &status);
362 0 : texportchar += GDKusec() - tm0;
363 0 : GDKfree(readcharrows);
364 : }
365 :
366 0 : if (strcmp(columntype,"varchar")==0)
367 : {
368 0 : strcols++; dimension=0; block=0;
369 0 : fits_get_rowsize(fptr,&optimal,&status);
370 0 : readstrrows = (char **) GDKmalloc (sizeof(char *) * optimal);
371 :
372 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
373 : {
374 0 : strvalue = (char *) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
375 0 : readstrrows[dimension] = strvalue;
376 0 : dimension++;
377 :
378 0 : if (dimension == (size_t) optimal)
379 : {
380 0 : dimension = 0;
381 0 : tm0 = GDKusec();
382 0 : fits_write_col_str(fptr, cc+1, (optimal*block)+1, 1, optimal, readstrrows, &status);
383 0 : texportstring += GDKusec() - tm0;
384 0 : for (dimension = 0; dimension < (size_t) optimal; dimension++)
385 0 : GDKfree(readstrrows[dimension]);
386 0 : dimension = 0;
387 0 : GDKfree(readstrrows);
388 0 : readstrrows = (char **) GDKmalloc(sizeof(char *) * optimal);
389 0 : block++;
390 : }
391 : }
392 0 : tm0 = GDKusec();
393 0 : fits_write_col_str(fptr, cc+1, (optimal*block)+1, 1, dimension, readstrrows, &status);
394 0 : texportstring += GDKusec() - tm0;
395 0 : for (numberrow = 0; numberrow < dimension; numberrow++)
396 0 : GDKfree(readstrrows[numberrow]);
397 0 : GDKfree(readstrrows);
398 : }
399 :
400 0 : if (strcmp(columntype,"smallint")==0)
401 : {
402 0 : shortcols++; dimension = 0; block = 0;
403 0 : fits_get_rowsize(fptr,&optimal,&status);
404 0 : readshortrows = (short *) GDKmalloc (sizeof(short) * optimal);
405 :
406 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
407 : {
408 0 : shortvalue = (short*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
409 0 : readshortrows[dimension] = *shortvalue;
410 0 : GDKfree(shortvalue);
411 0 : dimension++;
412 :
413 0 : if (dimension == (size_t) optimal)
414 : {
415 0 : dimension = 0;
416 0 : tm0 = GDKusec();
417 0 : fits_write_col(fptr, TSHORT, cc+1, (optimal*block)+1, 1, optimal, readshortrows, &status);
418 0 : texportshort += GDKusec() - tm0;
419 0 : GDKfree(readshortrows);
420 0 : readshortrows = (short *) GDKmalloc (sizeof(short) * optimal);
421 0 : block++;
422 : }
423 : }
424 0 : tm0 = GDKusec();
425 0 : fits_write_col(fptr, TSHORT, cc+1, (optimal*block)+1, 1, dimension, readshortrows, &status);
426 0 : texportshort += GDKusec() - tm0;
427 0 : GDKfree(readshortrows);
428 : }
429 :
430 0 : if (strcmp(columntype,"int")==0)
431 : {
432 0 : intcols++; dimension = 0; block = 0;
433 0 : fits_get_rowsize(fptr,&optimal,&status);
434 0 : readintrows = (int *) GDKmalloc (sizeof(int) * optimal);
435 :
436 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
437 : {
438 0 : intvalue = (int*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
439 0 : readintrows[dimension] = *intvalue;
440 0 : GDKfree(intvalue);
441 0 : dimension++;
442 :
443 0 : if (dimension == (size_t) optimal)
444 : {
445 0 : dimension = 0;
446 0 : tm0 = GDKusec();
447 0 : fits_write_col(fptr, TINT, cc+1, (optimal*block)+1, 1, optimal, readintrows, &status);
448 0 : texportint += GDKusec() - tm0;
449 0 : GDKfree(readintrows);
450 0 : readintrows = (int *) GDKmalloc (sizeof(int) * optimal);
451 0 : block++;
452 : }
453 : }
454 0 : tm0 = GDKusec();
455 0 : fits_write_col(fptr, TINT, cc+1, (optimal*block)+1, 1, dimension, readintrows, &status);
456 0 : texportint += GDKusec() - tm0;
457 0 : GDKfree(readintrows);
458 : }
459 :
460 0 : if (strcmp(columntype,"bigint")==0)
461 : {
462 0 : lngcols++; dimension = 0; block = 0;
463 0 : fits_get_rowsize(fptr,&optimal,&status);
464 0 : readlngrows = (lng *) GDKmalloc (sizeof(lng) * optimal);
465 :
466 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
467 : {
468 0 : lngvalue = (lng*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
469 0 : readlngrows[dimension] = *lngvalue;
470 0 : GDKfree(lngvalue);
471 0 : dimension++;
472 :
473 0 : if (dimension == (size_t) optimal)
474 : {
475 0 : dimension = 0;
476 0 : tm0 = GDKusec();
477 0 : fits_write_col(fptr, TLONG, cc+1, (optimal*block)+1, 1, optimal, readlngrows, &status);
478 0 : texportlng += GDKusec() - tm0;
479 0 : GDKfree(readlngrows);
480 0 : readlngrows = (lng *) GDKmalloc (sizeof(lng) * optimal);
481 0 : block++;
482 : }
483 : }
484 0 : tm0 = GDKusec();
485 0 : fits_write_col(fptr, TLONG, cc+1, (optimal*block)+1, 1, dimension, readlngrows, &status);
486 0 : texportlng += GDKusec() - tm0;
487 0 : GDKfree(readlngrows);
488 : }
489 :
490 0 : if (strcmp(columntype,"real")==0)
491 : {
492 0 : floatcols++; dimension = 0; block = 0;
493 0 : fits_get_rowsize(fptr,&optimal,&status);
494 0 : readfloatrows = (float *) GDKmalloc (sizeof(float) * optimal);
495 :
496 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
497 : {
498 0 : realvalue = (float*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
499 0 : readfloatrows[dimension] = *realvalue;
500 0 : GDKfree(realvalue);
501 0 : dimension++;
502 :
503 0 : if (dimension == (size_t) optimal)
504 : {
505 0 : dimension = 0;
506 0 : tm0 = GDKusec();
507 0 : fits_write_col(fptr, TFLOAT, cc+1, (optimal*block)+1, 1, optimal, readfloatrows, &status);
508 0 : texportfloat += GDKusec() - tm0;
509 0 : GDKfree(readfloatrows);
510 0 : readfloatrows = (float *) GDKmalloc (sizeof(float) * optimal);
511 0 : block++;
512 : }
513 : }
514 0 : tm0 = GDKusec();
515 0 : fits_write_col(fptr, TFLOAT, cc+1, (optimal*block)+1, 1, dimension, readfloatrows, &status);
516 0 : texportfloat += GDKusec() - tm0;
517 0 : GDKfree(readfloatrows);
518 : }
519 :
520 0 : if (strcmp(columntype,"double")==0)
521 : {
522 0 : doublecols++; dimension = 0; block = 0;
523 0 : fits_get_rowsize(fptr,&optimal,&status);
524 0 : readdoublerows = (double *) GDKmalloc (sizeof(double) * optimal);
525 :
526 0 : for (numberrow = 0; numberrow < nrows ; numberrow++)
527 : {
528 0 : doublevalue = (double*) store->table_api.column_find_value(m->session->tr, col, (oid) numberrow);
529 0 : readdoublerows[dimension] = *doublevalue;
530 0 : GDKfree(doublevalue);
531 0 : dimension++;
532 :
533 0 : if (dimension == (size_t) optimal)
534 : {
535 0 : dimension = 0;
536 0 : tm0 = GDKusec();
537 0 : fits_write_col(fptr, TDOUBLE, cc+1, (optimal*block)+1, 1, optimal, readdoublerows, &status);
538 0 : texportdouble += GDKusec() - tm0;
539 0 : GDKfree(readdoublerows);
540 0 : readdoublerows = (double *) GDKmalloc (sizeof(double) * optimal);
541 0 : block++;
542 : }
543 : }
544 0 : tm0 = GDKusec();
545 0 : fits_write_col(fptr, TDOUBLE, cc+1, (optimal*block)+1, 1, optimal, readdoublerows, &status);
546 0 : texportdouble += GDKusec() - tm0;
547 0 : GDKfree(readdoublerows);
548 : }
549 : }
550 :
551 :
552 : // print all the times that were needed to export each one of the columns
553 0 : if (texportboolean > 0) TRC_DEBUG(FITS, "%d Boolean\tcolumn(s) exported in "LLFMT" usec\n", boolcols, texportboolean);
554 0 : if (texportchar > 0) TRC_DEBUG(FITS, "%d Char\t\tcolumn(s) exported in "LLFMT" usec\n", charcols, texportchar);
555 0 : if (texportstring > 0) TRC_DEBUG(FITS, "%d String\tcolumn(s) exported in "LLFMT" usec\n", strcols, texportstring);
556 0 : if (texportshort > 0) TRC_DEBUG(FITS, "%d Short\t\tcolumn(s) exported in "LLFMT" usec\n", shortcols, texportshort);
557 0 : if (texportint > 0) TRC_DEBUG(FITS, "%d Integer\tcolumn(s) exported in "LLFMT" usec\n", intcols, texportint);
558 0 : if (texportlng > 0) TRC_DEBUG(FITS, "%d Long\t\tcolumn(s) exported in "LLFMT" usec\n", lngcols, texportlng);
559 0 : if (texportfloat > 0) TRC_DEBUG(FITS, "%d Float\t\tcolumn(s) exported in "LLFMT" usec\n", floatcols, texportfloat);
560 0 : if (texportdouble > 0) TRC_DEBUG(FITS, "%d Double\tcolumn(s) exported in "LLFMT" usec\n", doublecols, texportdouble);
561 :
562 :
563 0 : fits_close_file(fptr, &status);
564 0 : return msg;
565 : }
566 :
567 :
568 0 : str FITSdir(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
569 : {
570 0 : str msg = MAL_SUCCEED;
571 0 : str dir = *getArgReference_str(stk, pci, 1);
572 0 : DIR *dp;
573 0 : struct dirent *ep;
574 0 : fitsfile *fptr;
575 0 : char *s;
576 0 : int status = 0;
577 0 : (void)mb;
578 :
579 0 : dp = opendir(dir);
580 0 : if (dp != NULL) {
581 : char stmt[BUFSIZ];
582 : char fname[BUFSIZ];
583 :
584 : s = stmt;
585 :
586 0 : while ((ep = readdir(dp)) != NULL && !msg) {
587 0 : snprintf(fname, sizeof(fname), "%s/%s", dir, ep->d_name);
588 0 : status = 0;
589 0 : fits_open_file(&fptr, fname, READONLY, &status);
590 0 : if (status == 0) {
591 0 : char *filename = SQLescapeString(fname);
592 0 : if (!filename) {
593 0 : msg = createException(MAL, "fits.listdir", SQLSTATE(HY013) MAL_MALLOC_FAIL);
594 0 : break;
595 : }
596 0 : snprintf(stmt, sizeof(stmt), ATTACHDIR, filename);
597 0 : GDKfree(filename);
598 0 : TRC_DEBUG(FITS, "Executing: %s\n", s);
599 0 : msg = SQLstatementIntern(cntxt, s, "fits.listofdir", TRUE, FALSE, NULL);
600 0 : fits_close_file(fptr, &status);
601 : }
602 :
603 : }
604 0 : (void)closedir(dp);
605 : } else
606 0 : msg = createException(MAL, "listdir", SQLSTATE(FI000) "Couldn't open the directory");
607 :
608 0 : return msg;
609 : }
610 :
611 0 : str FITSdirpat(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
612 : {
613 0 : str msg = MAL_SUCCEED;
614 0 : str dir = *getArgReference_str(stk, pci, 1);
615 0 : str pat = *getArgReference_str(stk, pci, 2);
616 0 : fitsfile *fptr;
617 0 : char *s;
618 0 : int status = 0;
619 0 : glob_t globbuf;
620 0 : char fulldirectory[BUFSIZ];
621 0 : size_t j = 0;
622 :
623 0 : (void)mb;
624 :
625 0 : globbuf.gl_offs = 0;
626 0 : snprintf(fulldirectory, sizeof(fulldirectory), "%s/%s", dir, pat);
627 0 : glob(fulldirectory, GLOB_DOOFFS, NULL, &globbuf);
628 :
629 0 : TRC_DEBUG(FITS, "Fulldir: %s - Size: %zu\n", fulldirectory, globbuf.gl_pathc);
630 :
631 0 : if (globbuf.gl_pathc == 0)
632 0 : throw(MAL, "fits.listdirpat", SQLSTATE(FI000) "Couldn't open the directory or there are no files that match the pattern");
633 :
634 0 : for (j = 0; j < globbuf.gl_pathc; j++) {
635 0 : char stmt[BUFSIZ];
636 0 : char fname[BUFSIZ];
637 :
638 0 : s = stmt;
639 0 : strcpy_len(fname, globbuf.gl_pathv[j], sizeof(fname));
640 0 : status = 0;
641 0 : fits_open_file(&fptr, fname, READONLY, &status);
642 0 : if (status == 0) {
643 0 : char *filename = SQLescapeString(fname);
644 0 : if (!filename) {
645 0 : throw(MAL, "fits.listdirpat", SQLSTATE(HY013) MAL_MALLOC_FAIL);
646 : }
647 0 : snprintf(stmt, sizeof(stmt), ATTACHDIR, filename);
648 0 : GDKfree(filename);
649 0 : TRC_DEBUG(FITS, "Executing: %s\n", s);
650 0 : msg = SQLstatementIntern(cntxt, s, "fits.listofdirpat", TRUE, FALSE, NULL);
651 0 : fits_close_file(fptr, &status);
652 :
653 0 : break;
654 : }
655 : }
656 :
657 : return msg;
658 : }
659 :
660 :
661 : str
662 0 : FITStest(int *res, str *fname)
663 : {
664 0 : fitsfile *fptr; /* pointer to the FITS file, defined in fitsio.h */
665 0 : str msg = MAL_SUCCEED;
666 0 : int status = 0, hdutype;
667 :
668 0 : *res = 0;
669 0 : if (fits_open_file(&fptr, *fname, READONLY, &status))
670 0 : msg = createException(MAL, "fits.test", SQLSTATE(FI000) "Missing FITS file %s", *fname);
671 : else {
672 0 : fits_movabs_hdu(fptr, 2, &hdutype, &status);
673 0 : *res = hdutype;
674 0 : fits_close_file(fptr, &status);
675 : }
676 :
677 0 : return msg;
678 : }
679 :
680 0 : str FITSattach(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
681 : {
682 0 : mvc *m = NULL;
683 0 : sql_trans *tr;
684 0 : sql_schema *sch;
685 0 : sql_table *fits_tp, *fits_fl, *fits_tbl, *fits_col, *tbl = NULL;
686 0 : sql_column *col;
687 0 : str msg = MAL_SUCCEED;
688 0 : str fname = *getArgReference_str(stk, pci, 1);
689 0 : fitsfile *fptr; /* pointer to the FITS file */
690 0 : int status = 0, i, j, hdutype, hdunum = 1, cnum = 0, bitpixnumber = 0;
691 0 : oid fid, tid, cid, rid = oid_nil;
692 0 : char tname[BUFSIZ], *tname_low = NULL, *s, bname[BUFSIZ-100], stmt[BUFSIZ];
693 0 : long tbcol; /* type long used by fits library */
694 0 : char cname[BUFSIZ], tform[BUFSIZ], tunit[BUFSIZ], tnull[BUFSIZ], tdisp[BUFSIZ];
695 0 : char *esc_cname, *esc_tform, *esc_tunit;
696 0 : double tscal, tzero;
697 0 : char xtensionname[BUFSIZ] = "", stilversion[BUFSIZ] = "";
698 0 : char stilclass[BUFSIZ] = "", tdate[BUFSIZ] = "", orig[BUFSIZ] = "", comm[BUFSIZ] = "";
699 :
700 0 : if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != MAL_SUCCEED)
701 : return msg;
702 0 : if ((msg = checkSQLContext(cntxt)) != MAL_SUCCEED)
703 : return msg;
704 :
705 0 : if (fits_open_file(&fptr, fname, READONLY, &status)) {
706 0 : msg = createException(MAL, "fits.attach", SQLSTATE(FI000) "Missing FITS file %s.\n", fname);
707 0 : return msg;
708 : }
709 :
710 0 : tr = m->session->tr;
711 0 : sch = mvc_bind_schema(m, "sys");
712 0 : sqlstore *store = tr->store;
713 :
714 0 : fits_fl = mvc_bind_table(m, sch, "fits_files");
715 0 : if (fits_fl == NULL)
716 0 : FITSinitCatalog(m);
717 :
718 0 : fits_fl = mvc_bind_table(m, sch, "fits_files");
719 0 : fits_tbl = mvc_bind_table(m, sch, "fits_tables");
720 0 : fits_col = mvc_bind_table(m, sch, "fits_columns");
721 0 : fits_tp = mvc_bind_table(m, sch, "fits_table_properties");
722 :
723 : /* check if the file is already attached */
724 0 : col = mvc_bind_column(m, fits_fl, "name");
725 0 : rid = store->table_api.column_find_row(m->session->tr, col, fname, NULL);
726 0 : if (!is_oid_nil(rid)) {
727 0 : fits_close_file(fptr, &status);
728 0 : throw(MAL, "fits.attach", SQLSTATE(FI000) "File %s already attached\n", fname);
729 : }
730 :
731 : /* add row in the fits_files catalog table */
732 0 : BUN offset;
733 0 : if (store->storage_api.claim_tab(m->session->tr, fits_fl, 1, &offset, NULL) != LOG_OK) {
734 0 : fits_close_file(fptr, &status);
735 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
736 : }
737 0 : if (!isNew(fits_fl) && sql_trans_add_dependency_change(m->session->tr, fits_fl->base.id, dml) != LOG_OK) {
738 0 : fits_close_file(fptr, &status);
739 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
740 : }
741 0 : col = mvc_bind_column(m, fits_fl, "id");
742 0 : fid = store->storage_api.count_col(tr, col, 0) + 1;
743 0 : store->storage_api.append_col(m->session->tr,
744 : mvc_bind_column(m, fits_fl, "id"), offset, NULL, &fid, 1, false, TYPE_int);
745 0 : store->storage_api.append_col(m->session->tr,
746 : mvc_bind_column(m, fits_fl, "name"), offset, NULL, &fname, 1, false, TYPE_str);
747 0 : col = mvc_bind_column(m, fits_tbl, "id");
748 0 : tid = store->storage_api.count_col(tr, col, 0) + 1;
749 :
750 0 : if ((s = strrchr(fname, DIR_SEP)) == NULL)
751 : s = fname;
752 : else
753 0 : s++;
754 0 : if (strcpy_len(bname, s, sizeof(bname)) >= sizeof(bname)) {
755 0 : fits_close_file(fptr, &status);
756 0 : throw(MAL, "fits.attach", SQLSTATE(FI000) "File name too long\n");
757 : }
758 0 : strcpy(bname, s);
759 0 : s = strrchr(bname, '.');
760 0 : if (s) *s = 0;
761 :
762 0 : fits_get_num_hdus(fptr, &hdunum, &status);
763 0 : for (i = 1; i <= hdunum; i++) {
764 0 : fits_movabs_hdu(fptr, i, &hdutype, &status);
765 0 : if (hdutype != ASCII_TBL && hdutype != BINARY_TBL)
766 0 : continue;
767 :
768 : /* SQL table name - the name of FITS extension */
769 0 : fits_read_key(fptr, TSTRING, "EXTNAME", tname, NULL, &status);
770 0 : if (status) {
771 0 : snprintf(tname, sizeof(tname), "%s_%d", bname, i);
772 0 : tname_low = toLower(tname);
773 0 : status = 0;
774 : }else { /* check table name for existence in the fits catalog */
775 0 : tname_low = toLower(tname);
776 0 : col = mvc_bind_column(m, fits_tbl, "name");
777 0 : rid = store->table_api.column_find_row(m->session->tr, col, tname_low, NULL);
778 : /* or as regular SQL table */
779 0 : tbl = mvc_bind_table(m, sch, tname_low);
780 0 : if (!is_oid_nil(rid) || tbl) {
781 0 : snprintf(tname, sizeof(tname), "%s_%d", bname, i);
782 0 : tname_low = toLower(tname);
783 : }
784 : }
785 :
786 0 : fits_read_key(fptr, TSTRING, "BITPIX", &bitpixnumber, NULL, &status);
787 0 : if (status) {
788 0 : status = 0;
789 : }
790 0 : fits_read_key(fptr, TSTRING, "DATE-HDU", tdate, NULL, &status);
791 0 : if (status) {
792 0 : status = 0;
793 : }
794 0 : fits_read_key(fptr, TSTRING, "XTENSION", xtensionname, NULL, &status);
795 0 : if (status) {
796 0 : status = 0;
797 : }
798 0 : fits_read_key(fptr, TSTRING, "STILVERS", stilversion, NULL, &status);
799 0 : if (status) {
800 0 : status = 0;
801 : }
802 0 : fits_read_key(fptr, TSTRING, "STILCLAS", stilclass, NULL, &status);
803 0 : if (status) {
804 0 : status = 0;
805 : }
806 0 : fits_read_key(fptr, TSTRING, "ORIGIN", orig, NULL, &status);
807 0 : if (status) {
808 0 : status = 0;
809 : }
810 0 : fits_read_key(fptr, TSTRING, "COMMENT", comm, NULL, &status);
811 0 : if (status) {
812 0 : status = 0;
813 : }
814 :
815 0 : fits_get_num_cols(fptr, &cnum, &status);
816 :
817 0 : BUN offset;
818 0 : if (store->storage_api.claim_tab(m->session->tr, fits_tbl, 1, &offset, NULL) != LOG_OK) {
819 0 : fits_close_file(fptr, &status);
820 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
821 : }
822 0 : if (!isNew(fits_tbl) && sql_trans_add_dependency_change(m->session->tr, fits_tbl->base.id, dml) != LOG_OK) {
823 0 : fits_close_file(fptr, &status);
824 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
825 : }
826 0 : store->storage_api.append_col(m->session->tr,
827 : mvc_bind_column(m, fits_tbl, "id"), offset, NULL, &tid, 1, false, TYPE_int);
828 0 : store->storage_api.append_col(m->session->tr,
829 : mvc_bind_column(m, fits_tbl, "name"), offset, NULL, &tname_low, 1, false, TYPE_str);
830 0 : store->storage_api.append_col(m->session->tr,
831 : mvc_bind_column(m, fits_tbl, "columns"), offset, NULL, &cnum, 1, false, TYPE_int);
832 0 : store->storage_api.append_col(m->session->tr,
833 : mvc_bind_column(m, fits_tbl, "file_id"), offset, NULL, &fid, 1, false, TYPE_int);
834 0 : store->storage_api.append_col(m->session->tr,
835 : mvc_bind_column(m, fits_tbl, "hdu"), offset, NULL, &i, 1, false, TYPE_int);
836 0 : char *vptr = tdate;
837 0 : store->storage_api.append_col(m->session->tr,
838 : mvc_bind_column(m, fits_tbl, "date"), offset, NULL, &vptr, 1, false, TYPE_str);
839 0 : vptr = orig;
840 0 : store->storage_api.append_col(m->session->tr,
841 : mvc_bind_column(m, fits_tbl, "origin"), offset, NULL, &vptr, 1, false, TYPE_str);
842 0 : vptr = comm;
843 0 : store->storage_api.append_col(m->session->tr,
844 : mvc_bind_column(m, fits_tbl, "comment"), offset, NULL, &vptr, 1, false, TYPE_str);
845 :
846 0 : if (store->storage_api.claim_tab(m->session->tr, fits_tp, 1, &offset, NULL) != LOG_OK) {
847 0 : fits_close_file(fptr, &status);
848 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
849 : }
850 0 : if (!isNew(fits_tp) && sql_trans_add_dependency_change(m->session->tr, fits_tp->base.id, dml) != LOG_OK) {
851 0 : fits_close_file(fptr, &status);
852 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
853 : }
854 0 : store->storage_api.append_col(m->session->tr,
855 : mvc_bind_column(m, fits_tp, "table_id"), offset, NULL, &tid, 1, false, TYPE_int);
856 0 : vptr = xtensionname;
857 0 : store->storage_api.append_col(m->session->tr,
858 : mvc_bind_column(m, fits_tp, "xtension"), offset, NULL, &vptr, 1, false, TYPE_str);
859 0 : store->storage_api.append_col(m->session->tr,
860 : mvc_bind_column(m, fits_tp, "bitpix"), offset, NULL, &bitpixnumber, 1, false, TYPE_int);
861 0 : vptr = stilversion;
862 0 : store->storage_api.append_col(m->session->tr,
863 : mvc_bind_column(m, fits_tp, "stilvers"), offset, NULL, &vptr, 1, false, TYPE_str);
864 0 : vptr = stilclass;
865 0 : store->storage_api.append_col(m->session->tr,
866 : mvc_bind_column(m, fits_tp, "stilclas"), offset, NULL, &vptr, 1, false, TYPE_str);
867 :
868 : /* read columns description */
869 0 : s = stmt;
870 0 : col = mvc_bind_column(m, fits_col, "id");
871 0 : cid = store->storage_api.count_col(tr, col, 0) + 1;
872 0 : for (j = 1; j <= cnum; j++, cid++) {
873 0 : fits_get_acolparms(fptr, j, cname, &tbcol, tunit, tform, &tscal, &tzero, tnull, tdisp, &status);
874 : /* escape the various strings to avoid SQL injection attacks */
875 0 : esc_cname = SQLescapeString(cname);
876 0 : if (!esc_cname) {
877 0 : fits_close_file(fptr, &status);
878 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
879 : }
880 0 : esc_tform = SQLescapeString(tform);
881 0 : if (!esc_tform) {
882 0 : GDKfree(esc_cname);
883 0 : fits_close_file(fptr, &status);
884 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
885 : }
886 0 : esc_tunit = SQLescapeString(tunit);
887 0 : if (!esc_tunit) {
888 0 : GDKfree(esc_tform);
889 0 : GDKfree(esc_cname);
890 0 : fits_close_file(fptr, &status);
891 0 : throw(MAL, "fits.attach", SQLSTATE(HY013) MAL_MALLOC_FAIL);
892 : }
893 0 : snprintf(stmt, sizeof(stmt), FITS_INS_COL, (int)cid, esc_cname, esc_tform, esc_tunit, j, (int)tid);
894 0 : GDKfree(esc_tunit);
895 0 : GDKfree(esc_tform);
896 0 : GDKfree(esc_cname);
897 0 : msg = SQLstatementIntern(cntxt, s, "fits.attach", TRUE, FALSE, NULL);
898 0 : if (msg != MAL_SUCCEED) {
899 0 : fits_close_file(fptr, &status);
900 0 : return msg;
901 : }
902 : }
903 0 : tid++;
904 : }
905 0 : fits_close_file(fptr, &status);
906 :
907 0 : return MAL_SUCCEED;
908 : }
909 :
910 0 : str FITSloadTable(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
911 : {
912 0 : mvc *m = NULL;
913 0 : sql_schema *sch;
914 0 : sql_table *fits_fl, *fits_tbl, *tbl = NULL;
915 0 : sql_column *col;
916 0 : sql_subtype tpe;
917 0 : fitsfile *fptr;
918 0 : str tname = *getArgReference_str(stk, pci, 1);
919 0 : str fname;
920 0 : str msg = MAL_SUCCEED;
921 0 : oid rid = oid_nil, frid = oid_nil;
922 0 : int status = 0, cnum = 0, *fid, *hdu, hdutype, j, anynull = 0, mtype;
923 0 : int *tpcode = NULL;
924 0 : long *rep = NULL, *wid = NULL, rows; /* type long used by fits library */
925 0 : char keywrd[80], **cname, nm[FLEN_VALUE];
926 0 : const void *nilptr;
927 0 : BUN offset;
928 0 : BAT *pos = NULL;
929 :
930 0 : if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != MAL_SUCCEED)
931 : return msg;
932 0 : if ((msg = checkSQLContext(cntxt)) != MAL_SUCCEED)
933 : return msg;
934 0 : sch = mvc_bind_schema(m, "sys");
935 :
936 0 : fits_tbl = mvc_bind_table(m, sch, "fits_tables");
937 0 : if (fits_tbl == NULL) {
938 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "FITS catalog is missing.\n");
939 0 : return msg;
940 : }
941 :
942 0 : tbl = mvc_bind_table(m, sch, tname);
943 0 : if (tbl) {
944 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "Table %s is already created.\n", tname);
945 0 : return msg;
946 : }
947 :
948 0 : col = mvc_bind_column(m, fits_tbl, "name");
949 0 : sqlstore *store = m->session->tr->store;
950 0 : rid = store->table_api.column_find_row(m->session->tr, col, tname, NULL);
951 0 : if (is_oid_nil(rid)) {
952 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "Table %s is unknown in FITS catalog. Attach first the containing file\n", tname);
953 0 : return msg;
954 : }
955 :
956 : /* Open FITS file and move to the table HDU */
957 0 : col = mvc_bind_column(m, fits_tbl, "file_id");
958 0 : fid = (int*)store->table_api.column_find_value(m->session->tr, col, rid);
959 :
960 0 : fits_fl = mvc_bind_table(m, sch, "fits_files");
961 0 : col = mvc_bind_column(m, fits_fl, "id");
962 0 : frid = store->table_api.column_find_row(m->session->tr, col, (void *)fid, NULL);
963 0 : GDKfree(fid);
964 0 : if (is_oid_nil(frid)) {
965 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "Table %s is unknown in FITS catalog. Attach first the containing file\n", tname);
966 0 : return msg;
967 : }
968 0 : col = mvc_bind_column(m, fits_fl, "name");
969 0 : fname = (char *)store->table_api.column_find_value(m->session->tr, col, frid);
970 0 : if (fits_open_file(&fptr, fname, READONLY, &status)) {
971 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "Missing FITS file %s.\n", fname);
972 0 : GDKfree(fname);
973 0 : return msg;
974 : }
975 0 : GDKfree(fname);
976 :
977 0 : col = mvc_bind_column(m, fits_tbl, "hdu");
978 0 : hdu = (int*)store->table_api.column_find_value(m->session->tr, col, rid);
979 0 : fits_movabs_hdu(fptr, *hdu, &hdutype, &status);
980 0 : if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
981 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "HDU %d is not a table.\n", *hdu);
982 0 : GDKfree(hdu);
983 0 : fits_close_file(fptr, &status);
984 0 : return msg;
985 : }
986 0 : GDKfree(hdu);
987 :
988 : /* create a SQL table to hold the FITS table */
989 : /* col = mvc_bind_column(m, fits_tbl, "columns");
990 : cnum = *(int*) store->table_api.column_find_value(m->session->tr, col, rid); */
991 0 : fits_get_num_cols(fptr, &cnum, &status);
992 0 : mvc_create_table(&tbl, m, sch, tname, tt_table, 0, SQL_PERSIST, 0, cnum, 0);
993 :
994 : // TODO: Check that the allocations succeeded
995 0 : tpcode = (int *)GDKzalloc(sizeof(int) * cnum);
996 0 : rep = (long *)GDKzalloc(sizeof(long) * cnum);
997 0 : wid = (long *)GDKzalloc(sizeof(long) * cnum);
998 0 : cname = (char **)GDKzalloc(sizeof(char *) * cnum);
999 :
1000 0 : for (j = 1; j <= cnum; j++) {
1001 0 : sql_column *col = NULL;
1002 : /* fits_get_acolparms(fptr, j, cname, &tbcol, tunit, tform, &tscal, &tzero, tnull, tdisp, &status); */
1003 0 : snprintf(keywrd, 80, "TTYPE%d", j);
1004 0 : fits_read_key(fptr, TSTRING, keywrd, nm, NULL, &status);
1005 0 : if (status) {
1006 0 : snprintf(nm, FLEN_VALUE, "column_%d", j);
1007 0 : status = 0;
1008 : }
1009 0 : cname[j - 1] = toLower(nm);
1010 0 : fits_get_coltype(fptr, j, &tpcode[j - 1], &rep[j - 1], &wid[j - 1], &status);
1011 0 : fits2subtype(&tpe, tpcode[j - 1], rep[j - 1], wid[j - 1]);
1012 :
1013 0 : TRC_DEBUG(FITS, "%d %ld %ld - M: %s\n", tpcode[j-1], rep[j-1], wid[j-1], tpe.type->base.name);
1014 :
1015 0 : mvc_create_column(&col, m, tbl, cname[j - 1], &tpe);
1016 : }
1017 :
1018 : /* data load */
1019 0 : fits_get_num_rows(fptr, &rows, &status);
1020 : /* Nothing more to do */
1021 0 : if (rows == 0) {
1022 0 : goto bailout;
1023 : }
1024 :
1025 0 : TRC_INFO(FITS, "Loading %ld rows in table %s\n", rows, tname);
1026 :
1027 0 : if (store->storage_api.claim_tab(m->session->tr, tbl, rows, &offset, &pos) != LOG_OK) {
1028 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1029 0 : goto bailout;
1030 : }
1031 0 : for (j = 1; j <= cnum; j++) {
1032 0 : BAT *tmp = NULL;
1033 0 : lng time0 = GDKusec();
1034 0 : mtype = fits2mtype(tpcode[j - 1], rep[j - 1]);
1035 0 : nilptr = ATOMnilptr(mtype);
1036 0 : col = mvc_bind_column(m, tbl, cname[j - 1]);
1037 :
1038 0 : tmp = COLnew(0, mtype, rows, TRANSIENT);
1039 0 : if (tmp == NULL){
1040 0 : msg = createException(MAL,"fits.load", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1041 0 : goto bailout;
1042 : }
1043 0 : if (mtype == TYPE_blob) {
1044 0 : long i;
1045 0 : unsigned long nbytes = rep[j - 1] * wid[j - 1];
1046 0 : blob **v = (blob **)GDKzalloc(sizeof(blob *) * rows);
1047 :
1048 0 : mtype = fits2mtype(tpcode[j - 1], 1);
1049 0 : nilptr = ATOMnilptr(mtype);
1050 :
1051 0 : if (v == NULL) {
1052 0 : BBPreclaim(tmp);
1053 0 : msg = createException(MAL,"fits.load", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1054 0 : goto bailout;
1055 : }
1056 :
1057 0 : for(i = 0; i < rows; i++) {
1058 0 : v[i] = (blob *)GDKmalloc(offsetof(blob, data) + nbytes);
1059 0 : if (v[i] == NULL) {
1060 0 : BBPreclaim(tmp);
1061 0 : long k = 0;
1062 0 : for (k = 0; k < i; k++) {
1063 0 : GDKfree(v[k]);
1064 : }
1065 0 : GDKfree(v);
1066 0 : msg = createException(MAL,"fits.load", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1067 0 : goto bailout;
1068 : }
1069 0 : fits_read_col(fptr, tpcode[j - 1], j, i + 1, 1, rep[j - 1], (void *)nilptr,
1070 0 : (void *)v[i]->data, &anynull, &status);
1071 0 : v[i]->nitems = nbytes;
1072 0 : if (BUNappend(tmp, v[i], false) != GDK_SUCCEED) {
1073 0 : BBPreclaim(tmp);
1074 0 : for (i = 0; i < rows; i++) {
1075 0 : GDKfree(v[i]);
1076 : }
1077 0 : GDKfree(v);
1078 0 : msg = createException(MAL,"fits.loadtable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1079 0 : goto bailout;
1080 : }
1081 : }
1082 :
1083 0 : for(i = 0; i < rows; i++) {
1084 0 : GDKfree(v[i]);
1085 : }
1086 0 : GDKfree(v);
1087 : }
1088 0 : else if (mtype == TYPE_str) {
1089 : /* char *v = GDKzalloc(wid[j-1]);*/
1090 : /* type long demanded by "rows", i.e., by fits library */
1091 0 : long bsize = 50, batch = bsize, k, i;
1092 0 : lng tm0, tloadtm = 0, tattachtm = 0;
1093 0 : char **v = (char **) GDKzalloc(sizeof(char *) * bsize);
1094 0 : for(i = 0; i < bsize; i++)
1095 0 : v[i] = GDKzalloc(wid[j-1]);
1096 0 : for(i = 0; i < rows; i += batch) {
1097 0 : batch = rows - i < bsize ? rows - i: bsize;
1098 0 : tm0 = GDKusec();
1099 0 : fits_read_col(fptr, tpcode[j - 1], j, 1 + i, 1, batch, (void *) nilptr, (void *)v, &anynull, &status);
1100 0 : tloadtm += GDKusec() - tm0;
1101 0 : tm0 = GDKusec();
1102 0 : for(k = 0; k < batch ; k++)
1103 0 : if (BUNappend(tmp, v[k], false) != GDK_SUCCEED) {
1104 0 : BBPreclaim(tmp);
1105 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1106 0 : goto bailout;
1107 : }
1108 0 : tattachtm += GDKusec() - tm0;
1109 : }
1110 0 : for(i = 0; i < bsize ; i++)
1111 0 : GDKfree(v[i]);
1112 0 : GDKfree(v);
1113 0 : TRC_INFO(FITS, "String column load "LLFMT" usec, BUNappend "LLFMT" usec\n", tloadtm, tattachtm);
1114 : }
1115 : else {
1116 0 : BATiter bi = bat_iterator_nolock(tmp);
1117 0 : fits_read_col(fptr, tpcode[j - 1], j, 1, 1, rows, (void *) nilptr, (void *)BUNtloc(bi, 0), &anynull, &status);
1118 0 : BATsetcount(tmp, rows);
1119 0 : tmp->tsorted = false;
1120 0 : tmp->trevsorted = false;
1121 0 : tmp->tkey = false;
1122 : }
1123 :
1124 0 : if (status) {
1125 0 : char buf[FLEN_ERRMSG + 1];
1126 0 : fits_read_errmsg(buf);
1127 0 : BBPreclaim(tmp);
1128 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(FI000) "Cannot load column %s of %s table: %s.\n", cname[j - 1], tname, buf);
1129 0 : break;
1130 : }
1131 :
1132 0 : TRC_INFO(FITS, "#Column %s loaded for "LLFMT" usec\t", cname[j-1], GDKusec() - time0);
1133 0 : if (store->storage_api.append_col(m->session->tr, col, offset, pos, tmp, BATcount(tmp), true, tmp->ttype) != LOG_OK) {
1134 0 : BBPreclaim(tmp);
1135 0 : msg = createException(MAL, "fits.loadtable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1136 0 : break;
1137 : }
1138 0 : TRC_INFO(FITS, "Total "LLFMT" usec\n", GDKusec() - time0);
1139 0 : BBPreclaim(tmp);
1140 : }
1141 :
1142 0 : bailout:
1143 0 : bat_destroy(pos);
1144 0 : GDKfree(tpcode);
1145 0 : GDKfree(rep);
1146 0 : GDKfree(wid);
1147 0 : GDKfree(cname);
1148 :
1149 0 : fits_close_file(fptr, &status);
1150 0 : return msg;
1151 : }
1152 :
1153 : #include "mel.h"
1154 : static mel_func fits_init_funcs[] = {
1155 : pattern("fits", "listdir", FITSdir, true, "Attach all FITS files in the directory", args(1,2, arg("",void),arg("dirname",str))),
1156 : pattern("fits", "listdirpattern", FITSdirpat, true, "Attach all FITS file in the directory, giving a pattern", args(1,3, arg("",void),arg("dirname",str),arg("pattern",str))),
1157 : command("fits", "fitstest", FITStest, false, "Returns the type of first extension in the FITS file filename", args(1,2, arg("",int),arg("filename",str))),
1158 : pattern("fits", "attach", FITSattach, true, "Open a FITS file and return catalog of the table HDUs", args(1,2, arg("",void),arg("fname",str))),
1159 : pattern("fits", "load", FITSloadTable, true, "Load a FITS table from an attached file", args(1,2, arg("",void),arg("tablename",str))),
1160 : pattern("fits", "export", FITSexportTable, false, "Export a table to a FITS file", args(1,2, arg("",void),arg("tablename",str))),
1161 : { .imp=NULL }
1162 : };
1163 : #include "mal_import.h"
1164 : #ifdef _MSC_VER
1165 : #undef read
1166 : #pragma section(".CRT$XCU",read)
1167 : #endif
1168 315 : LIB_STARTUP_FUNC(init_fits_mal)
1169 315 : { mal_module("fits", NULL, fits_init_funcs); }
|