MonetDB: Aug2024 - Add test for clientinfo

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 42d6226ac18f for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/42d6226ac18f
Added Files:
sql/test/mapi/Tests/clientinfo-mclient.SQL.py
Modified Files:
sql/test/mapi/Tests/All
Branch: Aug2024
Log Message:

Add test for clientinfo

It uses mclient so it doesn't require pymonetdb updates


diffs (67 lines):

diff --git a/sql/test/mapi/Tests/All b/sql/test/mapi/Tests/All
--- a/sql/test/mapi/Tests/All
+++ b/sql/test/mapi/Tests/All
@@ -9,3 +9,4 @@ HAVE_HGE?sql_int128
 HAVE_HGE?python3_int128
 HAVE_HGE?sql_dec38
 HAVE_HGE?python3_dec38
+clientinfo-mclient
diff --git a/sql/test/mapi/Tests/clientinfo-mclient.SQL.py 
b/sql/test/mapi/Tests/clientinfo-mclient.SQL.py
new file mode 100644
--- /dev/null
+++ b/sql/test/mapi/Tests/clientinfo-mclient.SQL.py
@@ -0,0 +1,54 @@
+import os
+import subprocess
+
+TSTDB = os.environ['TSTDB']
+MAPIPORT = os.environ['MAPIPORT']
+
+QUERY = """\
+SELECT *
+FROM sys.sessions
+WHERE sessionid = current_sessionid()
+"""
+
+cmd = [
+'mclient',
+'-d', TSTDB,
+'-p', MAPIPORT,
+'-fexpanded',
+'-s', QUERY,
+]
+
+out = subprocess.check_output(cmd, encoding='latin1')
+
+# print(out)
+
+fields = dict()
+for line in out.splitlines()[1:]:
+line = line.strip()
+if line:
+k, v = line.split('|', 1)
+k = k.strip()
+v = v.strip()
+fields[k] = v
+
+assert fields['language'] == 'sql',\
+f'Found {fields["language"]!r}'
+
+assert fields['peer'] == '' or ']:' in fields['peer'],\
+f'Found {fields["peer"]!r}'
+
+assert fields['hostname'] != 'null',\
+f'Found {fields["hostname"]!r}'
+
+# could be mclient-11.51.0, mclient.exe, or whatever
+assert fields['application'].startswith('mclient'),\
+f'Found {fields["application"]!r}'
+
+assert fields['client'].startswith('libmapi '),\
+f'Found {fields["client"]!r}'
+
+assert fields['clientpid'] != 'null' and int(fields['clientpid']) > 0,\
+f'Found {fields["clientpid"]!r}'
+
+assert fields['remark'] == 'null',\
+f'Found {fields["remark"]!r}'
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Simplify mclient flush test

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: e55267399f84 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/e55267399f84
Modified Files:
sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
Branch: Aug2024
Log Message:

Simplify mclient flush test


diffs (34 lines):

diff --git 
a/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py 
b/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
--- a/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
+++ b/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
@@ -6,7 +6,7 @@ import subprocess
 
 # This SQL script redirects the output to a file (the %s).
 # We will check that all output arrives there, even if it's a gzipped file.
