Changeset: 4f024f388c4f for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=4f024f388c4f
Added Files:
sql/test/in-value-list/Tests/All
sql/test/in-value-list/Tests/various-in-value-list-queries.sql
sql/test/in-value-list/Tests/various-in-value-list-queries.stable.err
sql/test/in-value-list/Tests/various-in-value-list-queries.stable.out
Modified Files:
MonetDB.spec
NT/rules.msc
clients/Tests/MAL-signatures.stable.out
clients/Tests/MAL-signatures.stable.out.int128
clients/Tests/exports.stable.out
clients/mapiclient/dump.c
clients/mapiclient/mclient.1
clients/mapiclient/mclient.c
clients/mapiclient/mhelp.c
clients/mapiclient/msqldump.c
clients/mapiclient/msqldump.h
common/stream/stream.c
common/utils/Makefile.ag
configure.ag
gdk/ChangeLog
gdk/ChangeLog.Apr2019
gdk/Makefile.ag
gdk/gdk.h
gdk/gdk_aggr.c
gdk/gdk_align.c
gdk/gdk_atoms.c
gdk/gdk_atoms.h
gdk/gdk_bat.c
gdk/gdk_batop.c
gdk/gdk_bbp.c
gdk/gdk_calc.c
gdk/gdk_calc_private.h
gdk/gdk_delta.c
gdk/gdk_firstn.c
gdk/gdk_group.c
gdk/gdk_hash.h
gdk/gdk_heap.c
gdk/gdk_imprints.c
gdk/gdk_join.c
gdk/gdk_logger.c
gdk/gdk_logger.h
gdk/gdk_orderidx.c
gdk/gdk_private.h
gdk/gdk_qsort.c
gdk/gdk_qsort_impl.h
gdk/gdk_select.c
gdk/gdk_storage.c
gdk/gdk_tm.c
gdk/gdk_utils.c
gdk/gdk_value.c
geom/monetdb5/geom.c
geom/monetdb5/geom.h
monetdb5/extras/mal_optimizer_template/Tests/opt_sql_append.stable.err
monetdb5/extras/mal_optimizer_template/Tests/opt_sql_append.stable.out.Windows
monetdb5/extras/rapi/converters.c.h
monetdb5/mal/mal_atom.c
monetdb5/mal/mal_instruction.c
monetdb5/modules/atoms/Makefile.ag
monetdb5/modules/atoms/batxml.c
monetdb5/modules/atoms/blob.c
monetdb5/modules/atoms/blob.h
monetdb5/modules/atoms/color.c
monetdb5/modules/atoms/color.h
monetdb5/modules/atoms/identifier.c
monetdb5/modules/atoms/inet.c
monetdb5/modules/atoms/json.c
monetdb5/modules/atoms/json.h
monetdb5/modules/atoms/mtime.c
monetdb5/modules/atoms/mtime.h
monetdb5/modules/atoms/url.c
monetdb5/modules/atoms/url.h
monetdb5/modules/atoms/uuid.c
monetdb5/modules/atoms/xml.c
monetdb5/modules/atoms/xml.h
monetdb5/modules/kernel/aggr.c
monetdb5/modules/kernel/algebra.c
monetdb5/modules/kernel/algebra.mal
monetdb5/modules/kernel/bat5.c
monetdb5/modules/kernel/batstr.c
monetdb5/modules/mal/batcalc.c
monetdb5/modules/mal/mal_io.c
monetdb5/modules/mal/mal_mapi.c
monetdb5/modules/mal/mkey.c
monetdb5/modules/mal/pcre.c
monetdb5/modules/mal/remote.c
monetdb5/modules/mal/tablet.c
monetdb5/modules/mal/tokenizer.c
monetdb5/modules/mal/wlc.mal
monetdb5/optimizer/opt_mergetable.c
monetdb5/optimizer/opt_prelude.c
monetdb5/optimizer/opt_prelude.h
monetdb5/optimizer/opt_support.c
monetdb5/tools/Makefile.ag
rpm.mk.in
sql/ChangeLog
sql/ChangeLog.Apr2019
sql/backends/monet5/UDF/capi/capi.c
sql/backends/monet5/UDF/pyapi/conversion.c
sql/backends/monet5/UDF/pyapi/convert_loops.h
sql/backends/monet5/UDF/pyapi/type_conversion.c
sql/backends/monet5/UDF/udf/udf.c
sql/backends/monet5/rel_bin.c
sql/backends/monet5/sql.c
sql/backends/monet5/sql.h
sql/backends/monet5/sql_bat2time.c
sql/backends/monet5/sql_cast.c
sql/backends/monet5/sql_cast_impl_down_from_flt.h
sql/backends/monet5/sql_cast_impl_int.h
sql/backends/monet5/sql_cast_impl_up_to_flt.h
sql/backends/monet5/sql_cat.c
sql/backends/monet5/sql_cat.h
sql/backends/monet5/sql_execute.c
sql/backends/monet5/sql_rank.c
sql/backends/monet5/sql_rank.h
sql/backends/monet5/sql_rank.mal
sql/backends/monet5/sql_result.c
sql/backends/monet5/sql_round_impl.h
sql/backends/monet5/sql_scenario.c
sql/backends/monet5/sql_statement.c
sql/backends/monet5/sql_statement.h
sql/backends/monet5/sql_upgrades.c
sql/backends/monet5/sqlcatalog.mal
sql/backends/monet5/vaults/bam/Tests/query2.2.stable.out
sql/backends/monet5/vaults/bam/Tests/query2.2.stable.out.int128
sql/backends/monet5/vaults/bam/bam_db_interface.c
sql/backends/monet5/vaults/bam/bam_export.c
sql/backends/monet5/vaults/bam/bam_lib.c
sql/backends/monet5/wlr.mal
sql/common/sql_list.c
sql/common/sql_string.c
sql/common/sql_types.c
sql/include/sql_catalog.h
sql/include/sql_relation.h
sql/jdbc/tests/Tests/BugResultSetMetaData_Bug_6183.stable.out
sql/scripts/75_storagemodel.sql
sql/server/rel_dump.c
sql/server/rel_optimizer.c
sql/server/rel_psm.c
sql/server/rel_schema.c
sql/server/rel_select.c
sql/server/rel_semantic.c
sql/server/rel_sequence.c
sql/server/rel_updates.c
sql/server/sql_atom.c
sql/server/sql_mvc.c
sql/server/sql_mvc.h
sql/server/sql_parser.h
sql/server/sql_parser.y
sql/server/sql_partition.c
sql/server/sql_privileges.c
sql/server/sql_scan.c
sql/storage/bat/bat_logger.c
sql/storage/bat/bat_storage.c
sql/storage/bat/bat_table.c
sql/storage/sql_catalog.c
sql/storage/sql_storage.h
sql/storage/store.c
sql/test/BugDay_2005-10-06_2.9.3/Tests/UNION_JOIN_vs_plain_set_UNION-2.SF-920585.sql
sql/test/BugDay_2005-10-06_2.9.3/Tests/foreign_key_in_frontend_crashes_backend.SF-935578.sql
sql/test/BugTracker-2009/Tests/copy_multiple_files.SF-2902320.stable.err
sql/test/BugTracker-2009/Tests/copy_multiple_files.SF-2902320.stable.out.Windows
sql/test/BugTracker-2010/Tests/multiple-common-table-expressions.Bug-2664.sql
sql/test/BugTracker-2012/Tests/strange_escaping_in_csv.Bug-2133.sql
sql/test/BugTracker-2012/Tests/strange_escaping_in_csv.Bug-2133.stable.out
sql/test/BugTracker-2013/Tests/aggregates-typing-issues.Bug-3277.sql
sql/test/BugTracker-2013/Tests/median-null.Bug-3280.sql
sql/test/BugTracker-2014/Tests/null_in_string.Bug-2014.sql
sql/test/BugTracker-2015/Tests/crash.Bug-3736.stable.out
sql/test/BugTracker-2015/Tests/is_null_limit.Bug-3684.sql
sql/test/BugTracker-2016/Tests/storagemodel.stable.out
sql/test/BugTracker-2017/Tests/crash_correlated_subqueries_in_select.Bug-6254.stable.out
sql/test/BugTracker-2017/Tests/semijoinunion.Bug-6150.sql
sql/test/BugTracker-2018/Tests/All
sql/test/BugTracker-2018/Tests/count_from_commented_function_signatures.Bug-6542.stable.out
sql/test/BugTracker-2018/Tests/groupby_having_orderby_count.Bug-6624.stable.out
sql/test/BugTracker-2018/Tests/in-subquery-having-Bug-6651.sql
sql/test/BugTracker-2018/Tests/in-subquery-having-Bug-6651.stable.out
sql/test/BugTracker-2018/Tests/sqlitelogictest-groupby-coalesce-error-message.Bug-6608.sql
sql/test/BugTracker-2018/Tests/sqlitelogictest-groupby-coalesce-error-message.Bug-6608.stable.out
sql/test/Dependencies/Tests/Dependencies.stable.out
sql/test/Dependencies/Tests/Dependencies.stable.out.int128
sql/test/Dependencies/Tests/dependency_loop.stable.out
sql/test/Dependencies/Tests/dependency_loop.stable.out.int128
sql/test/Dump/Tests/dump.stable.out
sql/test/SQLite_regress/sqllogictest/Tests/select3.test.stable.out
sql/test/SQLite_regress/sqllogictest/Tests/select3.test.stable.out.int128
sql/test/Tests/rank.stable.out
sql/test/Tests/systemfunctions.stable.out
sql/test/Tests/systemfunctions.stable.out.int128
sql/test/bugs/Tests/innerjoin_multiple-bug-sf-943661.stable.out
sql/test/copy/Tests/int_parse.sql
sql/test/copy/Tests/int_parse.stable.err
sql/test/copy/Tests/int_parse_best.sql
sql/test/copy/Tests/key_copy.sql
sql/test/copy/Tests/key_copy.stable.err
sql/test/emptydb-upgrade-chain/Tests/upgrade.stable.out.int128
sql/test/emptydb-upgrade/Tests/upgrade.stable.out.int128
sql/test/emptydb/Tests/check.stable.out
sql/test/emptydb/Tests/check.stable.out.32bit
sql/test/emptydb/Tests/check.stable.out.int128
sql/test/group-concat/Tests/groupconcat05.stable.out
sql/test/json/Tests/jsonvalidity.Bug-3753.stable.out
sql/test/mergetables/Tests/sqlsmith-apply-outer-join-or.sql
sql/test/orderidx/Tests/imprints_all_types.stable.out
sql/test/orderidx/Tests/oidx_all_types.stable.out
sql/test/pg_regress/Tests/point.stable.out
sql/test/rank.sql
sql/test/sys-schema/Tests/check_ForeignKey_referential_integrity.sql
sql/test/sys-schema/Tests/check_ForeignKey_referential_integrity.stable.out
sql/test/testdb-upgrade-chain/Tests/upgrade.stable.out.int128
sql/test/testdb-upgrade-hge/Tests/dump.stable.out.int128
sql/test/testdb-upgrade/Tests/dump.stable.out
sql/test/testdb-upgrade/Tests/upgrade.stable.out.int128
sql/test/testdb/Tests/All
testing/Mtest.py.in
testing/malcheck.py
testing/process.py
tools/merovingian/client/monetdb.c
tools/merovingian/daemon/argvcmds.c
tools/merovingian/daemon/merovingian.c
tools/mserver/monet_version.c.in
tools/mserver/mserver5.1.in
tools/mserver/mserver5.c
Branch: default
Log Message:
Merge with in-value-list.
diffs (truncated from 669 to 300 lines):
diff --git a/monetdb5/modules/atoms/xml.c b/monetdb5/modules/atoms/xml.c
--- a/monetdb5/modules/atoms/xml.c
+++ b/monetdb5/modules/atoms/xml.c
@@ -691,6 +691,7 @@ ssize_t XMLfromString(const char *src, s
(void) src;
(void) len;
(void) x;
+ (void) external;
GDKerror("not implemented\n");
return -1;
}
diff --git a/monetdb5/optimizer/opt_mergetable.c
b/monetdb5/optimizer/opt_mergetable.c
--- a/monetdb5/optimizer/opt_mergetable.c
+++ b/monetdb5/optimizer/opt_mergetable.c
@@ -497,9 +497,9 @@ mat_apply2(matlist_t *ml, MalBlkPtr mb,
for(l=0; l < p->retc; l++) {
int res = 0;
if (is_select)
- res = setPartnr(ml, getArg(q,p->retc+1),
getArg(q,l), k);
+ res = propagatePartnr(ml, getArg(q,p->retc+1),
getArg(q,l), k);
else
- res = setPartnr(ml, -1, getArg(q,l), k);
+ res = propagatePartnr(ml, -1, getArg(q,l), k);
if(res) {
for(l=0; l < k; l++)
freeInstruction(r[l]);
@@ -511,13 +511,12 @@ mat_apply2(matlist_t *ml, MalBlkPtr mb,
}
for(k=0; k < p->retc; k++) {
- if(mat_add_var(ml, r[k], NULL, getArg(r[k], 0), mat_type(ml->v,
m), -1, -1, 1)) {
+ if(mat_add_var(ml, r[k], NULL, getArg(r[k], 0), mat_type(ml->v,
m), -1, -1, 0)) {
for(l=0; l < k; l++)
freeInstruction(r[l]);
GDKfree(r);
return -1;
}
- pushInstruction(mb, r[k]);
}
GDKfree(r);
return 0;
@@ -612,7 +611,7 @@ mat_setop(MalBlkPtr mb, InstrPtr p, matl
getArg(s,0) = newTmpVariable(mb, getArgType(mb,
mat[n].mi, k));
for (j=1; j<mat[n].mi->argc; j++) {
- if (overlap(ml, getArg(mat[m].mi, k),
getArg(mat[n].mi, j), -1, -2, 1)){
+ if (overlap(ml, getArg(mat[m].mi, k),
getArg(mat[n].mi, j), k, j, 1)){
s =
pushArgument(mb,s,getArg(mat[n].mi,j));
}
}
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
@@ -255,6 +255,45 @@ row2cols(backend *be, stmt *sub)
return sub;
}
+static stmt*
+distinct_value_list(backend *be, list *vals, stmt ** last_null_value)
+{
+ node *n;
+ stmt *s;
+
+ /* create bat append values */
+ s = stmt_temp(be, exp_subtype(vals->h->data));
+ for( n = vals->h; n; n = n->next) {
+ sql_exp *e = n->data;
+ stmt *i = exp_bin(be, e, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ if (exp_is_null(be->mvc, e))
+ *last_null_value = i;
+
+ if (!i)
+ return NULL;
+
+ s = stmt_append(be, s, i);
+ }
+
+ // Probably faster to filter out the values directly in the underlying
list of atoms.
+ // But for now use groupby to filter out duplicate values.
+
+ stmt* groupby = stmt_group(be, s, NULL, NULL, NULL, 1);
+ stmt* ext = stmt_result(be, groupby, 1);
+
+ return stmt_project(be, ext, s);
+}
+
+static stmt *
+stmt_selectnonil( backend *be, stmt *col, stmt *s )
+{
+ sql_subtype *t = tail_type(col);
+ stmt *n = stmt_atom(be, atom_general(be->mvc->sa, t, NULL));
+ stmt *nn = stmt_uselect2(be, col, n, n, 3, s, 1);
+ return nn;
+}
+
static stmt *
handle_in_exps(backend *be, sql_exp *ce, list *nl, stmt *left, stmt *right,
stmt *grp, stmt *ext, stmt *cnt, stmt *sel, int in, int use_r)
{
@@ -291,27 +330,55 @@ handle_in_exps(backend *be, sql_exp *ce,
stmt_const(be, bin_first_column(be, left), s),
stmt_bool(be, 1), cmp_equal, sel, 0);
} else {
- comp_type cmp = (in)?cmp_equal:cmp_notequal;
-
- if (!in)
- s = sel;
- for( n = nl->h; n; n = n->next) {
- sql_exp *e = n->data;
- stmt *i = exp_bin(be, use_r?e->r:e, left, right, grp,
ext, cnt, NULL);
- if(!i)
- return NULL;
-
- if (in) {
- i = stmt_uselect(be, c, i, cmp, sel, 0);
- if (s)
- s = stmt_tunion(be, s, i);
- else
- s = i;
- } else {
- s = stmt_uselect(be, c, i, cmp, s, 0);
+ // TODO: handle_in_exps should contain all necessary logic for
in-expressions to be SQL compliant.
+ // For non-SQL-standard compliant behavior, e.g. PostgreSQL
backwards compatibility, we should
+ // make sure that this behavior is replicated by the sql
optimizer and not handle_in_exps.
+
+ stmt* last_null_value = NULL; // CORNER CASE ALERT: See
description below.
+
+ // The actual in-value-list should not contain duplicates to
ensure that final join results are unique.
+ s = distinct_value_list(be, nl, &last_null_value);
+
+ if (last_null_value) {
+ // The actual in-value-list should not contain null
values.
+ s = stmt_project(be, stmt_selectnonil(be, s, NULL), s);
+ }
+
+ s = stmt_join(be, c, s, in, cmp_left);
+ s = stmt_result(be, s, 0);
+
+ if (!in) {
+ if (last_null_value) {
+ // CORNER CASE ALERT:
+ // In case of a not-in-expression with the
associated in-value-list containing a null value,
+ // the entire in-predicate is forced to always
return false, i.e. an empty candidate list.
+ // This is similar to postgres behavior.
+ // TODO: However I do not think this behavior
is in accordance with SQL standard 2003.
+
+ // Ugly trick to return empty candidate list,
because for all x it holds that: (x == null) == false.
+ //list* singleton_bat = sa_list(sql->sa);
+ // list_append(singleton_bat, null_value);
+ s = stmt_uselect(be, c, last_null_value,
cmp_equal, NULL, 0);
+ return s;
+ }
+ else {
+ // BACK TO HAPPY FLOW:
+ // Make sure that null values are never
returned.
+ stmt* non_nulls;
+ non_nulls = stmt_selectnonil(be, c, NULL);
+ s = stmt_tdiff(be, non_nulls, s);
+ s = stmt_project(be, s, non_nulls);
}
}
- }
+
+ if (sel) {
+ stmt* oid_intersection;
+ oid_intersection = stmt_tinter(be, s, sel);
+ s = stmt_project(be, oid_intersection, s);
+ s = stmt_result(be, s, 0);
+ }
+ }
+
return s;
}
@@ -3156,15 +3223,6 @@ sql_parse(backend *be, sql_allocator *sa
}
static stmt *
-stmt_selectnonil( backend *be, stmt *col, stmt *s )
-{
- sql_subtype *t = tail_type(col);
- stmt *n = stmt_atom(be, atom_general(be->mvc->sa, t, NULL));
- stmt *nn = stmt_uselect2(be, col, n, n, 3, s, 1);
- return nn;
-}
-
-static stmt *
insert_check_ukey(backend *be, list *inserts, sql_key *k, stmt *idx_inserts)
{
mvc *sql = be->mvc;
diff --git a/sql/backends/monet5/sql_execute.c
b/sql/backends/monet5/sql_execute.c
--- a/sql/backends/monet5/sql_execute.c
+++ b/sql/backends/monet5/sql_execute.c
@@ -316,7 +316,7 @@ SQLrun(Client c, backend *be, mvc *m)
throw(SQL, "sql.prepare", SQLSTATE(HY001)
MAL_MALLOC_FAIL);
}
retc = p->retc;
- freeMalBlk(mb);
+ freeMalBlk(mb); // TODO can be factored out
mb = mc;
// declare the argument values as a constant
// We use the knowledge that the arguments are first on
the stack
@@ -860,7 +860,7 @@ RAstatement(Client c, MalBlkPtr mb, MalS
/* generate MAL code, ignoring any code generation error */
if (backend_callinline(b, c) < 0 ||
backend_dumpstmt(b, c->curprg->def, rel, 1, 1, NULL) < 0) {
- msg = createException(SQL,"RAstatement","Program
contains errors");
+ msg = createException(SQL,"RAstatement","Program
contains errors"); // TODO: use macro definition.
} else {
SQLaddQueryToCache(c);
msg = SQLoptimizeFunction(c,c->curprg->def);
diff --git a/sql/backends/monet5/sql_statement.h
b/sql/backends/monet5/sql_statement.h
--- a/sql/backends/monet5/sql_statement.h
+++ b/sql/backends/monet5/sql_statement.h
@@ -111,7 +111,7 @@ typedef struct stmt {
stmtdata op4; /* only op4 will hold other types */
char nrcols;
- char key; /* key (aka all values are unique) */
+ char key; /* key (aka all values are unique) */ // TODO
make this thing a bool
char aggr; /* aggregated */
char partition; /* selected as mitosis candidate */
diff --git a/sql/test/in-value-list/Tests/All b/sql/test/in-value-list/Tests/All
new file mode 100644
--- /dev/null
+++ b/sql/test/in-value-list/Tests/All
@@ -0,0 +1,1 @@
+various-in-value-list-queries
diff --git a/sql/test/in-value-list/Tests/various-in-value-list-queries.sql
b/sql/test/in-value-list/Tests/various-in-value-list-queries.sql
new file mode 100644
--- /dev/null
+++ b/sql/test/in-value-list/Tests/various-in-value-list-queries.sql
@@ -0,0 +1,66 @@
+create table foo (i int);
+insert into foo values (null), (10), (null), (20), (10), (30), (30), (30),
(50), (40), (50);
+
+select * from foo where i in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i not in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i = 40 or i in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i = 40 or i not in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i = 40 and i in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i = 40 and i not in (10, 20, 20, 30, 30) order by i;
+
+select * from foo where i in (10, 20, 20, 30, 30) and i in (20, 30, 40) order
by i;
+
+select * from foo where i not in (10, 20, 20, 30, 30) and i not in (20, 30,
40) order by i;
+
+select * from foo where i in (10, 20, 20, 30, 30) and i not in (20, 30, 40)
order by i;
+
+select * from foo where i not in (10, 20, 20, 30, 30) and i in (20, 30, 40)
order by i;
+
+select * from foo where i in (10, 20, 20, 30, 30) or i in (20, 30, 40) order
by i;
+
+select * from foo where i not in (10, 20, 20, 30, 30) or i not in (20, 30, 40)
order by i;
+
+select * from foo where i in (10, 20, 20, 30, 30) or i not in (20, 30, 40)
order by i;
+
+select * from foo where i not in (10, 20, 20, 30, 30) or i in (20, 30, 40)
order by i;
+
+select * from foo where i = 40 and i not in (10, 20 + 20, 30) order by i;
+
+select * from foo where i = 40 and i in (2*10 + 10, null, i - 1, i, i + 1,
null) order by i;
+
+select * from foo where i = 40 and i not in (2*10 + 10, null, i - 1, i, i + 1,
null) order by i;
+
+select * from foo where i = 40 and (i + 10) in (2*10 + 10, null, i - 1, i, i +
10, null) order by i;
+
+select * from foo where i = 40 and (i + 10) not in (2*10 + 10, null, i - 1, i,
i + 10, null) order by i;
+
+select * from foo where i = 40 or i not in (10, 20 + 20, 30) order by i;
+
+select * from foo where i = 40 or i in (2*10 + 10, null, i - 1, i, i + 1,
null) order by i;
+
+select * from foo where i = 40 or i not in (2*10 + 10, null, i - 1, i, i + 1,
null) order by i;
+
+select * from foo where i = 40 or (i + 10) in (2*10 + 10, null, i - 1, i, i +
10, null) order by i;
+
+select * from foo where i = 40 or (i + 10) not in (2*10 + 10, null, i - 1, i,
i + 10, null) order by i;
+
+select * from foo where i in (10, null, 20, null) order by i;
+
+select * from foo where i not in (10, null, 20, null) order by i;
+
+select * from foo where i = 40 and i not in (null) order by i;
+
+select * from foo where i = 40 and i in (null) order by i;
+
+select * from foo where i = 40 and i in (select bar.i from foo as bar where i
in (10, 20)) order by i;
+
+select * from foo where i = 40 or i in (select bar.i from foo as bar where i
in (10, 20)) order by i;
+
+select * from foo where i = 40 and i not in (select bar.i from foo as bar
where i in (10, 20)) order by i;
+
+select * from foo where i = 40 or i not in (select bar.i from foo as bar where
i in (10, 20)) order by i;
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list