Changeset: 25c5c509d9b6 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=25c5c509d9b6
Modified Files:
        clients/Tests/MAL-signatures.stable.out
        clients/Tests/MAL-signatures.stable.out.int128
        clients/Tests/exports.stable.out
        clients/mapilib/mapi.c
        gdk/gdk_batop.c
        gdk/gdk_logger.c
        monetdb5/modules/kernel/algebra.c
        monetdb5/modules/kernel/algebra.h
        monetdb5/modules/kernel/algebra.mal
        sql/backends/monet5/rel_bin.c
        sql/backends/monet5/sql_statement.c
        sql/include/sql_mem.h
        sql/rel.txt
        sql/server/rel_exp.c
        sql/server/rel_optimizer.c
        sql/server/rel_rel.c
        sql/server/rel_schema.c
        sql/server/rel_unnest.c
        sql/server/rel_updates.c
        sql/server/rel_updates.h
        sql/test/Triggers/Tests/All
        sql/test/Triggers/trigger_action.sql
        sql/test/mergetables/Tests/mergequery.stable.out
        sql/test/miscellaneous/Tests/alter_table_add_column.sql
        sql/test/miscellaneous/Tests/alter_table_add_column.stable.out
        sql/test/remote/Tests/partition_elim.stable.out
        sql/test/sys-schema/Tests/check_ForeignKey_referential_integrity.sql
        
sql/test/sys-schema/Tests/check_ForeignKey_referential_integrity.stable.out
        testing/exportutils.py
Branch: default
Log Message:

Merge with Nov2019


diffs (truncated from 1865 to 300 lines):

diff --git a/clients/Tests/MAL-signatures.stable.out 
b/clients/Tests/MAL-signatures.stable.out
--- a/clients/Tests/MAL-signatures.stable.out
+++ b/clients/Tests/MAL-signatures.stable.out
@@ -576,7 +576,9 @@ stdout of test 'MAL-signatures` in direc
 [ "algebra",   "rangejoin",    "command algebra.rangejoin(l:bat[:any_1], 
r1:bat[:any_1], r2:bat[:any_1], sl:bat[:oid], sr:bat[:oid], li:bit, hi:bit, 
estimate:lng) (X_0:bat[:oid], X_1:bat[:oid]) ",   "ALGrangejoin;",        
"Range join: values in l and r1/r2 match if r1 <[=] l <[=] r2"  ]
 [ "algebra",   "reuse",        "command 
algebra.reuse(b:bat[:any_1]):bat[:any_1] ",    "ALGreuse;",    "Reuse a 
temporary BAT if you can. Otherwise,\n\tallocate enough storage to accept 
result of an\n \toperation (not involving the heap)" ]
 [ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
low:any_1, high:any_1, li:bit, hi:bit, anti:bit):bat[:oid] ",    "ALGselect1;", 
 "Select all head values for which the tail value is in range.\n\tInput is a 
dense-headed BAT, output is a dense-headed BAT with in\n\tthe tail the head 
value of the input BAT for which the tail value\n\tis between the values low 
and high (inclusive if li respectively\n\thi is set).  The output BAT is sorted 
on the tail value.  If low\n\tor high is nil, the boundary is not considered 
(effectively - and\n\t+ infinity).  If anti is set, the result is the 
complement.  Nil\n\tvalues in the tail are never matched, unless low=nil, 
high=nil,\n\tli=1, hi=1, anti=0.  All non-nil values are returned if 
low=nil,\n\thigh=nil, and li, hi are not both 1, or anti=1.\n\tNote that the 
output is suitable as second input for the other\n\tversion of this function."  
    ]
+[ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
low:any_1, high:any_1, li:bit, hi:bit, anti:bit, unknown:bit):bat[:oid] ",      
 "ALGselect1nil;",       "With unknow set, each nil != nil"      ]
 [ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
s:bat[:oid], low:any_1, high:any_1, li:bit, hi:bit, anti:bit):bat[:oid] ",      
 "ALGselect2;",  "Select all head values of the first input BAT for which the 