-SCRIPT = f"""\
+SCRIPT = """\
 \>%s
 SELECT 'Donald Knuth';
 """
@@ -19,16 +19,13 @@ with tempfile.TemporaryDirectory('mtest'
 with open(inputfile, 'w') as f:
 f.write(SCRIPT % outputfile)
 
-with open(inputfile) as f:
-subprocess.check_call([
-'mclient', '-i',
-inputfile,
-'-p', os.environ['MAPIPORT'],
-])
+subprocess.check_call([
+'mclient', '-i',
+'-p', os.environ['MAPIPORT'],
+inputfile,
+])
 
 with gzip.open(outputfile, 'rt', encoding='utf-8') as f:
 content = f.read()
 
-# print(content)
-
 assert 'Donald Knuth' in content
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Incorporate TLSTester in Mtest.py

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 9b8c0e6feeb5 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/9b8c0e6feeb5
Modified Files:
clients/mapilib/Tests/tlssecurity.py
testing/Mtest.py.in
testing/tlstester.py
Branch: Aug2024
Log Message:

Incorporate TLSTester in Mtest.py


diffs (196 lines):

diff --git a/clients/mapilib/Tests/tlssecurity.py 
b/clients/mapilib/Tests/tlssecurity.py
--- a/clients/mapilib/Tests/tlssecurity.py
+++ b/clients/mapilib/Tests/tlssecurity.py
@@ -14,7 +14,7 @@ import os
 import re
 import subprocess
 import sys
-import threading
+import urllib.request
 
 from MonetDBtesting import tlstester
 
@@ -26,45 +26,64 @@ if '-v' in sys.argv:
 #level = logging.DEBUG
 logging.basicConfig(level=level)
 
+# A tmpdir to write certificates to
 tgtdir = os.environ['TSTTRGDIR']
 assert os.path.isdir(tgtdir)
+scratchdir = os.path.join(tgtdir, "scratch")
+logging.debug(f"scratchdir={scratchdir}")
 
-hostnames = ['localhost']
-# Generate certificates and write them to the scratch dir
-# Write them to the scratch dir for inspection by the user.
-certs = tlstester.Certs(hostnames)
-certsdir = os.path.join(tgtdir, "certs")
-try:
-os.mkdir(certsdir)
-except FileExistsError:
-pass
-count = 0
-for name, content in certs.all().items():
-with open(os.path.join(certsdir, name), "wb") as a:
-a.write(content)
-count += 1
-logging.debug(f"Wrote {count} files to {certsdir}")
+class TLSTesterClient:
+"""Connect to TLSTester to figure out port numbers and download 
certificates"""
+def __init__(self, scratchdir, base_port=None, host='localhost'):
+if not base_port:
+base_port = os.environ['TST_TLSTESTERPORT']
+self.url = f'http://{host}:{base_port}/'
+self.scratch = scratchdir
+try:
+os.mkdir(scratchdir)
+except FileExistsError:
+pass
+self.filenames = dict()
+self.contents = dict()
+self.portmap = dict()
+for line in self.fetch('').splitlines():
+name, port = str(line, 'ascii').split(':', 1)
+self.portmap[name] = int(port)
+logging.debug(f'port {name} = {port}')
+
+def get_port(self, name):
+return self.portmap[name]
+
+def fetch(self, name):
+cached = self.contents.get(name)
+if cached is not None:
+return cached
+url = self.url + name
+logging.debug(f'fetch {url}')
+with urllib.request.urlopen(url) as response:
+content = response.read()
+self.contents[name] = content
+return content
+
+def download(self, name):
+cached = self.filenames.get(name)
+if cached:
+return cached
+content = self.fetch(name)
+path = os.path.join(self.scratch, name)
+with open(path, 'wb') as f:
+f.write(content)
+self.filenames[name] = path
+return path
+
+tlstester = TLSTesterClient(scratchdir)
+
 
 def certpath(name):
-return os.path.join(certsdir, name)
-def certbytes(name):
-filename = certpath(name)
-with open(filename, 'rb') as f:
-return f.read()
-
-# Start the worker threads
-
-server = tlstester.TLSTester(
-certs=certs,
-listen_addr='127.0.0.1',
-preassigned=dict(),
-sequential=False,
-hostnames=hostnames)
-server_thread = threading.Thread(target=server.serve_forever, daemon=True)
-server_thread.start()
+return tlstester.download(name)
 
 def attempt(experiment: str, portname: str, expected_error_regex: str, 
tls=True, host='localhost', **params):
-port = server.get_port(portname)
+port = tlstester.get_port(portname)
 scheme = 'monetdbs' if tls else 'monetdb'
 url = f"{scheme}://{host}:{port}/demo"
 if params:
@@ -196,7 +215,7 @@ attempt('connect_server_name', 'sni', No
 # Connect to port 'server1' over TLS, with certhash set to a prefix of the hash
 # of the server certificate in DER form. Have a succesful MAPI exchange.
 
-server1hash = sha256(certs.get_file('server1.der')).hexdigest()
+server1hash = sha256(tlstester.fetch('server1.der')).hexdigest()
 attempt('connect_right_hash', 'server1', None, certhash='sha256:' + 
server1hash[:6])
 
 # connect_wrong_hash
@@ -217,7 +236,7 @@ attempt('connect_wrong_hash', 'server1',
 # Connect to port 'server1' over TLS, with certhash set to a prefix of the hash
 # of the CA1 certificate in DER form. This should fail.
 
-ca1hash = sha256(certs.get_file('ca1.der')).hexdigest()
+ca1hash = sha256(tlstester.fetch('ca1.der')).hexdigest()
 attempt('connect_ca_hash', 'server1', "does not match certhash", 
certhash='sha256:' + ca1hash[:6])
 
 
diff --git a/testing/Mtest.py.in b/testing/Mtest.py.in
--- a/testing/Mtest.py.in
+++ b/testing/Mtest.py.in
@@ -3240,6 +3240,36 @@ def SetExecEnv(exe,port,verbose) :
 print(end='', flush=True)
 ### SetExecEnv(exe,port,procdebug) #
 
+def StartTlsTester(tsttrgdir):
+try:
+import cryptography
+except:
+# co

MonetDB: Aug2024 - Move odbcconnect from clients/odbc/samples to...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: e8186ee84c8e for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/e8186ee84c8e
Added Files:
clients/odbc/tests/odbcconnect.c
Removed Files:
clients/odbc/samples/odbcconnect.c
Modified Files:
clients/odbc/samples/CMakeLists.txt
clients/odbc/tests/CMakeLists.txt
Branch: Aug2024
Log Message:

Move odbcconnect from clients/odbc/samples to clients/odbc/tests


diffs (63 lines):

diff --git a/clients/odbc/samples/CMakeLists.txt 
b/clients/odbc/samples/CMakeLists.txt
--- a/clients/odbc/samples/CMakeLists.txt
+++ b/clients/odbc/samples/CMakeLists.txt
@@ -26,17 +26,9 @@ target_link_libraries(arraytest
   PRIVATE
   ODBC::ODBC)
 
-add_executable(odbcconnect
-  odbcconnect.c)
-
-target_link_libraries(odbcconnect
-  PRIVATE
-  ODBC::ODBC)
-
 install(TARGETS
   odbcsample1
   arraytest
-  odbcconnect
   RUNTIME
   DESTINATION
   ${CMAKE_INSTALL_BINDIR}
@@ -46,7 +38,6 @@ if(WIN32)
   install(FILES
 $
 $
-$
 DESTINATION ${CMAKE_INSTALL_BINDIR}
 OPTIONAL)
 endif()
diff --git a/clients/odbc/tests/CMakeLists.txt 
b/clients/odbc/tests/CMakeLists.txt
--- a/clients/odbc/tests/CMakeLists.txt
+++ b/clients/odbc/tests/CMakeLists.txt
@@ -39,11 +39,19 @@ target_link_libraries(ODBCtester
   PRIVATE
   ODBC::ODBC)
 
+add_executable(odbcconnect
+  odbcconnect.c)
+
+target_link_libraries(odbcconnect
+  PRIVATE
+  ODBC::ODBC)
+
 install(TARGETS
   ODBCgetInfo
   ODBCStmtAttr
   ODBCmetadata
   ODBCtester
+  odbcconnect
   RUNTIME
   DESTINATION
   ${CMAKE_INSTALL_BINDIR}
@@ -55,6 +63,7 @@ if(WIN32)
 $
 $
 $
+$
 DESTINATION ${CMAKE_INSTALL_BINDIR}
 OPTIONAL)
 endif()
diff --git a/clients/odbc/samples/odbcconnect.c 
b/clients/odbc/tests/odbcconnect.c
rename from clients/odbc/samples/odbcconnect.c
rename to clients/odbc/tests/odbcconnect.c
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Run a dummy server if the cryptography module...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 6d3098fd2db1 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/6d3098fd2db1
Modified Files:
testing/Mtest.py.in
Branch: Aug2024
Log Message:

Run a dummy server if the cryptography module cannot be found


diffs (52 lines):

diff --git a/testing/Mtest.py.in b/testing/Mtest.py.in
--- a/testing/Mtest.py.in
+++ b/testing/Mtest.py.in
@@ -35,6 +35,8 @@ import socket
 import struct
 import signal
 import fnmatch
+import http
+import http.server
 import glob
 import pymonetdb # check for pymonetdb early: it is essential for our work
 
@@ -3240,13 +3242,26 @@ def SetExecEnv(exe,port,verbose) :
 print(end='', flush=True)
 ### SetExecEnv(exe,port,procdebug) #
 
-def StartTlsTester(tsttrgdir):
+def DummyTlsTester():
+class DummyHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
+def handle(self):
+self.requestline = ''
+self.request_version = ''
+self.command = ''
+self.send_error(http.HTTPStatus.INTERNAL_SERVER_ERROR, 
f"{sys.argv[0]} is not running TLSTestter because the 'cryptography' module is 
not present")
+server = http.server.HTTPServer(('localhost', 0), DummyHTTPRequestHandler)
+port = server.server_address[1]
+server_thread = threading.Thread(target=server.serve_forever, daemon=True)
+server_thread.start()
+return port
+### DummyTlsTester(exe,port,procdebug) #
+
+def StartTlsTester():
 try:
 import cryptography
 except:
-# continue without so we can at least run the other tests
-print("cryptography not found!", file=sys.stderr)
-return None
+# Start a dummy server that only returns errors.
+return DummyTlsTester()
 from MonetDBtesting import tlstester
 hostnames = ['localhost']
 certs = tlstester.Certs(hostnames)
@@ -3808,7 +3823,7 @@ def main(argv) :
 
 # start tlstester
 if not env.get('TST_TLSTESTERPORT'):
-tlstester_port = StartTlsTester(os.path.join(TSTTRGBASE, TSTPREF))
+tlstester_port = StartTlsTester()
 if tlstester_port:
 env['TST_TLSTESTERPORT'] = str(tlstester_port)
 if env.get('TST_TLSTESTERPORT'):
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - ODBCtester.SQL.sh does not need to create odb...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: e88194ba7d08 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/e88194ba7d08
Modified Files:
sql/odbc/tests/Tests/ODBCtester.SQL.sh
Branch: Aug2024
Log Message:

ODBCtester.SQL.sh does not need to create odbc.ini

Mtest takes care of that


diffs (23 lines):

diff --git a/sql/odbc/tests/Tests/ODBCtester.SQL.sh 
b/sql/odbc/tests/Tests/ODBCtester.SQL.sh
--- a/sql/odbc/tests/Tests/ODBCtester.SQL.sh
+++ b/sql/odbc/tests/Tests/ODBCtester.SQL.sh
@@ -1,19 +1,3 @@
 #!/bin/sh
 
-ODBCINI=$PWD/odbc.ini
-trap "rm $ODBCINI" 0 15
-cat > $ODBCINI <

MonetDB: Aug2024 - Make odbcconnect flags more straightforward

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 48e8d738ef63 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/48e8d738ef63
Modified Files:
clients/odbc/tests/odbcconnect.c
Branch: Aug2024
Log Message:

Make odbcconnect flags more straightforward

-d for driver, -b for browse


diffs (29 lines):

diff --git a/clients/odbc/tests/odbcconnect.c b/clients/odbc/tests/odbcconnect.c
--- a/clients/odbc/tests/odbcconnect.c
+++ b/clients/odbc/tests/odbcconnect.c
@@ -28,14 +28,13 @@ static const char *USAGE =
"Usage:\n"
"odbcconnect [-d | -c | -b ] [-v] [-u USER] [-p PASSWORD] 
TARGET..\n"
"Options:\n"
-   "-d  Target is DSN, call SQLConnect()\n"
-   "-c  Target is connection string, call 
SQLDriverConnect()\n"
+   "-d  Target is connection string, call 
SQLDriverConnect()\n"
"-b  Target is connection string, call 
SQLBrowseConnect()\n"
"-l  List registered drivers and data sources\n"
"-u USER\n"
"-p PASSWORD\n"
"-v  Be verbose\n"
-   "TARGET  Connection String or DSN\n";
+   "TARGET  DSN or with -d and -b, Connection String\n";
 
 typedef int (action_t)(SQLCHAR *);
 
@@ -84,8 +83,6 @@ main(int argc, char **argv)
for (int i = 1; i < argc; i++) {
char *arg = argv[i];
if (strcmp(arg, "-d") == 0)
-   action = do_sqlconnect;
-   else if (strcmp(arg, "-c") == 0)
action = do_sqldriverconnect;
else if (strcmp(arg, "-b") == 0)
action = do_sqlbrowseconnect;
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - odbc: Map InvalidCredentialsException to SQLS...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 58c7bfa4dbc9 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/58c7bfa4dbc9
Modified Files:
clients/odbc/driver/SQLConnect.c
Branch: Aug2024
Log Message:

odbc: Map InvalidCredentialsException to SQLSTATE 28000


diffs (17 lines):

diff --git a/clients/odbc/driver/SQLConnect.c b/clients/odbc/driver/SQLConnect.c
--- a/clients/odbc/driver/SQLConnect.c
+++ b/clients/odbc/driver/SQLConnect.c
@@ -487,8 +487,12 @@ MNDBConnectSettings(ODBCDbc *dbc, const 
mapi_reconnect(mid);
}
if (mid == NULL || mapi_error(mid)) {
-   const char *error_state = "08001";
+   const char *error_state;
const char *error_explanation = mid ? mapi_error_str(mid) : 
NULL;
+   if (error_explanation && strncmp(error_explanation, 
"InvalidCredentialsException:", 28) == 0)
+   error_state = "28000";
+   else
+   error_state = "08001";
addDbcError(dbc, error_state, error_explanation, 0);
if (mid)
mapi_destroy(mid);
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Use raw string to suppress Python warning

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 750de1fa87e2 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/750de1fa87e2
Modified Files:
sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
Branch: Aug2024
Log Message:

Use raw string to suppress Python warning


diffs (12 lines):

diff --git 
a/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py 
b/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
--- a/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
+++ b/sql/test/BugTracker-2024/Tests/7536-mclient-forgets-to-flush.SQL.py
@@ -6,7 +6,7 @@ import subprocess
 
 # This SQL script redirects the output to a file (the %s).
 # We will check that all output arrives there, even if it's a gzipped file.
-SCRIPT = """\
+SCRIPT = r"""
 \>%s
 SELECT 'Donald Knuth';
 """
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Add tests for client_{info,application,remark...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: f17ab4c9405d for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/f17ab4c9405d
Modified Files:
sql/test/mapi/Tests/clientinfo-mclient.SQL.py
Branch: Aug2024
Log Message:

Add tests for client_{info,application,remark} properties


diffs (116 lines):

diff --git a/sql/test/mapi/Tests/clientinfo-mclient.SQL.py 
b/sql/test/mapi/Tests/clientinfo-mclient.SQL.py
--- a/sql/test/mapi/Tests/clientinfo-mclient.SQL.py
+++ b/sql/test/mapi/Tests/clientinfo-mclient.SQL.py
@@ -1,5 +1,6 @@
 import os
 import subprocess
+import urllib.parse
 
 TSTDB = os.environ['TSTDB']
 MAPIPORT = os.environ['MAPIPORT']
@@ -10,26 +11,27 @@ FROM sys.sessions
 WHERE sessionid = current_sessionid()
 """
 
-cmd = [
-'mclient',
-'-d', TSTDB,
-'-p', MAPIPORT,
-'-fexpanded',
-'-s', QUERY,
-]
-
-out = subprocess.check_output(cmd, encoding='latin1')
+def run_mclient(**extra_args):
+url = f'monetdb://localhost:{MAPIPORT}/{TSTDB}'
+if extra_args:
+url += '?' + urllib.parse.urlencode(extra_args)
+cmd = [ 'mclient', '-d', url, '-fexpanded', '-s', QUERY ]
+out = subprocess.check_output(cmd, encoding='latin1')
+fields = dict()
+for line in out.splitlines()[1:]:
+line = line.strip()
+if line:
+k, v = line.split('|', 1)
+k = k.strip()
+v = v.strip()
+fields[k] = v
+return fields
 
-# print(out)
 
-fields = dict()
-for line in out.splitlines()[1:]:
-line = line.strip()
-if line:
-k, v = line.split('|', 1)
-k = k.strip()
-v = v.strip()
-fields[k] = v
+###
+# By default, most fields get filled in
+
+fields = run_mclient()
 
 assert fields['language'] == 'sql',\
 f'Found {fields["language"]!r}'
@@ -52,3 +54,59 @@ assert fields['clientpid'] != 'null' and
 
 assert fields['remark'] == 'null',\
 f'Found {fields["remark"]!r}'
+
+
+###
+# client_info=off suppresses everything sent by the client.
+# Server still fills in language and peer
+
+fields = run_mclient(client_info='off')
+
+assert fields['language'] == 'sql',\
+f'Found {fields["language"]!r}'
+
+assert fields['peer'] == '' or ']:' in fields['peer'],\
+f'Found {fields["peer"]!r}'
+
+assert fields['hostname'] == 'null',\
+f'Found {fields["hostname"]!r}'
+
+# could be mclient-11.51.0, mclient.exe, or whatever
+assert fields['application'] == 'null',\
+f'Found {fields["application"]!r}'
+
+assert fields['client'] == 'null',\
+f'Found {fields["client"]!r}'
+
+assert fields['clientpid'] == 'null',\
+f'Found {fields["clientpid"]!r}'
+
+assert fields['remark'] == 'null',\
+f'Found {fields["remark"]!r}'
+
+###
+# We can override application and remark
+
+fields = run_mclient(client_application='app', client_remark='mark')
+
+# could be mclient-11.51.0, mclient.exe, or whatever
+assert fields['application'] == 'app',\
+f'Found {fields["application"]!r}'
+
+assert fields['remark'] == 'mark',\
+f'Found {fields["remark"]!r}'
+
+
+###
+# We can override application and remark, but client_info=off
+# suppresses that.
+
+fields = run_mclient(client_application='app', client_remark='mark', 
client_info='off')
+
+# could be mclient-11.51.0, mclient.exe, or whatever
+assert fields['application'] == 'null',\
+f'Found {fields["application"]!r}'
+
+assert fields['remark'] == 'null',\
+f'Found {fields["remark"]!r}'
+
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Add test for SQLConnect/DriverConnect/BrowseC...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 0ad8b1cc201d for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/0ad8b1cc201d
Added Files:
sql/odbc/tests/Tests/ODBCconnect.py
Modified Files:
sql/odbc/tests/Tests/All
Branch: Aug2024
Log Message:

Add test for SQLConnect/DriverConnect/BrowseConnect

still incomplete


diffs (149 lines):

diff --git a/sql/odbc/tests/Tests/All b/sql/odbc/tests/Tests/All
--- a/sql/odbc/tests/Tests/All
+++ b/sql/odbc/tests/Tests/All
@@ -2,4 +2,5 @@ HAVE_ODBC?ODBCgetInfo
 HAVE_ODBC?ODBCmetadata
 HAVE_ODBC?ODBCStmtAttr
 HAVE_ODBC?ODBCtester
+HAVE_ODBC?ODBCconnect
 HAVE_PYODBC&!SANITIZER?pyodbc-test
diff --git a/sql/odbc/tests/Tests/ODBCconnect.py 
b/sql/odbc/tests/Tests/ODBCconnect.py
new file mode 100755
--- /dev/null
+++ b/sql/odbc/tests/Tests/ODBCconnect.py
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0.  If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# Copyright 2024 MonetDB Foundation;
+# Copyright August 2008 - 2023 MonetDB B.V.;
+# Copyright 1997 - July 2008 CWI.
+
+
+# For each target, odbcconnect first prints a status line,
+# either 'OK' or 'Error FUNCNAME', followed by zero or more
+# sqlstate lines of the form '- STATE: MESSAGE'.
+
+import atexit
+import subprocess
+import sys
+
+
+class Execution:
+def __init__(self, *odbcconnect_args):
+cmd = self.cmd = ['odbcconnect', *odbcconnect_args]
+proc = self.proc = subprocess.run(
+cmd, capture_output=True, encoding='utf-8')
+self.expected_exitcode = 0
+self.remaining = proc.stdout.splitlines()
+self.checks = []
+
+def report(self):
+parts = [f'COMMAND: {self.cmd}',
+ f'EXIT CODE: {self.proc.returncode}', '']
+if self.proc.stdout:
+parts += [
+'--- stdout: ---',
+self.proc.stdout.rstrip(),
+'--- end ---',
+''
+]
+if self.proc.stderr:
+parts += [
+'--- stderr: ---',
+self.proc.stderr.rstrip(),
+'--- end ---',
+''
+]
+if self.checks:
+parts.append('--- test history: ---')
+for wanted, found in self.checks:
+parts.append(f'wanted {wanted!r}, found {found!r}')
+parts.append('--- end ---')
+if self.remaining:
+parts += [
+'--- remaining output: ---',
+*self.remaining,
+'--- end ---'
+]
+return '\n'.join(parts)
+
+def expect(self, pattern):
+line = self.next_line()
+self.checks.append((pattern, line))
+if pattern not in line:
+raise Exception(f'Wanted {pattern!r}, found {line!r}')
+
+def next_line(self):
+if not self.remaining:
+raise Exception(f"Unexpected end of output")
+line = self.remaining[0]
+del self.remaining[0]
+return line
+
+def expect_fail(self, exitcode=1):
+self.expected_exitcode = exitcode
+
+def end(self):
+if self.remaining:
+raise Exception(f'Unexpected output remaining: {self.remaining}')
+code = self.proc.returncode
+expected = self.expected_exitcode
+if code != expected:
+raise Exception(
+f'Process exited with code {code!r}, expected {expected!r}')
+
+
+ex = None
+
+
+@atexit.register
+def show_context():
+global ex
+if ex:
+# ex.end()
+print(file=sys.stderr)
+print(ex.report(), file=sys.stderr)
+
+
+dbname = 'MonetDB-Test'
+
+
+ex = Execution(dbname)
+ex.expect('OK')
+ex.end()
+
+ex = Execution(dbname + '-nonexistent')
+ex.expect_fail()
+ex.expect('Error')
+ex.expect('IM002:')  # IM002 not found
+ex.end()
+
+ex = Execution(dbname, '-p', 'wrongpassword')
+ex.expect_fail()
+ex.expect('Error')
+ex.expect('28000:')  # 28000 bad credentials
+ex.end()
+
+ex = Execution(dbname, '-u', 'wronguser')
+ex.expect_fail()
+ex.expect('Error')
+ex.expect('28000:')  # 28000 bad credentials
+ex.end()
+
+ex = Execution(dbname, '-p', '')
+ex.expect_fail()
+ex.expect('Error')
+ex.expect('28000:')  # 28000 bad credentials
+ex.end()
+
+ex = Execution(dbname, '-u', '')
+ex.expect_fail()
+ex.expect('Error')
+ex.expect('28000:')  # 28000 bad credentials
+ex.end()
+
+ex = None
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Move TLSTesterClient from tlssecurity,py to t...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 61f71d59ed8d for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/61f71d59ed8d
Modified Files:
clients/mapilib/Tests/tlssecurity.py
testing/tlstester.py
Branch: Aug2024
Log Message:

Move TLSTesterClient from tlssecurity,py to testing/tlstester.py

So we can use it from other tests as well


diffs (129 lines):

diff --git a/clients/mapilib/Tests/tlssecurity.py 
b/clients/mapilib/Tests/tlssecurity.py
--- a/clients/mapilib/Tests/tlssecurity.py
+++ b/clients/mapilib/Tests/tlssecurity.py
@@ -14,9 +14,8 @@ import os
 import re
 import subprocess
 import sys
-import urllib.request
 
-from MonetDBtesting import tlstester
+from MonetDBtesting.tlstester import TLSTesterClient
 
 level = logging.WARNING
 # if sys.platform == 'win32':
@@ -32,50 +31,6 @@ assert os.path.isdir(tgtdir)
 scratchdir = os.path.join(tgtdir, "scratch")
 logging.debug(f"scratchdir={scratchdir}")
 
-class TLSTesterClient:
-"""Connect to TLSTester to figure out port numbers and download 
certificates"""
-def __init__(self, scratchdir, base_port=None, host='localhost'):
-if not base_port:
-base_port = os.environ['TST_TLSTESTERPORT']
-self.url = f'http://{host}:{base_port}/'
-self.scratch = scratchdir
-try:
-os.mkdir(scratchdir)
-except FileExistsError:
-pass
-self.filenames = dict()
-self.contents = dict()
-self.portmap = dict()
-for line in self.fetch('').splitlines():
-name, port = str(line, 'ascii').split(':', 1)
-self.portmap[name] = int(port)
-logging.debug(f'port {name} = {port}')
-
-def get_port(self, name):
-return self.portmap[name]
-
-def fetch(self, name):
-cached = self.contents.get(name)
-if cached is not None:
-return cached
-url = self.url + name
-logging.debug(f'fetch {url}')
-with urllib.request.urlopen(url) as response:
-content = response.read()
-self.contents[name] = content
-return content
-
-def download(self, name):
-cached = self.filenames.get(name)
-if cached:
-return cached
-content = self.fetch(name)
-path = os.path.join(self.scratch, name)
-with open(path, 'wb') as f:
-f.write(content)
-self.filenames[name] = path
-return path
-
 tlstester = TLSTesterClient(scratchdir)
 
 
diff --git a/testing/tlstester.py b/testing/tlstester.py
--- a/testing/tlstester.py
+++ b/testing/tlstester.py
@@ -27,6 +27,7 @@ import tempfile
 from threading import Thread
 import threading
 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
+import urllib.request
 
 # Our TLS implementation never uses anything less than TLSv1.3.
 assert ssl.HAS_TLSv1_3
@@ -98,6 +99,52 @@ argparser.add_argument(
 )
 
 
+
+class TLSTesterClient:
+"""Connect to TLSTester to figure out port numbers and download 
certificates"""
+def __init__(self, scratchdir, base_port=None, host='localhost'):
+if not base_port:
+base_port = os.environ['TST_TLSTESTERPORT']
+self.url = f'http://{host}:{base_port}/'
+self.scratch = scratchdir
+try:
+os.mkdir(scratchdir)
+except FileExistsError:
+pass
+self.filenames = dict()
+self.contents = dict()
+self.portmap = dict()
+for line in self.fetch('').splitlines():
+name, port = str(line, 'ascii').split(':', 1)
+self.portmap[name] = int(port)
+logging.debug(f'port {name} = {port}')
+
+def get_port(self, name):
+return self.portmap[name]
+
+def fetch(self, name):
+cached = self.contents.get(name)
+if cached is not None:
+return cached
+url = self.url + name
+logging.debug(f'fetch {url}')
+with urllib.request.urlopen(url) as response:
+content = response.read()
+self.contents[name] = content
+return content
+
+def download(self, name):
+cached = self.filenames.get(name)
+if cached:
+return cached
+content = self.fetch(name)
+path = os.path.join(self.scratch, name)
+with open(path, 'wb') as f:
+f.write(content)
+self.filenames[name] = path
+return path
+
+
 class Certs:
 hostnames: str
 _files: Dict[str, bytes]
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Move tlstester to testing/ directory

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: d3c75b4b83c6 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/d3c75b4b83c6
Added Files:
testing/tlstester.py
Removed Files:
clients/mapilib/Tests/tlstester.py
Modified Files:
clients/mapilib/Tests/tlssecurity.py
testing/CMakeLists.txt
Branch: Aug2024
Log Message:

Move tlstester to testing/ directory


diffs (27 lines):

diff --git a/clients/mapilib/Tests/tlssecurity.py 
b/clients/mapilib/Tests/tlssecurity.py
--- a/clients/mapilib/Tests/tlssecurity.py
+++ b/clients/mapilib/Tests/tlssecurity.py
@@ -16,8 +16,7 @@ import subprocess
 import sys
 import threading
 
-sys.path.append(os.environ.get('TSTSRCDIR','.'))
-import tlstester
+from MonetDBtesting import tlstester
 
 level = logging.WARNING
 # if sys.platform == 'win32':
diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt
--- a/testing/CMakeLists.txt
+++ b/testing/CMakeLists.txt
@@ -147,6 +147,7 @@ if(PYTHON3_LIBDIR)
 malmapi.py
 helpers.py
 sqltest.py
+tlstester.py
 utils.py
 DESTINATION ${PYTHON3_LIBDIR}/MonetDBtesting
 COMPONENT pytesting)
diff --git a/clients/mapilib/Tests/tlstester.py b/testing/tlstester.py
rename from clients/mapilib/Tests/tlstester.py
rename to testing/tlstester.py
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Use Mtest's TLSTester to test JDBC TLS

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: e0c0dbd3e852 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/e0c0dbd3e852
Added Files:
sql/jdbc/tests/Tests/TLSTester.py
Modified Files:
sql/jdbc/tests/Tests/All
Branch: Aug2024
Log Message:

Use Mtest's TLSTester to test JDBC TLS


diffs (32 lines):

diff --git a/sql/jdbc/tests/Tests/All b/sql/jdbc/tests/Tests/All
--- a/sql/jdbc/tests/Tests/All
+++ b/sql/jdbc/tests/Tests/All
@@ -1,4 +1,5 @@
 HAVE_JDBCTESTS?JDBC_API_Tester
+HAVE_JDBCTESTS?TLSTester
 HAVE_JDBCTESTS?OnClientTester
 HAVE_JDBCCLIENT_JAR?Test_JdbcClient
 # next test should be done AFTER all the other tests have completed
diff --git a/sql/jdbc/tests/Tests/TLSTester.py 
b/sql/jdbc/tests/Tests/TLSTester.py
new file mode 100644
--- /dev/null
+++ b/sql/jdbc/tests/Tests/TLSTester.py
@@ -0,0 +1,18 @@
+import os, sys
+from subprocess import run, PIPE, CalledProcessError
+
+PORT=os.environ['TST_TLSTESTERPORT']
+
+cmd = [
+'java', 'TLSTester',
+# '-v',
+f'localhost:{PORT}',
+'-a', 'localhost.localdomain'
+]
+try:
+p = run(cmd, stdout=PIPE, stderr=PIPE, check=True, encoding='utf-8')
+sys.stderr.write(p.stdout)
+sys.stderr.write(p.stderr)
+except CalledProcessError as e:
+raise SystemExit(e.stderr)
+
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Don't write certs to TSTTRGDIR, it may not ex...

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 5eb6a7727787 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/5eb6a7727787
Modified Files:
testing/Mtest.py.in
Branch: Aug2024
Log Message:

Don't write certs to TSTTRGDIR, it may not exist yet


diffs (26 lines):

diff --git a/testing/Mtest.py.in b/testing/Mtest.py.in
--- a/testing/Mtest.py.in
+++ b/testing/Mtest.py.in
@@ -3250,14 +3250,6 @@ def StartTlsTester(tsttrgdir):
 from MonetDBtesting import tlstester
 hostnames = ['localhost']
 certs = tlstester.Certs(hostnames)
-certsdir = os.path.join(tsttrgdir, "certs")
-try:
-os.mkdir(certsdir)
-except FileExistsError:
-pass
-for name, content in certs.all().items():
-with open(os.path.join(certsdir, name), "wb") as f:
-f.write(content)
 server = tlstester.TLSTester(
 certs = certs,
 listen_addr='localhost',
@@ -3267,7 +3259,6 @@ def StartTlsTester(tsttrgdir):
 server_thread = threading.Thread(target=server.serve_forever, daemon=True)
 server_thread.start()
 return server.get_port('base')
-
 ### StartTlsTester() #
 
 #
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - Add list drivers and list dns's to odbcconnect

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: 230778a77ea1 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/230778a77ea1
Modified Files:
clients/odbc/samples/odbcconnect.c
Branch: Aug2024
Log Message:

Add list drivers and list dns's to odbcconnect

Fix a small memory leak in the process


diffs (204 lines):

diff --git a/clients/odbc/samples/odbcconnect.c 
b/clients/odbc/samples/odbcconnect.c
--- a/clients/odbc/samples/odbcconnect.c
+++ b/clients/odbc/samples/odbcconnect.c
@@ -15,6 +15,8 @@
 
 #include 
 #endif
+
+#include 
 #include 
 #include 
 #include 
@@ -29,14 +31,22 @@ static const char *USAGE =
"-d  Target is DSN, call SQLConnect()\n"
"-c  Target is connection string, call 
SQLDriverConnect()\n"
"-b  Target is connection string, call 
SQLBrowseConnect()\n"
+   "-l  List registered drivers and data sources\n"
"-u USER\n"
"-p PASSWORD\n"
"-v  Be verbose\n"
"TARGET  Connection String or DSN\n";
 
-static int do_sqlconnect(SQLCHAR *target);
-static int do_sqldriverconnect(SQLCHAR *target);
-static int do_sqlbrowseconnect(SQLCHAR *target);
+typedef int (action_t)(SQLCHAR *);
+
+static int do_actions(action_t action, int ntargets, SQLCHAR **targets);
+
+static action_t do_sqlconnect;
+static action_t do_sqldriverconnect;
+static action_t do_sqlbrowseconnect;
+
+static int do_listdrivers(void);
+static int do_listdsns(const char *prefix, SQLSMALLINT dir);
 
 static void ensure_ok(SQLSMALLINT type, SQLHANDLE handle, const char *message, 
SQLRETURN ret);
 
@@ -49,6 +59,7 @@ SQLHANDLE env = NULL;
 SQLHANDLE conn = NULL;
 
 SQLCHAR outbuf[4096];
+SQLCHAR attrbuf[4096];
 
 static void
 cleanup(void)
@@ -68,7 +79,7 @@ main(int argc, char **argv)
action = do_sqlconnect;
SQLCHAR **targets = calloc(argc, sizeof(argv[0]));
int ntargets = 0;
-   int ret = 0;
+   int ret;
 
for (int i = 1; i < argc; i++) {
char *arg = argv[i];
@@ -78,6 +89,8 @@ main(int argc, char **argv)
action = do_sqldriverconnect;
else if (strcmp(arg, "-b") == 0)
action = do_sqlbrowseconnect;
+   else if (strcmp(arg, "-l") == 0)
+   action = NULL;
else if (strcmp(arg, "-u") == 0 && i + 1 < argc)
user = (SQLCHAR*)argv[++i];
else if (strcmp(arg, "-p") == 0 && i + 1 < argc)
@@ -88,15 +101,11 @@ main(int argc, char **argv)
targets[ntargets++] = (SQLCHAR*)arg;
else {
fprintf(stderr, "\nERROR: invalid argument: %s\n%s", 
arg, USAGE);
-   return 1;
+   ret = 1;
+   goto end;
}
}
 
-   if (ntargets == 0) {
-   fprintf(stderr, "\nERROR: pass at least one target\n%s", USAGE);
-   return 1;
-   }
-
ensure_ok(
SQL_HANDLE_ENV, NULL, "allocate env handle",
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env));
@@ -105,20 +114,25 @@ main(int argc, char **argv)
SQL_HANDLE_ENV, env, "set odbc version",
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, 
(SQLPOINTER)SQL_OV_ODBC3, 0));
 
-   ensure_ok(
-   SQL_HANDLE_ENV, env, "allocate conn handle",
-   SQLAllocHandle(SQL_HANDLE_DBC, env, &conn));
-
-   for (int i = 0; i < ntargets; i++) {
-   SQLCHAR *t = targets[i];
-   if (verbose)
-   printf("\nTarget: %s\n", t);
-   outbuf[0] = '\0';
-   int ret = action(t);
-   if (ret)
-   break;
+   if (action) {
+   if (ntargets == 0) {
+   fprintf(stderr, "\nERROR: pass at least one 
target\n%s", USAGE);
+   ret = 1;
+   goto end;
+   }
+   ret = do_actions(action, ntargets, targets);
+   } else {
+   if (ntargets != 0) {
+   fprintf(stderr, "\nERROR: -l does not take 
arguments\n%s", USAGE);
+   ret = 1;
+   goto end;
+   }
+   ret = do_listdrivers();
+   ret |= do_listdsns("SYSTEM", SQL_FETCH_FIRST_SYSTEM);
+   ret |= do_listdsns("SYSTEM", SQL_FETCH_FIRST_USER);
}
 
+end:
free(targets);
cleanup();
 
@@ -174,6 +188,26 @@ ensure_ok(SQLSMALLINT type, SQLHANDLE ha
 
 
 static int
+do_actions(action_t action, int ntargets, SQLCHAR **targets)
+{
+   ensure_ok(
+   SQL_HANDLE_ENV, env, "allocate conn handle",
+   SQLAllocHandle(SQL_HANDLE_DBC, env, &conn));
+
+   for (int i = 0; i < ntargets; i++) {
+   SQLCHAR *t = targets[i];
+   if (verbose)
+

MonetDB: default - Implements break for expressions list (by def...

2024-06-13 Thread stefanos mavros via checkin-list
Changeset: efd0930c1c01 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/efd0930c1c01
Modified Files:
sql/server/rel_dump.c
Branch: default
Log Message:

Implements break for expressions list (by default is off)

If you want to have it enabled change the expbrk arg of exps_print() in
rel_print_rel() to 1 for the operator that you are interested in


diffs (192 lines):

diff --git a/sql/server/rel_dump.c b/sql/server/rel_dump.c
--- a/sql/server/rel_dump.c
+++ b/sql/server/rel_dump.c
@@ -110,7 +110,7 @@ dump_sql_subtype(allocator *sa, sql_subt
return sa_strdup(sa, buf);
 }
 
-static void exps_print(mvc *sql, stream *fout, list *exps, int depth, list 
*refs, int alias, int brackets, int decorate);
+static void exps_print(mvc *sql, stream *fout, list *exps, int depth, list 
*refs, int alias, int brackets, int decorate, int expbrk);
 
 static void rel_print_rel(mvc *sql, stream  *fout, sql_rel *rel, int depth, 
list *refs, int decorate);
 
@@ -153,14 +153,14 @@ exp_print(mvc *sql, stream *fout, sql_ex
} else if (e->flag & PSM_WHILE) {
mnstr_printf(fout, "while ");
exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
-   exps_print(sql, fout, e->r, depth, refs, 0, 0, 
decorate);
+   exps_print(sql, fout, e->r, depth, refs, 0, 0, 
decorate, 0);
alias = 0;
} else if (e->flag & PSM_IF) {
mnstr_printf(fout, "if ");
exp_print(sql, fout, e->l, depth, refs, 0, 0, decorate);
-   exps_print(sql, fout, e->r, depth, refs, 0, 0, 
decorate);
+   exps_print(sql, fout, e->r, depth, refs, 0, 0, 
decorate, 0);
if (e->f)
-   exps_print(sql, fout, e->f, depth, refs, 0, 0, 
decorate);
+   exps_print(sql, fout, e->f, depth, refs, 0, 0, 
decorate, 0);
alias = 0;
} else if (e->flag & PSM_REL) {
rel_print_rel(sql, fout, e->l, depth+10, refs, 1);
@@ -209,7 +209,7 @@ exp_print(mvc *sql, stream *fout, sql_ex
mnstr_printf(fout, "\"%s\"", 
dump_escape_ident(sql->ta, vname->name));
} else if (e->f) {  /* values list */
list *l = e->f;
-   exps_print(sql, fout, l, depth, refs, 0, 0, 
decorate);
+   exps_print(sql, fout, l, depth, refs, 0, 0, 
decorate, 0);
} else { /* numbered arguments */
mnstr_printf(fout, "A%u", e->flag);
}
@@ -220,11 +220,11 @@ exp_print(mvc *sql, stream *fout, sql_ex
mnstr_printf(fout, "\"%s\".\"%s\"",
f->func->s?dump_escape_ident(sql->ta, 
f->func->s->base.name):"sys",
dump_escape_ident(sql->ta, f->func->base.name));
-   exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate);
+   exps_print(sql, fout, e->l, depth, refs, 0, 1, decorate, 0);
if (e->r) { /* list of optional lists */
list *l = e->r;
for(node *n = l->h; n; n = n->next)
-   exps_print(sql, fout, n->data, depth, refs, 0, 
1, decorate);
+   exps_print(sql, fout, n->data, depth, refs, 0, 
1, decorate, 0);
}
if (e->flag && is_compare_func(f))
mnstr_printf(fout, " %s", e->flag==1?"ANY":"ALL");
@@ -241,7 +241,7 @@ exp_print(mvc *sql, stream *fout, sql_ex
if (zero_if_empty(e))
mnstr_printf(fout, " zero if empty ");
if (e->l)
-   exps_print(sql, fout, e->l, depth, refs, 0, 1, 
decorate);
+   exps_print(sql, fout, e->l, depth, refs, 0, 1, 
decorate, 0);
else
mnstr_printf(fout, "()");
} break;
@@ -268,23 +268,23 @@ exp_print(mvc *sql, stream *fout, sql_ex
if (is_anti(e))
mnstr_printf(fout, " !");
cmp_print(sql, fout, e->flag);
-   exps_print(sql, fout, e->r, depth, refs, 0, 1, 
decorate);
+   exps_print(sql, fout, e->r, depth, refs, 0, 1, 
decorate, 0);
} else if (e->flag == cmp_or) {
-   exps_print(sql, fout, e->l, depth, refs, 0, 1, 
decorate);
+   exps_print(sql, fout, e->l, depth, refs, 0, 1, 
decorate, 0);
if (is_anti(e))
mnstr_printf(fout, " !");
cmp_print(sql, fout, e->flag);
-   exps_print(sql, fout, e->r, depth, refs, 0, 1, 
decorate);
+

MonetDB: default - When breaking list of exps with single entry ...

2024-06-13 Thread stefanos mavros via checkin-list
Changeset: 7bdc764bbe7c for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/7bdc764bbe7c
Modified Files:
sql/server/rel_dump.c
Branch: default
Log Message:

When breaking list of exps with single entry don't ident the closing bracket


diffs (19 lines):

diff --git a/sql/server/rel_dump.c b/sql/server/rel_dump.c
--- a/sql/server/rel_dump.c
+++ b/sql/server/rel_dump.c
@@ -431,12 +431,13 @@ exps_print(mvc *sql, stream *fout, list 
if (expbrk && en->next!=NULL)
print_indent(sql, fout, depth+2, decorate);
}
-   if (expbrk)
+   int multi_exps = expbrk && (list_length(exps) > 1);
+   if (multi_exps)
print_indent(sql, fout, depth+1, decorate);
if (brackets)
mnstr_printf(fout, ")");
else
-   mnstr_printf(fout, expbrk?"]":" ]");
+   mnstr_printf(fout, multi_exps?"]":" ]");
 }
 
 static int
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: default - introduced vacuum_tab/vacuum_col for variable...

2024-06-13 Thread Niels Nes via checkin-list
Changeset: 960fa2655454 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/960fa2655454
Modified Files:
clients/Tests/MAL-signatures-hge.test
clients/Tests/MAL-signatures.test
sql/backends/monet5/sql.c
sql/backends/monet5/sql.h
sql/server/sql_parser.y
sql/storage/bat/bat_storage.c
sql/storage/bat/bat_storage.h
sql/storage/sql_storage.h
sql/storage/store.c
sql/test/sysmon/vacuum/Tests/test_vacuum.SQL.py
Branch: default
Log Message:

introduced vacuum_tab/vacuum_col for variable size data types


diffs (truncated from 803 to 300 lines):

diff --git a/clients/Tests/MAL-signatures-hge.test 
b/clients/Tests/MAL-signatures-hge.test
--- a/clients/Tests/MAL-signatures-hge.test
+++ b/clients/Tests/MAL-signatures-hge.test
@@ -49805,8 +49805,13 @@ SQLstddev_pop;
 return the standard deviation population of groups
 sql
 stop_vacuum
+unsafe pattern sql.stop_vacuum(X_0:str, X_1:str):void
+SQLstr_stop_vacuum;
+stop auto vacuum
+sql
+stop_vacuum
 unsafe pattern sql.stop_vacuum(X_0:str, X_1:str, X_2:str):void
-SQLstr_column_stop_vacuum;
+SQLstr_stop_vacuum;
 stop auto vacuum
 sql
 storage
@@ -49965,8 +49970,13 @@ SYSupdate_tables;
 Procedure triggered on update of the sys._tables table
 sql
 vacuum
+unsafe pattern sql.vacuum(X_0:str, X_1:str, X_2:int):void
+SQLstr_auto_vacuum;
+auto vacuum string column of given table with interval(sec)
+sql
+vacuum
 unsafe pattern sql.vacuum(X_0:str, X_1:str, X_2:str, X_3:int):void
-SQLstr_column_auto_vacuum;
+SQLstr_auto_vacuum;
 auto vacuum string column with interval(sec)
 sql
 vacuum
diff --git a/clients/Tests/MAL-signatures.test 
b/clients/Tests/MAL-signatures.test
--- a/clients/Tests/MAL-signatures.test
+++ b/clients/Tests/MAL-signatures.test
@@ -38215,8 +38215,13 @@ SQLstddev_pop;
 return the standard deviation population of groups
 sql
 stop_vacuum
+unsafe pattern sql.stop_vacuum(X_0:str, X_1:str):void
+SQLstr_stop_vacuum;
+stop auto vacuum
+sql
+stop_vacuum
 unsafe pattern sql.stop_vacuum(X_0:str, X_1:str, X_2:str):void
-SQLstr_column_stop_vacuum;
+SQLstr_stop_vacuum;
 stop auto vacuum
 sql
 storage
@@ -38350,8 +38355,13 @@ SYSupdate_tables;
 Procedure triggered on update of the sys._tables table
 sql
 vacuum
+unsafe pattern sql.vacuum(X_0:str, X_1:str, X_2:int):void
+SQLstr_auto_vacuum;
+auto vacuum string column of given table with interval(sec)
+sql
+vacuum
 unsafe pattern sql.vacuum(X_0:str, X_1:str, X_2:str, X_3:int):void
-SQLstr_column_auto_vacuum;
+SQLstr_auto_vacuum;
 auto vacuum string column with interval(sec)
 sql
 vacuum
diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c
--- a/sql/backends/monet5/sql.c
+++ b/sql/backends/monet5/sql.c
@@ -4928,40 +4928,40 @@ finalize:
 }
 
 static str
-do_str_column_vacuum(sql_trans *tr, sql_column *c, char *sname, char *tname, 
char *cname)
+do_str_column_vacuum(sql_trans *tr, sql_column *c)
 {
-   int res;
-   int access = 0;
-   BAT* b = NULL;
-   BAT* bn = NULL;
-   sqlstore *store = tr->store;
-
-   if ((b = store->storage_api.bind_col(tr, c, access)) == NULL)
-   throw(SQL, "do_str_column_vacuum", SQLSTATE(42S22) 
"storage_api.bind_col failed for %s.%s.%s", sname, tname, cname);
-   // vacuum varsized bats
if (ATOMvarsized(c->type.type->localtype)) {
-   // TODO check for num of updates on the BAT against some 
threshold
-   // and decide whether to proceed
-   if ((bn = COLcopy(b, b->ttype, true, PERSISTENT)) == NULL) {
-   BBPunfix(b->batCacheid);
-   throw(SQL, "do_str_column_vacuum", SQLSTATE(42S22) 
"COLcopy failed for %s.%s.%s", sname, tname, cname);
+   int res = 0;
+   sqlstore *store = tr->store;
+
+   if ((res = (int) store->storage_api.vacuum_col(tr, c)) != 
LOG_OK) {
+   if (res == LOG_CONFLICT)
+   throw(SQL, "do_str_column_vacuum", 
SQLSTATE(25S01) "TRANSACTION CONFLICT in storage_api.vacuum_col %s.%s.%s", 
c->t->s->base.name, c->t->base.name, c->base.name);
+   if (res == LOG_ERR)
+   throw(SQL, "do_str_column_vacuum", 
SQLSTATE(HY000) "LOG ERROR in storage_api.vacuum_col %s.%s.%s", 
c->t->s->base.name, c->t->base.name, c->base.name);
+   throw(SQL, "do_str_column_vacuum", SQLSTATE(HY000) 
"ERROR in storage_api.vacuum_col %s.%s.%s", c->t->s->base.name, 
c->t->base.name, c->base.name);
}
-   if ((res = (int) store->storage_api.swap_bats(tr, c, bn)) != 
LOG_OK) {
-   BBPreclaim(bn);
-   BBPunfix(b->batCacheid);
-   if (res == LOG_CONFLICT)
-   throw(SQL, "do_str_column_vacuum", 
SQLSTATE(25S01) "TRANSACTION CONFLICT in storage_api.swap_bats %s.%s.%s", 
sname, tname, cname);
-   if (res == LOG_ERR)
-   

MonetDB: default - add missing sql interfaces

2024-06-13 Thread Niels Nes via checkin-list
Changeset: d74cd175f7f1 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/d74cd175f7f1
Modified Files:
sql/scripts/26_sysmon.sql
Branch: default
Log Message:

add missing sql interfaces


diffs (12 lines):

diff --git a/sql/scripts/26_sysmon.sql b/sql/scripts/26_sysmon.sql
--- a/sql/scripts/26_sysmon.sql
+++ b/sql/scripts/26_sysmon.sql
@@ -79,3 +79,8 @@ create procedure sys.vacuum(sname string
 external name sql.vacuum;
 create procedure sys.stop_vacuum(sname string, tname string, cname string)
 external name sql.stop_vacuum;
+
+create procedure sys.vacuum(sname string, tname string, interval int)
+external name sql.vacuum;
+create procedure sys.stop_vacuum(sname string, tname string)
+external name sql.stop_vacuum;
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: default - add direct interface for table vacuum

2024-06-13 Thread Niels Nes via checkin-list
Changeset: 2af293dc3d14 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/2af293dc3d14
Modified Files:
clients/Tests/MAL-signatures-hge.test
sql/backends/monet5/sql.c
sql/scripts/26_sysmon.sql
Branch: default
Log Message:

add direct interface for table vacuum


diffs (108 lines):

diff --git a/clients/Tests/MAL-signatures-hge.test 
b/clients/Tests/MAL-signatures-hge.test
--- a/clients/Tests/MAL-signatures-hge.test
+++ b/clients/Tests/MAL-signatures-hge.test
@@ -49980,8 +49980,13 @@ SQLstr_auto_vacuum;
 auto vacuum string column with interval(sec)
 sql
 vacuum
+unsafe pattern sql.vacuum(X_0:str, X_1:str):void
+SQLstr_vacuum;
+vacuum a string column
+sql
+vacuum
 unsafe pattern sql.vacuum(X_0:str, X_1:str, X_2:str):void
-SQLstr_column_vacuum;
+SQLstr_vacuum;
 vacuum a string column
 sql
 variance
diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c
--- a/sql/backends/monet5/sql.c
+++ b/sql/backends/monet5/sql.c
@@ -4962,13 +4962,15 @@ do_str_table_vacuum(sql_trans *tr, sql_t
 }
 
 static str
-SQLstr_column_vacuum(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
+SQLstr_vacuum(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
 {
mvc *m = NULL;
str msg = NULL;
char *sname = *getArgReference_str(stk, pci, 1);
char *tname = *getArgReference_str(stk, pci, 2);
-   char *cname = *getArgReference_str(stk, pci, 3);
+   char *cname = NULL;
+   if (pci->argc == 4)
+   cname = *getArgReference_str(stk, pci, 3);
 
if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != NULL)
return msg;
@@ -4981,26 +4983,31 @@ SQLstr_column_vacuum(Client cntxt, MalBl
sql_column *c = NULL;
 
if (strNil(sname))
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "Schema 
name cannot be NULL");
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "Schema name 
cannot be NULL");
if (strNil(tname))
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "Table name 
cannot be NULL");
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "Table name cannot 
be NULL");
if (strNil(cname))
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "Column 
name cannot be NULL");
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "Column name 
cannot be NULL");
if ((s = mvc_bind_schema(m, sname)) == NULL)
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(3F000) "Invalid or 
missing schema %s",sname);
+   throw(SQL, "sql.str_vacuum", SQLSTATE(3F000) "Invalid or 
missing schema %s",sname);
if ((t = mvc_bind_table(m, s, tname)) == NULL)
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42S02) "Invalid or 
missing table %s.%s",sname,tname);
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42S02) "Invalid or 
missing table %s.%s",sname,tname);
if (!isTable(t))
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "%s '%s' is 
not persistent",
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "%s '%s' is not 
persistent",
  TABLE_TYPE_DESCRIPTION(t->type, t->properties), 
t->base.name);
if (isTempTable(t))
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "Cannot 
vacuum column from temporary table");
-   if ((c = mvc_bind_column(m, t, cname)) == NULL)
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42S22) "Column not 
found in %s.%s.%s",sname,tname,cname);
-   if (c->storage_type)
-   throw(SQL, "sql.str_column_vacuum", SQLSTATE(42000) "Cannot 
vacuum compressed column");
-
-   return do_str_column_vacuum(tr, c);
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "Cannot vacuum 
column from temporary table");
+   if (cname) {
+   if ((c = mvc_bind_column(m, t, cname)) == NULL)
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42S22) "Column 
not found in %s.%s.%s",sname,tname,cname);
+   if (c->storage_type)
+   throw(SQL, "sql.str_vacuum", SQLSTATE(42000) "Cannot 
vacuum compressed column");
+   }
+
+   if (c)
+   return do_str_column_vacuum(tr, c);
+   else
+   return do_str_table_vacuum(tr, t);
 }
 
 
@@ -6223,9 +6230,10 @@ static mel_func sql_init_funcs[] = {
  pattern("sql", "corr", SQLcorr, false, "return the correlation value of 
groups", args(1,8, 
arg("",dbl),arg("b",hge),arg("c",hge),arg("p",bit),arg("o",bit),arg("t",int),arg("s",oid),arg("e",oid))),
  pattern("batsql", "corr", SQLcorr, false, "return the correlation value of 
groups", args(1,8, 
batarg("",dbl),optbatarg("b",hge),optbatarg("c",hge),optbatarg("p",bit),optbatarg("o",bit),arg("t",int),optbatarg("s",oid),optbatarg("e",oid))),
 #endif
- pattern("sql", "vacuum", SQLstr_column_vacuum, true, "vacuum a string 
column", args(0,3, arg("sname",str),arg("tname",str),arg("cname",str))),
+ p

MonetDB: Dec2023 - small cleanup/comment added

2024-06-13 Thread Niels Nes via checkin-list
Changeset: 9b75c288d86c for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/9b75c288d86c
Modified Files:
sql/storage/store.c
Branch: Dec2023
Log Message:

small cleanup/comment added


diffs (20 lines):

diff --git a/sql/storage/store.c b/sql/storage/store.c
--- a/sql/storage/store.c
+++ b/sql/storage/store.c
@@ -6286,6 +6286,7 @@ sql_trans_alter_null(sql_trans *tr, sql_
 
if ((res = new_column(tr, col, &dup)))
return res;
+   col = dup;
dup->null = isnull;
 
/* disallow concurrent updates on the column if not null is set 
*/
@@ -6296,7 +6297,7 @@ sql_trans_alter_null(sql_trans *tr, sql_
return res;
if ((res = store_reset_sql_functions(tr, col->t->base.id))) /* 
reset sql functions depending on the table */
return res;
-   if (isNew(col) || isnull)
+   if (isNew(col) || isnull) /* new ie can still change, or 
persistent only widen semantics */
store->storage_api.col_not_null(tr, col, !isnull);
}
return res;
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - merged with dec2023

2024-06-13 Thread Niels Nes via checkin-list
Changeset: 50a123e080b0 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/50a123e080b0
Modified Files:
gdk/gdk.h
gdk/gdk_utils.c
monetdb5/modules/mal/txtsim.c
sql/storage/store.c
Branch: Aug2024
Log Message:

merged with dec2023


diffs (120 lines):

diff --git a/clients/odbc/driver/ODBCError.c b/clients/odbc/driver/ODBCError.c
--- a/clients/odbc/driver/ODBCError.c
+++ b/clients/odbc/driver/ODBCError.c
@@ -373,27 +373,6 @@ appendODBCError(ODBCError **head, ODBCEr
 }
 
 
-#if 0  /* unused */
-/*
- * Prepends a valid ODBCError object 'err' to the front of the list
- * of a valid ODBCError object 'head' and return the new head.
- *
- * Precondition: both head and err must be valid (non NULL)
- * Returns: the new head (which is the same as the prepended 'err').
- */
-void
-prependODBCError(ODBCError **head, ODBCError *err)
-{
-   assert(head);
-   assert(err);
-   assert(err->next == NULL);
-
-   err->next = *head;
-   *head = err;
-}
-#endif
-
-
 /*
  * Frees the ODBCError object including its linked ODBCError objects.
  *
diff --git a/clients/odbc/driver/ODBCError.h b/clients/odbc/driver/ODBCError.h
--- a/clients/odbc/driver/ODBCError.h
+++ b/clients/odbc/driver/ODBCError.h
@@ -110,18 +110,6 @@ int getErrorRecCount(ODBCError *error);
 void appendODBCError(ODBCError **head, ODBCError *err);
 
 
-#if 0  /* unused */
-/*
- * Prepends a valid ODBCError object 'this' to the front of the list
- * of a valid ODBCError object 'head' and return the new head.
- *
- * Precondition: both head and this must be valid (non NULL)
- * Returns: the new head (which is the same as the prepended 'this').
- */
-void prependODBCError(ODBCError **head, ODBCError *err);
-#endif
-
-
 /*
  * Frees the ODBCError object including its linked ODBCError objects.
  *
diff --git a/gdk/gdk.h b/gdk/gdk.h
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -1087,7 +1087,7 @@ bat_iterator_nolock(BAT *b)
 {
/* does not get matched by bat_iterator_end */
if (b) {
-   bool isview = VIEWtparent(b);
+   const bool isview = VIEWtparent(b) != 0;
return (BATiter) {
.b = b,
.h = b->theap,
diff --git a/gdk/gdk_utils.c b/gdk/gdk_utils.c
--- a/gdk/gdk_utils.c
+++ b/gdk/gdk_utils.c
@@ -378,19 +378,7 @@ GDKlog(FILE *lockFile, const char *forma
 
 /*
  * @+ Interrupt handling
- * The current version simply catches signals and prints a warning.
- * It should be extended to cope with the specifics of the interrupt
- * received.
  */
-#if 0  /* these are unused */
-static void
-BATSIGignore(int nr)
-{
-   (void) nr;
-   GDKsyserror("! ERROR signal %d caught by thread %zu\n", nr, (size_t) 
MT_getpid());
-}
-#endif
-
 #ifdef WIN32
 static void
 BATSIGabort(int nr)
diff --git a/monetdb5/modules/mal/txtsim.c b/monetdb5/modules/mal/txtsim.c
--- a/monetdb5/modules/mal/txtsim.c
+++ b/monetdb5/modules/mal/txtsim.c
@@ -247,7 +247,7 @@ levenshtein(int *res, const char *x, con
throw(MAL, "txtsim.levenshtein", "Illegal unicode code point");
 }
 
-/* Levenshtein OP but with column externaly allocated */
+/* Levenshtein OP but with column externally allocated */
 static inline int
 levenshtein2(const char *x, const char *y, const size_t xlen, const size_t 
ylen,
 unsigned int *column, const int insdel_cost,
diff --git a/sql/storage/store.c b/sql/storage/store.c
--- a/sql/storage/store.c
+++ b/sql/storage/store.c
@@ -6330,6 +6330,7 @@ sql_trans_alter_null(sql_trans *tr, sql_
 
if ((res = new_column(tr, col, &dup)))
return res;
+   col = dup;
dup->null = isnull;
 
/* disallow concurrent updates on the column if not null is set 
*/
@@ -6340,7 +6341,7 @@ sql_trans_alter_null(sql_trans *tr, sql_
return res;
if ((res = store_reset_sql_functions(tr, col->t->base.id))) /* 
reset sql functions depending on the table */
return res;
-   if (isNew(col) || isnull)
+   if (isNew(col) || isnull) /* new ie can still change, or 
persistent only widen semantics */
store->storage_api.col_not_null(tr, col, !isnull);
}
return res;
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org


MonetDB: Aug2024 - fall back to type STRING on only NULL's

2024-06-13 Thread Niels Nes via checkin-list
Changeset: 6b8fe0152645 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/6b8fe0152645
Modified Files:
sql/backends/monet5/vaults/csv/csv.c
Branch: Aug2024
Log Message:

fall back to type STRING on only NULL's


diffs (16 lines):

diff --git a/sql/backends/monet5/vaults/csv/csv.c 
b/sql/backends/monet5/vaults/csv/csv.c
--- a/sql/backends/monet5/vaults/csv/csv.c
+++ b/sql/backends/monet5/vaults/csv/csv.c
@@ -344,6 +344,12 @@ detect_types(const char *buf, char delim
types = ntypes;
nr_lines++;
}
+   if (types) { /* NULL -> STRING */
+   for(int i = 0; i

MonetDB: Aug2024 - Remove fancy type signature

2024-06-13 Thread Joeri van Ruth via checkin-list
Changeset: f66c54d25182 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/f66c54d25182
Modified Files:
testing/tlstester.py
Branch: Aug2024
Log Message:

Remove fancy type signature

Old Python versions don't support it


diffs (12 lines):

diff --git a/testing/tlstester.py b/testing/tlstester.py
--- a/testing/tlstester.py
+++ b/testing/tlstester.py
@@ -542,7 +542,7 @@ class WebHandler(http.server.BaseHTTPReq
 self.end_headers()
 self.wfile.write(content)
 
-def log_request(self, code: int | str = "-", size: int | str = "-") -> 
None:
+def log_request(self, code = "-", size = "-"):
 # be silent
 pass
 
___
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org