LCOV - code coverage report
Current view: top level - sql/backends/monet5/vaults/monetdb - monetdb.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 121 145 83.4 %
Date: 2025-03-25 21:27:32 Functions: 6 6 100.0 %

          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             : #include "monetdb_config.h"
      14             : #include "rel_proto_loader.h"
      15             : #include "rel_exp.h"
      16             : 
      17             : #include "mal_instruction.h"
      18             : #include "mal_interpreter.h"
      19             : #include "mal_parser.h"
      20             : #include "mal_builder.h"
      21             : #include "mal_namespace.h"
      22             : #include "mal_exception.h"
      23             : #include "mal_linker.h"
      24             : #include "mal_backend.h"
      25             : #include "sql_types.h"
      26             : #include "rel_bin.h"
      27             : #include "sql_storage.h"
      28             : #include "mapi.h"
      29             : #include "msettings.h"
      30             : 
      31             : #include "rel_remote.h"
      32             : #include "rel_basetable.h"
      33             : #include <unistd.h>
      34             : 
      35             : static char *sql_template(allocator *sa, const char **parts);
      36             : 
      37             : typedef struct mdb_loader_t {
      38             :         char *uri;
      39             :         const char *sname;
      40             :         const char *tname;
      41             : } mdb_loader_t;
      42             : 
      43             : 
      44             : /*
      45             :  * returns an error string (static or via tmp sa_allocator allocated), NULL on success
      46             :  *
      47             :  * Extend the subfunc f with result columns, ie.
      48             :         f->res = typelist;
      49             :         f->coltypes = typelist;
      50             :         f->colnames = nameslist; use tname if passed, for the relation name
      51             :  * Fill the list res_exps, with one result expressions per resulting column.
      52             :  */
      53             : static str
      54           3 : monetdb_relation(mvc *sql, sql_subfunc *f, char *raw_uri, list *res_exps, char *aname)
      55             : {
      56           3 :         str ret; // intentionally uninitialized to provoke control flow warnings
      57             : 
      58           3 :         const char *uri_error = NULL;
      59           3 :         Mapi dbh = NULL;
      60           3 :         MapiHdl hdl = NULL;
      61             : 
      62             :         // Normalize uri
      63           3 :         msettings *mp = sa_msettings_create(sql->sa);
      64           3 :         if (!mp) {
      65           0 :                 ret = sa_message(sql->sa, "could not allocate msettings");
      66           0 :                 goto end;
      67             :         }
      68             : 
      69           6 :         if (
      70           3 :                 (uri_error = msettings_parse_url(mp, raw_uri))
      71           3 :                 || (uri_error = msettings_validate(mp))
      72             :         ) {
      73           0 :                 ret = sa_message(sql->sa, "uri '%s' invalid: %s\n", raw_uri, uri_error);
      74           0 :                 goto end;
      75             :         }
      76           3 :         const char *uri = sa_msettings_to_string(mp, sql->sa, strlen(raw_uri));
      77             : 
      78           3 :         const char *sname = msetting_string(mp, MP_TABLESCHEMA);   // not MP_SCHEMA, that's something else
      79           3 :         const char *tname = msetting_string(mp, MP_TABLE);
      80           3 :         assert(sname != NULL && tname != NULL); // msetting_string() never returns NULL, can return ""
      81           3 :         if (!sname[0] || !tname[0]) {
      82           0 :                 ret = sa_message(sql->sa, "monetdb_loader" "schema and/or table missing in '%s'\n", uri);
      83           0 :                 goto end;
      84             :         }
      85             : 
      86             :         /* set up mapi connection; user and password will possibly be overridden in the uri */
      87           3 :         dbh = mapi_mapiuri(uri, "monetdb", "monetdb", "sql");
      88           3 :         if (dbh == NULL) {
      89           0 :                 ret = MAL_MALLOC_FAIL;
      90           0 :                 goto end;
      91             :         }
      92           3 :         if (mapi_reconnect(dbh) < 0) {
      93           0 :                 ret = sa_strdup(sql->sa, mapi_error_str(dbh));
      94           0 :                 goto end;
      95             :         }
      96           3 :         mapi_cache_limit(dbh, 100);
      97             : 
      98             :         /* construct the query with proper quoting */
      99           3 :         char *query;
     100           3 :         query = sql_template(sql->sa, (const char*[]) {
     101             :                 "select c.name, c.type, c.type_digits, c.type_scale from sys.schemas s, sys._tables t, sys._columns c where s.name = R'",
     102             :                 sname,
     103             :                 "' and s.id = t.schema_id and t.name = R'",
     104             :                 tname,
     105             :                 "' and t.id = c.table_id order by c.number;",
     106             :                 NULL
     107             :         });
     108             : 
     109           3 :         if ((hdl = mapi_query(dbh, query)) == NULL || mapi_error(dbh)) {
     110           0 :                 const char *err = mapi_error_str(dbh);
     111           0 :                 if (err)
     112           0 :                         ret = sa_strdup(sql->sa, err);
     113             :                 else
     114           0 :                         ret = sa_message(sql->sa, "Failed to access remote server");
     115           0 :                 goto end;
     116             :         }
     117             : 
     118           3 :         if (mapi_get_row_count(hdl) == 0) { /* non existing table */
     119           1 :                 ret = sa_message(sql->sa, "Table %s.%s is missing on remote server", sname, tname);
     120           1 :                 goto end;
     121             :         }
     122             : 
     123           2 :         if (!aname)
     124           0 :                 aname = sa_strdup(sql->sa, tname);
     125             : 
     126           2 :         f->tname = sa_strdup(sql->sa, aname);
     127             : 
     128           2 :         list *typelist = sa_list(sql->sa);
     129           2 :         list *nameslist = sa_list(sql->sa);
     130          19 :         while (mapi_fetch_row(hdl)) {
     131          17 :                 char *nme = sa_strdup(sql->sa, mapi_fetch_field(hdl, 0));
     132          17 :                 char *tpe = mapi_fetch_field(hdl, 1);
     133          17 :                 char *dig = mapi_fetch_field(hdl, 2);
     134          17 :                 char *scl = mapi_fetch_field(hdl, 3);
     135             : 
     136          17 :                 sql_subtype *t = NULL;
     137          34 :                 if (scl && scl[0]) {
     138          17 :                         int d = (int)strtol(dig, NULL, 10);
     139          17 :                         int s = (int)strtol(scl, NULL, 10);
     140          17 :                         t = sql_bind_subtype(sql->sa, tpe, d, s);
     141           0 :                 } else if (dig && dig[0]) {
     142           0 :                         int d = (int)strtol(dig, NULL, 10);
     143           0 :                         t = sql_bind_subtype(sql->sa, tpe, d, 0);
     144             :                 } else
     145           0 :                         t = sql_bind_subtype(sql->sa, tpe, 0, 0);
     146          17 :                 if (!t) {
     147           0 :                         ret = sa_message(sql->sa, "monetdb_loader" "type %s not found\n", tpe);
     148           0 :                         goto end;
     149             :                 }
     150          17 :                 sql_exp *ne = exp_column(sql->sa, a_create(sql->sa, f->tname), nme, t, CARD_MULTI, 1, 0, 0);
     151          17 :                 set_basecol(ne);
     152          17 :                 ne->alias.label = -(sql->nid++);
     153          17 :                 list_append(res_exps, ne);
     154          17 :                 list_append(typelist, t);
     155          17 :                 list_append(nameslist, nme);
     156             :         }
     157             : 
     158           2 :         f->res = typelist;
     159           2 :         f->coltypes = typelist;
     160           2 :         f->colnames = nameslist;
     161             : 
     162           2 :         mdb_loader_t *r = (mdb_loader_t *)sa_alloc(sql->sa, sizeof(mdb_loader_t));
     163           2 :         r->sname = sname;
     164           2 :         r->tname = tname;
     165           2 :         r->uri = sa_strdup(sql->sa, uri);
     166           2 :         f->sname = (char*)r; /* pass mdb_loader */
     167           2 :         ret = NULL;
     168             : 
     169           3 : end:
     170           3 :         if (hdl)
     171           3 :                 mapi_close_handle(hdl);
     172           3 :         if (dbh)
     173           3 :                 mapi_destroy(dbh);
     174             :         // do not destroy mp because r->sname and r->tname point inside it
     175           3 :         return ret;
     176             : }
     177             : 
     178             : /* Return the concatenation of the arguments, with quotes doubled
     179             :  * ONLY IN THE ODD positions.
     180             :  * The list of arguments must be terminated with a NULL.
     181             :  *
     182             :  * For example, { "SELECT R'",        "it's",        "' AS bla",        NULL }
     183             :  * becomes "SELECT R'it''s' AS bla"
     184             :  */
     185             : static char *
     186           3 : sql_template(allocator *sa, const char **parts)
     187             : {
     188           3 :         int nparts = 0;
     189          18 :         while (parts[nparts] != NULL)
     190          15 :                 nparts++;
     191             : 
     192             :         size_t max_length = 0;
     193          18 :         for (int i = 0; i < nparts; i++) {
     194          15 :                 size_t length = strlen(parts[i]);
     195          15 :                 if (i % 2 == 1)
     196           6 :                         length += length;
     197          15 :                 max_length += length;
     198             :         }
     199           3 :         char *result = sa_alloc(sa, max_length + 1);
     200             : 
     201             :         char *w = result;
     202          18 :         for (int i = 0; i < nparts; i++) {
     203         644 :                 for (const char *r = parts[i]; *r; r++) {
     204         629 :                         *w++ = *r;
     205         629 :                         if (*r == '\'' && i % 2 == 1)
     206           0 :                                 *w++ = *r;  // double that quote
     207             :                 }
     208             :         }
     209           3 :         *w = 0;
     210             : 
     211           3 :         assert(w <= result + max_length);
     212           3 :         return result;
     213             : }
     214             : 
     215             : 
     216             : static void *
     217           2 : monetdb_load(void *BE, sql_subfunc *f, char *uri, sql_exp *topn)
     218             : {
     219           2 :         (void)uri; // assumed to be equivalent to mdb_loader_t->uri, though maybe unnormalized.
     220           2 :         (void)topn;
     221             : 
     222           2 :         backend *be = (backend*)BE;
     223           2 :         mvc *sql = be->mvc;
     224           2 :         mdb_loader_t *r = (mdb_loader_t*)f->sname;
     225           2 :         char name[16], *nme;
     226             : 
     227           2 :         nme = number2name(name, sizeof(name), ++be->remote);
     228             : 
     229             :         /* create proper relation for remote side */
     230             :         /* table ( project ( REMOTE ( table ) [ cols ] ) [ cols ] REMOTE Prop ) [ cols ] */
     231             : 
     232           2 :         sql_table *t;
     233           2 :         if (mvc_create_table( &t, be->mvc, be->mvc->session->tr->tmp/* misuse tmp schema */, r->tname /*gettable name*/, tt_remote, false, SQL_DECLARED_TABLE, 0, 0, false) != LOG_OK)
     234             :                 /* alloc error */
     235             :                 return NULL;
     236           2 :         t->query = r->uri; /* set uri */
     237           2 :         node *n, *nn = f->colnames->h, *tn = f->coltypes->h;
     238          19 :         for (n = f->res->h; n; n = n->next, nn = nn->next, tn = tn->next) {
     239          17 :                 const char *name = nn->data;
     240          17 :                 sql_subtype *tp = tn->data;
     241          17 :                 sql_column *c = NULL;
     242             : 
     243          17 :                 if (!tp || mvc_create_column(&c, be->mvc, t, name, tp) != LOG_OK) {
     244           0 :                         return NULL;
     245             :                 }
     246             :         }
     247             : 
     248           2 :         sql_rel *rel = NULL;
     249           2 :         rel = rel_basetable(sql, t, a_create(sql->sa, f->tname));
     250           2 :         rel_base_use_all(sql, rel);
     251             : 
     252           2 :         rel = rel_project(sql->sa, rel, rel_projections(sql, rel, NULL, 0, 0));
     253           2 :         prop *p = rel->p = prop_create(sql->sa, PROP_REMOTE, rel->p);
     254           2 :         tid_uri *tu = SA_NEW(sql->sa, tid_uri);
     255           2 :         tu->id = t->base.id;
     256           2 :         tu->uri = mapiuri_uri(r->uri, sql->sa);
     257           2 :         p->id = tu->id;
     258           2 :         p->value.pval = append(sa_list(sql->sa), tu);
     259             : 
     260           2 :         stmt *s = stmt_func(be, NULL, sa_strdup(sql->sa, nme), rel, 0);
     261           2 :         return s;
     262             : }
     263             : 
     264             : static str
     265         346 : MONETDBprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     266             : {
     267         346 :         (void)cntxt; (void)mb; (void)stk; (void)pci;
     268             : 
     269         346 :         pl_register("monetdb", &monetdb_relation, &monetdb_load);
     270         346 :         pl_register("monetdbs", &monetdb_relation, &monetdb_load);
     271         346 :         pl_register("mapi", &monetdb_relation, &monetdb_load);
     272         346 :         return MAL_SUCCEED;
     273             : }
     274             : 
     275             : static str
     276         345 : MONETDBepilogue(void *ret)
     277             : {
     278         345 :         pl_unregister("monetdb");
     279         345 :         pl_unregister("monetdbs");
     280         345 :         pl_unregister("mapi");
     281         345 :         (void)ret;
     282         345 :         return MAL_SUCCEED;
     283             : }
     284             : 
     285             : #include "sql_scenario.h"
     286             : #include "mel.h"
     287             : 
     288             : static mel_func monetdb_init_funcs[] = {
     289             :         pattern("monetdb", "prelude", MONETDBprelude, false, "", noargs),
     290             :         command("monetdb", "epilogue", MONETDBepilogue, false, "", noargs),
     291             : { .imp=NULL }
     292             : };
     293             : 
     294             : #include "mal_import.h"
     295             : #ifdef _MSC_VER
     296             : #undef read
     297             : #pragma section(".CRT$XCU",read)
     298             : #endif
     299         346 : LIB_STARTUP_FUNC(init_monetdb_mal)
     300         346 : { mal_module("monetdb", NULL, monetdb_init_funcs); }

Generated by: LCOV version 1.14