tail value\n\tis in range and for which the head value occurs in the tail of 
the\n\tsecond input BAT.\n\tThe first input is a dense-headed BAT, the second 
input is a\n\tdense-headed BAT with sorted tail, output is a dense-headed 
BAT\n\twith in the tail the head value of the input BAT for which the\n\ttail 
value is between the values low and high (inclusive if li\n\trespectively hi is 
set).  The output BAT is sorted on the tail\n\tvalue.  If low or high is nil, 
the boundary is not considered\n\t(effectively - and + infinity).  If anti is 
set, the result is the\n\tcomplement.  Nil values in the tail are never 
matched, unless\n\tlow=nil, high=nil, li=1, hi=1, anti=0.  All non-nil values 
are\n\treturned if low=nil, high=nil, and li, hi are not both 1, or 
anti=1.\n\tNote that th
 e output is suitable as second input for this\n\tfunction."    ]
+[ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
s:bat[:oid], low:any_1, high:any_1, li:bit, hi:bit, anti:bit, 
unknown:bit):bat[:oid] ",  "ALGselect2nil;",       "With unknow set, each nil 
!= nil"      ]
 [ "algebra",   "selectNotNil", "command 
algebra.selectNotNil(b:bat[:any_2]):bat[:any_2] ",     "ALGselectNotNil;",     
"Select all not-nil values"     ]
 [ "algebra",   "semijoin",     "command algebra.semijoin(l:bat[:any_1], 
r:bat[:any_1], sl:bat[:oid], sr:bat[:oid], nil_matches:bit, estimate:lng) 
(X_0:bat[:oid], X_1:bat[:oid]) ",    "ALGsemijoin;", "Semi join with candidate 
lists"        ]
 [ "algebra",   "slice",        "command algebra.slice(b:bat[:any_1], x:lng, 
y:lng):bat[:any_1] ",      "ALGslice;",    "Return the slice with the BUNs at 
position x till y."  ]
diff --git a/clients/Tests/MAL-signatures.stable.out.int128 
b/clients/Tests/MAL-signatures.stable.out.int128
--- a/clients/Tests/MAL-signatures.stable.out.int128
+++ b/clients/Tests/MAL-signatures.stable.out.int128
@@ -680,7 +680,9 @@ stdout of test 'MAL-signatures` in direc
 [ "algebra",   "rangejoin",    "command algebra.rangejoin(l:bat[:any_1], 
r1:bat[:any_1], r2:bat[:any_1], sl:bat[:oid], sr:bat[:oid], li:bit, hi:bit, 
estimate:lng) (X_0:bat[:oid], X_1:bat[:oid]) ",   "ALGrangejoin;",        
"Range join: values in l and r1/r2 match if r1 <[=] l <[=] r2"  ]
 [ "algebra",   "reuse",        "command 
algebra.reuse(b:bat[:any_1]):bat[:any_1] ",    "ALGreuse;",    "Reuse a 
temporary BAT if you can. Otherwise,\n\tallocate enough storage to accept 
result of an\n \toperation (not involving the heap)" ]
 [ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
low:any_1, high:any_1, li:bit, hi:bit, anti:bit):bat[:oid] ",    "ALGselect1;", 
 "Select all head values for which the tail value is in range.\n\tInput is a 
dense-headed BAT, output is a dense-headed BAT with in\n\tthe tail the head 
value of the input BAT for which the tail value\n\tis between the values low 
and high (inclusive if li respectively\n\thi is set).  The output BAT is sorted 
on the tail value.  If low\n\tor high is nil, the boundary is not considered 
(effectively - and\n\t+ infinity).  If anti is set, the result is the 
complement.  Nil\n\tvalues in the tail are never matched, unless low=nil, 
high=nil,\n\tli=1, hi=1, anti=0.  All non-nil values are returned if 
low=nil,\n\thigh=nil, and li, hi are not both 1, or anti=1.\n\tNote that the 
output is suitable as second input for the other\n\tversion of this function."  
    ]
+[ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
low:any_1, high:any_1, li:bit, hi:bit, anti:bit, unknown:bit):bat[:oid] ",      
 "ALGselect1nil;",       "With unknow set, each nil != nil"      ]
 [ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
s:bat[:oid], low:any_1, high:any_1, li:bit, hi:bit, anti:bit):bat[:oid] ",      
 "ALGselect2;",  "Select all head values of the first input BAT for which the 
tail value\n\tis in range and for which the head value occurs in the tail of 
the\n\tsecond input BAT.\n\tThe first input is a dense-headed BAT, the second 
input is a\n\tdense-headed BAT with sorted tail, output is a dense-headed 
BAT\n\twith in the tail the head value of the input BAT for which the\n\ttail 
value is between the values low and high (inclusive if li\n\trespectively hi is 
set).  The output BAT is sorted on the tail\n\tvalue.  If low or high is nil, 
the boundary is not considered\n\t(effectively - and + infinity).  If anti is 
set, the result is the\n\tcomplement.  Nil values in the tail are never 
matched, unless\n\tlow=nil, high=nil, li=1, hi=1, anti=0.  All non-nil values 
are\n\treturned if low=nil, high=nil, and li, hi are not both 1, or 
anti=1.\n\tNote that th
 e output is suitable as second input for this\n\tfunction."    ]
+[ "algebra",   "select",       "command algebra.select(b:bat[:any_1], 
s:bat[:oid], low:any_1, high:any_1, li:bit, hi:bit, anti:bit, 
unknown:bit):bat[:oid] ",  "ALGselect2nil;",       "With unknow set, each nil 
!= nil"      ]
 [ "algebra",   "selectNotNil", "command 
algebra.selectNotNil(b:bat[:any_2]):bat[:any_2] ",     "ALGselectNotNil;",     
"Select all not-nil values"     ]
 [ "algebra",   "semijoin",     "command algebra.semijoin(l:bat[:any_1], 
r:bat[:any_1], sl:bat[:oid], sr:bat[:oid], nil_matches:bit, estimate:lng) 
(X_0:bat[:oid], X_1:bat[:oid]) ",    "ALGsemijoin;", "Semi join with candidate 
lists"        ]
 [ "algebra",   "slice",        "command algebra.slice(b:bat[:any_1], x:lng, 
y:lng):bat[:any_1] ",      "ALGslice;",    "Return the slice with the BUNs at 
position x till y."  ]
diff --git a/clients/Tests/exports.stable.out b/clients/Tests/exports.stable.out
--- a/clients/Tests/exports.stable.out
+++ b/clients/Tests/exports.stable.out
@@ -740,7 +740,9 @@ str ALGprojecttail(Client cntxt, MalBlkP
 str ALGrangejoin(bat *r1, bat *r2, const bat *lid, const bat *rlid, const bat 
*rhid, const bat *slid, const bat *srid, const bit *li, const bit *hi, const 
lng *estimate);
 str ALGreuse(bat *ret, const bat *bid);
 str ALGselect1(bat *result, const bat *bid, const void *low, const void *high, 
const bit *li, const bit *hi, const bit *anti);
+str ALGselect1nil(bat *result, const bat *bid, const void *low, const void 
*high, const bit *li, const bit *hi, const bit *anti, const bit *unknown);
 str ALGselect2(bat *result, const bat *bid, const bat *sid, const void *low, 
const void *high, const bit *li, const bit *hi, const bit *anti);
+str ALGselect2nil(bat *result, const bat *bid, const bat *sid, const void 
*low, const void *high, const bit *li, const bit *hi, const bit *anti, const 
bit *unknown);
 str ALGselectNotNil(bat *result, const bat *bid);
 str ALGsemijoin(bat *r1, bat *r2, const bat *l, const bat *r, const bat *sl, 
const bat *sr, const bit *nil_matches, const lng *estimate);
 str ALGslice(bat *ret, const bat *bid, const lng *start, const lng *end);
diff --git a/clients/mapilib/mapi.c b/clients/mapilib/mapi.c
--- a/clients/mapilib/mapi.c
+++ b/clients/mapilib/mapi.c
@@ -890,10 +890,6 @@ struct MapiStatement {
 #define debugprint(fmt,arg)    ((void) 0)
 #endif
 
-#ifdef HAVE_EMBEDDED
-#define printf(...)    ((void)0)
-#endif
-
 /*
  * All external calls to the library should pass the mapi-check
  * routine. It assures a working connection and proper reset of
diff --git a/gdk/gdk_batop.c b/gdk/gdk_batop.c
--- a/gdk/gdk_batop.c
+++ b/gdk/gdk_batop.c
@@ -1047,12 +1047,6 @@ BATkeyed(BAT *b)
                return false;
        }
 
-       /* In order that multiple threads don't scan the same BAT at
-        * the same time (happens a lot with mitosis/mergetable), we
-        * use a lock.  We reuse the hash lock for this, not because
-        * this scanning interferes with hashes, but because it's
-        * there, and not so likely to be used at the same time. */
-       MT_lock_set(&b->batIdxLock);
        b->batDirtydesc = true;
        if (!b->tkey && b->tnokey[0] == 0 && b->tnokey[1] == 0) {
                if (b->tsorted || b->trevsorted) {
@@ -1160,7 +1154,6 @@ BATkeyed(BAT *b)
                }
        }
   doreturn:
-       MT_lock_unset(&b->batIdxLock);
        return b->tkey;
 }
 
diff --git a/gdk/gdk_logger.c b/gdk/gdk_logger.c
--- a/gdk/gdk_logger.c
+++ b/gdk/gdk_logger.c
@@ -81,10 +81,6 @@
 #define LOG_USE_ID     15
 #define LOG_CLEAR_ID   16
 
-#ifdef HAVE_EMBEDDED
-#define printf(fmt,...) ((void) 0)
-#endif
-
 #ifdef NATIVE_WIN32
 #define getfilepos _ftelli64
 #else
diff --git a/monetdb5/modules/kernel/algebra.c 
b/monetdb5/modules/kernel/algebra.c
--- a/monetdb5/modules/kernel/algebra.c
+++ b/monetdb5/modules/kernel/algebra.c
@@ -281,12 +281,58 @@ ALGselect2(bat *result, const bat *bid, 
 }
 
 str
+ALGselect2nil(bat *result, const bat *bid, const bat *sid, const void *low, 
const void *high, const bit *li, const bit *hi, const bit *anti, const bit 
*unknown)
+{
+       BAT *b, *s = NULL, *bn;
+       const void *nilptr;
+
+       if (!*unknown)
+               return ALGselect2(result, bid, sid, low, high, li, hi, anti);
+
+       if ((*li != 0 && *li != 1) ||
+               (*hi != 0 && *hi != 1) ||
+               (*anti != 0 && *anti != 1)) {
+               throw(MAL, "algebra.select", ILLEGAL_ARGUMENT);
+       }
+       if ((b = BATdescriptor(*bid)) == NULL) {
+               throw(MAL, "algebra.select", SQLSTATE(HY002) 
RUNTIME_OBJECT_MISSING);
+       }
+       if (sid && !is_bat_nil(*sid) && (s = BATdescriptor(*sid)) == NULL) {
+               BBPunfix(b->batCacheid);
+               throw(MAL, "algebra.select", SQLSTATE(HY002) 
RUNTIME_OBJECT_MISSING);
+       }
+       derefStr(b, low);
+       derefStr(b, high);
+       /* here we don't need open ended parts with nil */
+       nilptr = ATOMnilptr(b->ttype);
+       if (*li == 1 && ATOMcmp(b->ttype, low, nilptr) == 0) 
+               low = high; 
+       else if (*hi == 1 && ATOMcmp(b->ttype, high, nilptr) == 0)
+               high = low;
+       bn = BATselect(b, s, low, high, *li, *hi, *anti);
+       BBPunfix(b->batCacheid);
+       if (s)
+               BBPunfix(s->batCacheid);
+       if (bn == NULL)
+               throw(MAL, "algebra.select", GDK_EXCEPTION);
+       *result = bn->batCacheid;
+       BBPkeepref(bn->batCacheid);
+       return MAL_SUCCEED;
+}
+
+str
 ALGselect1(bat *result, const bat *bid, const void *low, const void *high, 
const bit *li, const bit *hi, const bit *anti)
 {
        return ALGselect2(result, bid, NULL, low, high, li, hi, anti);
 }
 
 str
+ALGselect1nil(bat *result, const bat *bid, const void *low, const void *high, 
const bit *li, const bit *hi, const bit *anti, const bit *unknown)
+{
+       return ALGselect2nil(result, bid, NULL, low, high, li, hi, anti, 
unknown);
+}
+
+str
 ALGthetaselect2(bat *result, const bat *bid, const bat *sid, const void *val, 
const char **op)
 {
        BAT *b, *s = NULL, *bn;
diff --git a/monetdb5/modules/kernel/algebra.h 
b/monetdb5/modules/kernel/algebra.h
--- a/monetdb5/modules/kernel/algebra.h
+++ b/monetdb5/modules/kernel/algebra.h
@@ -26,6 +26,8 @@ mal_export str ALGgroupby(bat *res, cons
 mal_export str ALGcard(lng *result, const bat *bid);
 mal_export str ALGselect1(bat *result, const bat *bid, const void *low, const 
void *high, const bit *li, const bit *hi, const bit *anti);
 mal_export str ALGselect2(bat *result, const bat *bid, const bat *sid, const 
void *low, const void *high, const bit *li, const bit *hi, const bit *anti);
+mal_export str ALGselect1nil(bat *result, const bat *bid, const void *low, 
const void *high, const bit *li, const bit *hi, const bit *anti, const bit 
*unknown);
+mal_export str ALGselect2nil(bat *result, const bat *bid, const bat *sid, 
const void *low, const void *high, const bit *li, const bit *hi, const bit 
*anti, const bit *unknown);
 mal_export str ALGthetaselect1(bat *result, const bat *bid, const void *val, 
const char **op);
 mal_export str ALGthetaselect2(bat *result, const bat *bid, const bat *sid, 
const void *val, const char **op);
 
diff --git a/monetdb5/modules/kernel/algebra.mal 
b/monetdb5/modules/kernel/algebra.mal
--- a/monetdb5/modules/kernel/algebra.mal
+++ b/monetdb5/modules/kernel/algebra.mal
@@ -70,6 +70,14 @@ comment "Select all head values of the f
        Note that the output is suitable as second input for this
        function.";
 
+command select(b:bat[:any_1], low:any_1, high:any_1, li:bit, hi:bit, anti:bit, 
unknown:bit) :bat[:oid]
+address ALGselect1nil
+comment "With unknow set, each nil != nil";
+
+command select(b:bat[:any_1], s:bat[:oid], low:any_1, high:any_1, li:bit, 
hi:bit, anti:bit, unknown:bit) :bat[:oid]
+address ALGselect2nil
+comment "With unknow set, each nil != nil";
+
 command thetaselect(b:bat[:any_1], val:any_1, op:str) :bat[:oid]
 address ALGthetaselect1
 comment "Select all head values for which the tail value obeys the relation
diff --git a/sql/backends/monet5/rel_bin.c b/sql/backends/monet5/rel_bin.c
--- a/sql/backends/monet5/rel_bin.c
+++ b/sql/backends/monet5/rel_bin.c
@@ -3621,20 +3621,8 @@ sql_insert_check_null(backend *be, sql_t
 static stmt ** 
 table_update_stmts(mvc *sql, sql_table *t, int *Len)
 {
-       stmt **updates;
-       int i, len = list_length(t->columns.set);
-       node *m;
-
-       *Len = len;
-       updates = SA_NEW_ARRAY(sql->sa, stmt *, len);
-       for (m = t->columns.set->h, i = 0; m; m = m->next, i++) {
-               sql_column *c = m->data;
-
-               /* update the column number, for correct array access */
-               c->colnr = i;
-               updates[i] = NULL;
-       }
-       return updates;
+       *Len = list_length(t->columns.set);
+       return SA_ZNEW_ARRAY(sql->sa, stmt *, *Len);
 }
 
 static stmt *
@@ -4499,7 +4487,7 @@ sql_stack_add_updated(mvc *sql, const ch
 
                        append(exps, oe);
                        append(exps, ne);
-               } else { /* later select correct updated rows only ? */
+               } else {
                        sql_exp *oe = exp_column(sql->sa, on, c->base.name, 
&c->type, CARD_MULTI, c->null, 0);
                        sql_exp *ne = exp_column(sql->sa, nn, c->base.name, 
&c->type, CARD_MULTI, c->null, 0);
 
diff --git a/sql/backends/monet5/sql_statement.c 
b/sql/backends/monet5/sql_statement.c
--- a/sql/backends/monet5/sql_statement.c
+++ b/sql/backends/monet5/sql_statement.c
@@ -1589,7 +1589,7 @@ select2_join2(backend *be, stmt *op1, st
        if (op1->nr < 0 && (sub && sub->nr < 0))
                return NULL;
        l = op1->nr;
-       if (((cmp & CMP_BETWEEN) || op2->nrcols > 0 || op3->nrcols > 0) && 
(type == st_uselect2)) {
+       if (((cmp & CMP_BETWEEN && cmp & CMP_SYMMETRIC) || op2->nrcols > 0 || 
op3->nrcols > 0) && (type == st_uselect2)) {
                int k;
 
                if (op2->nr < 0 || op3->nr < 0)
@@ -1691,8 +1691,8 @@ select2_join2(backend *be, stmt *op1, st
                        q = pushNil(mb, q, TYPE_lng); /* estimate */
                if (type == st_uselect2) {
                        q = pushBit(mb, q, anti);
-                       if (q == NULL)
-                               return NULL;
+                       if (cmp & CMP_BETWEEN)
+                               q = pushBit(mb, q, TRUE); /* all nil's are != */
                }
                if (q == NULL)
                        return NULL;
diff --git a/sql/include/sql_mem.h b/sql/include/sql_mem.h
--- a/sql/include/sql_mem.h
+++ b/sql/include/sql_mem.h
@@ -74,6 +74,7 @@ extern size_t sa_size( sql_allocator *sa
 #define SA_NEW( sa, type ) ((type*)sa_alloc( sa, sizeof(type)) )
 #define SA_ZNEW( sa, type ) ((type*)sa_zalloc( sa, sizeof(type)) )
 #define SA_NEW_ARRAY( sa, type, size ) (type*)sa_alloc( sa, 
((size)*sizeof(type)))
+#define SA_ZNEW_ARRAY( sa, type, size ) (type*)sa_zalloc( sa, 
((size)*sizeof(type)))
 #define SA_RENEW_ARRAY( sa, type, ptr, sz, osz ) (type*)sa_realloc( sa, ptr, 
((sz)*sizeof(type)), ((osz)*sizeof(type)))
 
 #define _strlen(s) (int)strlen(s)
diff --git a/sql/rel.txt b/sql/rel.txt
--- a/sql/rel.txt
+++ b/sql/rel.txt
@@ -130,6 +130,7 @@ e_cmp
 
                        cmp_all = 14,           cross product
                        cmp_project = 15,       special case for projection 
joins
+
                )
 
 e_func
diff --git a/sql/server/rel_exp.c b/sql/server/rel_exp.c
--- a/sql/server/rel_exp.c
+++ b/sql/server/rel_exp.c
@@ -1068,6 +1068,9 @@ exp_match_exp( sql_exp *e1, sql_exp *e2)
                            exp_match_exp(e1->l, e2->l) && 
                            exp_match_list(e1->r, e2->r))
                                return 1;
+                       else if (e1->flag == e2->flag && (e1->flag == cmp_equal 
|| e1->flag == cmp_notequal) &&
+                               exp_match_exp(e1->l, e2->r) && 
exp_match_exp(e1->r, e2->l))
+                               return 1; /* = and <> operations are 
reflective, so exp_match_exp can be called crossed */
                        break;
                case e_convert:
                        if (!subtype_cmp(exp_totype(e1), exp_totype(e2)) &&
diff --git a/sql/server/rel_optimizer.c b/sql/server/rel_optimizer.c
--- a/sql/server/rel_optimizer.c
+++ b/sql/server/rel_optimizer.c
@@ -5246,6 +5246,114 @@ rel_remove_empty_join(mvc *sql, sql_rel 
        return rel;
 }
 
+typedef struct {
+       sql_rel *p; /* the found join's parent */
+       sql_rel *j; /* the found join relation itself */
+} found_join;
+
+static void
+rel_find_joins(mvc *sql, sql_rel *parent, sql_rel *rel, list *l)
+{
+       if (!rel)
+               return;
+
+       switch (rel->op) {
+               case op_basetable:
+               case op_table:
+               case op_ddl:
+                       break;
+               case op_join:
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to