<commit_before>/*
 * The contents of this file are subject to the MonetDB Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.monetdb.org/Legal/MonetDBLicense
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is the MonetDB Database System.
 *
 * The Initial Developer of the Original Code is CWI.
 * Portions created by CWI are Copyright (C) 1997-July 2008 CWI.
 * Copyright August 2008-2011 MonetDB B.V.
 * All Rights Reserved.
 */

/*
 * @f sql_scenario
 * @t SQL catwalk management
 * @a <NAME>, <NAME>
 * @+ SQL scenario
 * The SQL scenario implementation is a derivative of the MAL session scenario.
 *
 * It is also the first version that uses state records attached to
 * the client record. They are initialized as part of the initialization
 * phase of the scenario.
 *
 */
/*
 * @+ Scenario routines
 * Before we are can process SQL statements the global catalog
 * should be initialized. Thereafter, each time a client enters
 * we update its context descriptor to denote an SQL scenario.
 */
#include "monetdb_config.h"
#include "sql_scenario.h"
#include "sql_result.h"
#include "sql_gencode.h"
#include "sql_optimizer.h"
#include "sql_env.h"
#include "sql_mvc.h"
#include "sql_readline.h"
#include "sql_user.h"
#include "sql_datetime.h"
#include "mal_io.h"
#include "mal_parser.h"
#include "mal_builder.h"
#include "mal_namespace.h"
#include "mal_sabaoth.h"
#include "mal_debugger.h"
#include "mal_linker.h"
#include "bat5.h"
#include <mtime.h>
#include "optimizer.h"
#include "opt_statistics.h"
#include "opt_prelude.h"
#include <unistd.h>

static int SQLinitialized = 0;
static int SQLnewcatalog = 0;
static int SQLdebug = 0;
static char *sqlinit = NULL;
MT_Lock sql_contextLock;

static void
monet5_freestack(int clientid, backend_stack stk)
{
	MalStkPtr p = (ptr)stk;

	(void)clientid;
	if (p != NULL)
		freeStack(p);
#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#monet5_freestack\n");
#endif
}

static void
monet5_freecode(int clientid, backend_code code, backend_stack stk, int nr, char *name)
{
	(void) code;
	(void) stk;
	(void) nr;
	(void)clientid;
	SQLCacheRemove(MCgetClient(clientid), name);

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#monet5_free:%d\n", nr);
#endif
}

str
SQLsession(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
{
	str *ret = (str*) getArgReference(stk, pci, 0);
	str msg = MAL_SUCCEED;

	(void)mb;
	if (SQLinitialized == 0 )
		SQLprelude();
	msg = setScenario(cntxt, "sql");
	*ret = 0;
	return msg;
}

str
SQLsession2(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
{
	str *ret = (str*) getArgReference(stk, pci, 0);
	str msg = MAL_SUCCEED;

	(void)mb;
	if (SQLinitialized == 0 )
		SQLprelude();
	msg = setScenario(cntxt, "msql");
	*ret = 0;
	return msg;
}

static str SQLinit(void);

str
SQLprelude(void)
{
	int res = 0;
	str tmp;
	Scenario ms, s = getFreeScenario();
	if (!s)
		throw(MAL, "sql.start", "out of scenario slots");
	sqlinit = GDKgetenv("sqlinit");
	s->name = "S_Q_L";
	s->language = "sql";
	s->initSystem = NULL;
	s->exitSystem = "SQLexit";
	s->initClient = "SQLinitClient";
	s->exitClient = "SQLexitClient";
	s->reader = "SQLreader";
	s->parser = "SQLparser";
	s->engine = "SQLengine";
	if (GDKembedded) {
		tmp = SQLinit();
		if (tmp == MAL_SUCCEED)
			s->name = "sql";
		return tmp;
	}

	ms = getFreeScenario();
	if (!ms)
		throw(MAL, "sql.start", "out of scenario slots");

	ms->name = "M_S_Q_L";
	ms->language = "msql";
	ms->initSystem = NULL;
	ms->exitSystem = "SQLexit";
	ms->initClient = "SQLinitClient";
	ms->exitClient = "SQLexitClient";
	ms->reader = "MALreader";
	ms->parser = "MALparser";
	ms->optimizer = "MALoptimizer";
	/* ms->tactics = .. */
	ms->engine = "MALengine";
	fprintf(stdout, "# MonetDB/SQL module loaded\n");
	fflush(stdout); /* make merovingian see this *now* */
	tmp = SQLinit();
	if (tmp != MAL_SUCCEED)
		return(tmp);

	/* only register availability of scenarios AFTER we are inited! */
	s->name = "sql";
	tmp = SABAOTHmarchScenario(&res, &s->name);
	if (tmp != MAL_SUCCEED)
		return(tmp);
	ms->name = "msql";
	tmp = SABAOTHmarchScenario(&res, &ms->name);
	return tmp;
}

str
SQLepilogue(void)
{
	int res = 0;
	char *s = "sql";

	if( SQLinitialized){
		/*
		mvc_exit();
		SQLinitialized= FALSE;
		*/
	}
	/* this function is never called, but for the style of it, we clean
	 * up our own mess */
	if (!GDKembedded)
		return SABAOTHretreatScenario(&res, &s);
	return MAL_SUCCEED;
}

MT_Id sqllogthread, minmaxthread;

static str
SQLinit(void)
{
	char *debug_str = GDKgetenv("sql_debug");
	int readonly  = GDKgetenv_isyes("gdk_readonly");
	int single_user = GDKgetenv_isyes("gdk_single_user");
	char *gmt = "GMT", *dbname = NULL;
	tzone tz;

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLinit Monet 5\n");
#endif
	if (SQLinitialized)
		return MAL_SUCCEED;

	MT_lock_init( &sql_contextLock, "sql_contextLock");

	mal_set_lock(sql_contextLock,"SQL init");
	memset((char*)&be_funcs, 0, sizeof(backend_functions));
	be_funcs.fstack		= &monet5_freestack;
	be_funcs.fcode		= &monet5_freecode;
	be_funcs.fcreate_table_function	= &monet5_create_table_function;
	be_funcs.fresolve_function	= &monet5_resolve_function;
	monet5_user_init(&be_funcs);

	MTIMEtimezone(&tz, &gmt);
	(void) tz;
	if (debug_str)
		SQLdebug = strtol(debug_str,NULL,10);
	if (single_user)
		SQLdebug |= 64;
	if (readonly)
		SQLdebug |= 32;
	dbname = GDKgetenv("gdk_dbname");
	if (((SQLdebug&96)==96 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_suro, 0)) < 0) ||
			((SQLdebug&96)==64 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_su, 0)) < 0) ||
			((SQLdebug&96)==32 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_ro, 0)) < 0) ||
			((SQLdebug&112)==0 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_bat, 0)) < 0))
		throw(SQL, "SQLinit", "Catalogue initialization failed");
	SQLinitialized = TRUE;
	mal_unset_lock(sql_contextLock,"SQL init");
	if (!GDKembedded && MT_create_thread(&sqllogthread, (void (*)(void *)) mvc_logmanager, NULL, MT_THR_DETACHED) != 0) {
		mal_unset_lock(sql_contextLock,"SQL init");
		throw(SQL, "SQLinit", "Starting log manager failed");
	}
	if (0 && !GDKembedded && MT_create_thread(&minmaxthread, (void (*)(void *)) mvc_minmaxmanager, NULL, MT_THR_DETACHED) != 0) {
		mal_unset_lock(sql_contextLock,"SQL init");
		throw(SQL, "SQLinit", "Starting minmax manager failed");
	}
	return MAL_SUCCEED;
}

str
SQLexit(Client c)
{
#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLexit\n");
#endif
	(void) c;		/* not used */
	if( SQLinitialized == FALSE)
		throw(SQL, "SQLexit", "Catalogue not available");
	return MAL_SUCCEED;
}

#define SQLglobal(name, val) \
	stack_push_var(sql, name, &ctype);	   \
	stack_set_var(sql, name, VALset(&src, ctype.type->localtype, val));

#define NR_GLOBAL_VARS 9
/* NR_GLOBAL_VAR should match exactly the number of variables created
   in global_variables */
/* initialize the global variable, ie make mvc point to these */
static int
global_variables(mvc *sql, char *user, char *schema)
{
	sql_subtype ctype;
	char *typename;
	lng sec = 0;
	bit T = TRUE;
	bit F = FALSE;
	ValRecord src;
	str optimizer;

 	typename = "int";
	sql_find_subtype(&ctype, typename, 0, 0);
	SQLglobal("debug", &sql->debug);

	typename = "varchar";
	sql_find_subtype(&ctype, typename, 1024, 0);
	SQLglobal("current_schema", schema);
	SQLglobal("current_user", user);
	SQLglobal("current_role", user);
	/* inherit the optimizer from the server */
	if (optimizerpipe )
		optimizer= optimizerpipe;
	else
		optimizer= GDKgetenv("sql_optimizer");
	if (optimizer == NULL)
		optimizer= "default_pipe";
	SQLglobal("optimizer", setOptimizers(optimizer));
	SQLglobal("trace","show,ticks,stmt");

	typename = "sec_interval";
	sql_find_subtype(&ctype, typename, inttype2digits(ihour, isec), 0);
	SQLglobal("current_timezone", &sec);

	typename = "boolean";
	sql_find_subtype(&ctype, typename, 0, 0);
	SQLglobal("cache", &T);
	SQLglobal("history", &F);

	return 0;
}

static int
error(stream *out, char *str)
{
	char *p;

	if (!out)
		out = GDKerr;

	if (str == NULL)
		return 0;

	if (mnstr_errnr(out))
		return -1;
	while ((p = strchr(str, '\n')) != NULL) {
		p++;        /* include newline */
		if (*str != '!' && mnstr_write(out, "!", 1, 1) != 1)
			return -1;
		if (mnstr_write(out, str, p - str, 1) != 1)
			return -1;
		str = p;
	}
	if (str && *str) {
		if (*str != '!' && mnstr_write(out, "!", 1, 1) != 1)
			return -1;
		if (mnstr_write(out, str, strlen(str), 1) != 1 || mnstr_write(out, "\n", 1, 1) != 1)
			return -1;
	}
	return 0;
}

#define TRANS_ABORTED "!current transaction is aborted (please ROLLBACK)\n"

static int
handle_error(mvc *m, stream *out, int pstatus)
{
	int go = 1;
	char *buf = GDKerrbuf;

	/* transaction already broken */
	if (m->type != Q_TRANS && pstatus < 0) {
		if (mnstr_write(out, TRANS_ABORTED, sizeof(TRANS_ABORTED) - 1, 1) != 1) {
			go = !go;
		}
	} else {
		if (error(out, m->errstr) < 0 || (buf && buf[0] && error(out, buf) < 0)) {
			go = !go;
		}
	}
	/* reset error buffers */
	m->errstr[0] = 0;
	if (buf)
		buf[0] = 0;
	return go;
}


str
SQLinitClient(Client c)
{
	mvc *m;
	str schema;
	str msg = MAL_SUCCEED;
	backend *be;
	bstream *bfd = NULL;
	stream *fd = NULL;

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLinitClient\n");
#endif
	if (SQLinitialized == 0 )
		SQLprelude();
	/*
	 * @-
	 * Based on the initialization retun value we can prepare a SQLinit string with all information needed
	 * to initialize the catalog based on the mandatory scripts to be executed.
	 */
	if (sqlinit) { /* add sqlinit to the fdin stack */
		buffer *b = (buffer*)GDKmalloc(sizeof(buffer));
		size_t len = strlen(sqlinit);
		bstream *fdin;

		buffer_init(b, _strdup(sqlinit), len);
		fdin = bstream_create(buffer_rastream(b, "si"), b->len);
		bstream_next(fdin);
		MCpushClientInput(c, fdin, 0, "");
	}
	if (c->state[MAL_SCENARIO_PARSER] == 0) {
		m = mvc_create(c->idx, 0, SQLdebug, c->fdin, c->fdout);
		global_variables(m, "monetdb", "sys");
		if (isAdministrator(c))  /* console should return everything */
			m->reply_size = -1;
		be = (void *) backend_create(m, c);
	} else {
		m = c->state[MAL_SCENARIO_OPTIMIZE];
		be = c->state[MAL_SCENARIO_PARSER];
		mvc_reset(m, c->fdin, c->fdout, SQLdebug, NR_GLOBAL_VARS);
		backend_reset(be);
	}
	/* pass through credentials of the user if not console */
	schema = monet5_user_get_def_schema(m, c->user);
	if (!schema) {
		_DELETE(schema);
		throw(PERMD, "SQLinitClient", "schema authorization error");
	}
	_DELETE(schema);

	/*expect SQL text first */
	be->language = 'S';
	/* Set state, this indicates an initialized client scenario */
	c->state[MAL_SCENARIO_READER] = c;
	c->state[MAL_SCENARIO_PARSER] = be;
	c->state[MAL_SCENARIO_OPTIMIZE] = m;

	initSQLreferences();
	/* initialize the database with predefined SQL functions */
	if (SQLnewcatalog > 0) {
		char path[PATHLENGTH];
		str fullname;

		SQLnewcatalog = 0;
		snprintf(path, PATHLENGTH, "createdb");
		slash_2_dir_sep(path);
		fullname = MSP_locate_sqlscript(path, 1);
		if (fullname) {
			str filename = fullname;<commit_msg>Fixed a bug where SQL thought a function should exist and MAL thought not. When a SQL client connects, the server allocated a MAL-level client record which may reuse part of a client record that an earlier client had used.  In particular, the client record contains MAL functions that the SQL engine had created so that SQL can reuse those functions.  (That is in particular, c->nspace->subscope.) In addition, a SQL-level client record is allocated which may also reuse part of a client record from an earlier client.  This data contains the information used by the SQL engine to determine whether it has to recreate MAL functions.  This is the data in m->session->tr. Unfortunately, the two client records are not in any way linked, although the idea behind the reuse was that they were. This means that if you fire lots of clients off at the server which all do a very simple query (such as call a SQL function) and then disconnect, it may happen that a new client inherits the SQL client record (which thinks the MAL function implementating the SQL function still exists) but get a fresh MAL client record (where the MAL function does not exist).  If this happens, the execution of the query will result in an error about the MAL function not being found.<commit_after>/*
 * The contents of this file are subject to the MonetDB Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.monetdb.org/Legal/MonetDBLicense
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is the MonetDB Database System.
 *
 * The Initial Developer of the Original Code is CWI.
 * Portions created by CWI are Copyright (C) 1997-July 2008 CWI.
 * Copyright August 2008-2011 MonetDB B.V.
 * All Rights Reserved.
 */

/*
 * @f sql_scenario
 * @t SQL catwalk management
 * @a <NAME>, <NAME>
 * @+ SQL scenario
 * The SQL scenario implementation is a derivative of the MAL session scenario.
 *
 * It is also the first version that uses state records attached to
 * the client record. They are initialized as part of the initialization
 * phase of the scenario.
 *
 */
/*
 * @+ Scenario routines
 * Before we are can process SQL statements the global catalog
 * should be initialized. Thereafter, each time a client enters
 * we update its context descriptor to denote an SQL scenario.
 */
#include "monetdb_config.h"
#include "sql_scenario.h"
#include "sql_result.h"
#include "sql_gencode.h"
#include "sql_optimizer.h"
#include "sql_env.h"
#include "sql_mvc.h"
#include "sql_readline.h"
#include "sql_user.h"
#include "sql_datetime.h"
#include "mal_io.h"
#include "mal_parser.h"
#include "mal_builder.h"
#include "mal_namespace.h"
#include "mal_sabaoth.h"
#include "mal_debugger.h"
#include "mal_linker.h"
#include "bat5.h"
#include <mtime.h>
#include "optimizer.h"
#include "opt_statistics.h"
#include "opt_prelude.h"
#include <unistd.h>

static int SQLinitialized = 0;
static int SQLnewcatalog = 0;
static int SQLdebug = 0;
static char *sqlinit = NULL;
MT_Lock sql_contextLock;

static void
monet5_freestack(int clientid, backend_stack stk)
{
	MalStkPtr p = (ptr)stk;

	(void)clientid;
	if (p != NULL)
		freeStack(p);
#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#monet5_freestack\n");
#endif
}

static void
monet5_freecode(int clientid, backend_code code, backend_stack stk, int nr, char *name)
{
	(void) code;
	(void) stk;
	(void) nr;
	(void)clientid;
	SQLCacheRemove(MCgetClient(clientid), name);

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#monet5_free:%d\n", nr);
#endif
}

str
SQLsession(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
{
	str *ret = (str*) getArgReference(stk, pci, 0);
	str msg = MAL_SUCCEED;

	(void)mb;
	if (SQLinitialized == 0 )
		SQLprelude();
	msg = setScenario(cntxt, "sql");
	*ret = 0;
	return msg;
}

str
SQLsession2(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
{
	str *ret = (str*) getArgReference(stk, pci, 0);
	str msg = MAL_SUCCEED;

	(void)mb;
	if (SQLinitialized == 0 )
		SQLprelude();
	msg = setScenario(cntxt, "msql");
	*ret = 0;
	return msg;
}

static str SQLinit(void);

str
SQLprelude(void)
{
	int res = 0;
	str tmp;
	Scenario ms, s = getFreeScenario();
	if (!s)
		throw(MAL, "sql.start", "out of scenario slots");
	sqlinit = GDKgetenv("sqlinit");
	s->name = "S_Q_L";
	s->language = "sql";
	s->initSystem = NULL;
	s->exitSystem = "SQLexit";
	s->initClient = "SQLinitClient";
	s->exitClient = "SQLexitClient";
	s->reader = "SQLreader";
	s->parser = "SQLparser";
	s->engine = "SQLengine";
	if (GDKembedded) {
		tmp = SQLinit();
		if (tmp == MAL_SUCCEED)
			s->name = "sql";
		return tmp;
	}

	ms = getFreeScenario();
	if (!ms)
		throw(MAL, "sql.start", "out of scenario slots");

	ms->name = "M_S_Q_L";
	ms->language = "msql";
	ms->initSystem = NULL;
	ms->exitSystem = "SQLexit";
	ms->initClient = "SQLinitClient";
	ms->exitClient = "SQLexitClient";
	ms->reader = "MALreader";
	ms->parser = "MALparser";
	ms->optimizer = "MALoptimizer";
	/* ms->tactics = .. */
	ms->engine = "MALengine";
	fprintf(stdout, "# MonetDB/SQL module loaded\n");
	fflush(stdout); /* make merovingian see this *now* */
	tmp = SQLinit();
	if (tmp != MAL_SUCCEED)
		return(tmp);

	/* only register availability of scenarios AFTER we are inited! */
	s->name = "sql";
	tmp = SABAOTHmarchScenario(&res, &s->name);
	if (tmp != MAL_SUCCEED)
		return(tmp);
	ms->name = "msql";
	tmp = SABAOTHmarchScenario(&res, &ms->name);
	return tmp;
}

str
SQLepilogue(void)
{
	int res = 0;
	char *s = "sql";

	if( SQLinitialized){
		/*
		mvc_exit();
		SQLinitialized= FALSE;
		*/
	}
	/* this function is never called, but for the style of it, we clean
	 * up our own mess */
	if (!GDKembedded)
		return SABAOTHretreatScenario(&res, &s);
	return MAL_SUCCEED;
}

MT_Id sqllogthread, minmaxthread;

static str
SQLinit(void)
{
	char *debug_str = GDKgetenv("sql_debug");
	int readonly  = GDKgetenv_isyes("gdk_readonly");
	int single_user = GDKgetenv_isyes("gdk_single_user");
	char *gmt = "GMT", *dbname = NULL;
	tzone tz;

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLinit Monet 5\n");
#endif
	if (SQLinitialized)
		return MAL_SUCCEED;

	MT_lock_init( &sql_contextLock, "sql_contextLock");

	mal_set_lock(sql_contextLock,"SQL init");
	memset((char*)&be_funcs, 0, sizeof(backend_functions));
	be_funcs.fstack		= &monet5_freestack;
	be_funcs.fcode		= &monet5_freecode;
	be_funcs.fcreate_table_function	= &monet5_create_table_function;
	be_funcs.fresolve_function	= &monet5_resolve_function;
	monet5_user_init(&be_funcs);

	MTIMEtimezone(&tz, &gmt);
	(void) tz;
	if (debug_str)
		SQLdebug = strtol(debug_str,NULL,10);
	if (single_user)
		SQLdebug |= 64;
	if (readonly)
		SQLdebug |= 32;
	dbname = GDKgetenv("gdk_dbname");
	if (((SQLdebug&96)==96 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_suro, 0)) < 0) ||
			((SQLdebug&96)==64 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_su, 0)) < 0) ||
			((SQLdebug&96)==32 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_ro, 0)) < 0) ||
			((SQLdebug&112)==0 && (SQLnewcatalog = mvc_init(dbname, FALSE, store_bat, 0)) < 0))
		throw(SQL, "SQLinit", "Catalogue initialization failed");
	SQLinitialized = TRUE;
	mal_unset_lock(sql_contextLock,"SQL init");
	if (!GDKembedded && MT_create_thread(&sqllogthread, (void (*)(void *)) mvc_logmanager, NULL, MT_THR_DETACHED) != 0) {
		mal_unset_lock(sql_contextLock,"SQL init");
		throw(SQL, "SQLinit", "Starting log manager failed");
	}
	if (0 && !GDKembedded && MT_create_thread(&minmaxthread, (void (*)(void *)) mvc_minmaxmanager, NULL, MT_THR_DETACHED) != 0) {
		mal_unset_lock(sql_contextLock,"SQL init");
		throw(SQL, "SQLinit", "Starting minmax manager failed");
	}
	return MAL_SUCCEED;
}

str
SQLexit(Client c)
{
#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLexit\n");
#endif
	(void) c;		/* not used */
	if( SQLinitialized == FALSE)
		throw(SQL, "SQLexit", "Catalogue not available");
	return MAL_SUCCEED;
}

#define SQLglobal(name, val) \
	stack_push_var(sql, name, &ctype);	   \
	stack_set_var(sql, name, VALset(&src, ctype.type->localtype, val));

#define NR_GLOBAL_VARS 9
/* NR_GLOBAL_VAR should match exactly the number of variables created
   in global_variables */
/* initialize the global variable, ie make mvc point to these */
static int
global_variables(mvc *sql, char *user, char *schema)
{
	sql_subtype ctype;
	char *typename;
	lng sec = 0;
	bit T = TRUE;
	bit F = FALSE;
	ValRecord src;
	str optimizer;

 	typename = "int";
	sql_find_subtype(&ctype, typename, 0, 0);
	SQLglobal("debug", &sql->debug);

	typename = "varchar";
	sql_find_subtype(&ctype, typename, 1024, 0);
	SQLglobal("current_schema", schema);
	SQLglobal("current_user", user);
	SQLglobal("current_role", user);
	/* inherit the optimizer from the server */
	if (optimizerpipe )
		optimizer= optimizerpipe;
	else
		optimizer= GDKgetenv("sql_optimizer");
	if (optimizer == NULL)
		optimizer= "default_pipe";
	SQLglobal("optimizer", setOptimizers(optimizer));
	SQLglobal("trace","show,ticks,stmt");

	typename = "sec_interval";
	sql_find_subtype(&ctype, typename, inttype2digits(ihour, isec), 0);
	SQLglobal("current_timezone", &sec);

	typename = "boolean";
	sql_find_subtype(&ctype, typename, 0, 0);
	SQLglobal("cache", &T);
	SQLglobal("history", &F);

	return 0;
}

static int
error(stream *out, char *str)
{
	char *p;

	if (!out)
		out = GDKerr;

	if (str == NULL)
		return 0;

	if (mnstr_errnr(out))
		return -1;
	while ((p = strchr(str, '\n')) != NULL) {
		p++;        /* include newline */
		if (*str != '!' && mnstr_write(out, "!", 1, 1) != 1)
			return -1;
		if (mnstr_write(out, str, p - str, 1) != 1)
			return -1;
		str = p;
	}
	if (str && *str) {
		if (*str != '!' && mnstr_write(out, "!", 1, 1) != 1)
			return -1;
		if (mnstr_write(out, str, strlen(str), 1) != 1 || mnstr_write(out, "\n", 1, 1) != 1)
			return -1;
	}
	return 0;
}

#define TRANS_ABORTED "!current transaction is aborted (please ROLLBACK)\n"

static int
handle_error(mvc *m, stream *out, int pstatus)
{
	int go = 1;
	char *buf = GDKerrbuf;

	/* transaction already broken */
	if (m->type != Q_TRANS && pstatus < 0) {
		if (mnstr_write(out, TRANS_ABORTED, sizeof(TRANS_ABORTED) - 1, 1) != 1) {
			go = !go;
		}
	} else {
		if (error(out, m->errstr) < 0 || (buf && buf[0] && error(out, buf) < 0)) {
			go = !go;
		}
	}
	/* reset error buffers */
	m->errstr[0] = 0;
	if (buf)
		buf[0] = 0;
	return go;
}


str
SQLinitClient(Client c)
{
	mvc *m;
	str schema;
	str msg = MAL_SUCCEED;
	backend *be;
	bstream *bfd = NULL;
	stream *fd = NULL;

#ifdef _SQL_SCENARIO_DEBUG
	mnstr_printf(GDKout, "#SQLinitClient\n");
#endif
	if (SQLinitialized == 0 )
		SQLprelude();
	/*
	 * @-
	 * Based on the initialization retun value we can prepare a SQLinit string with all information needed
	 * to initialize the catalog based on the mandatory scripts to be executed.
	 */
	if (sqlinit) { /* add sqlinit to the fdin stack */
		buffer *b = (buffer*)GDKmalloc(sizeof(buffer));
		size_t len = strlen(sqlinit);
		bstream *fdin;

		buffer_init(b, _strdup(sqlinit), len);
		fdin = bstream_create(buffer_rastream(b, "si"), b->len);
		bstream_next(fdin);
		MCpushClientInput(c, fdin, 0, "");
	}
	if (c->state[MAL_SCENARIO_PARSER] == 0) {
		m = mvc_create(c->idx, 0, SQLdebug, c->fdin, c->fdout);
		global_variables(m, "monetdb", "sys");
		if (isAdministrator(c))  /* console should return everything */
			m->reply_size = -1;
		be = (void *) backend_create(m, c);
	} else {
		m = c->state[MAL_SCENARIO_OPTIMIZE];
		be = c->state[MAL_SCENARIO_PARSER];
		mvc_reset(m, c->fdin, c->fdout, SQLdebug, NR_GLOBAL_VARS);
		backend_reset(be);
	}
	if (m->session->tr) {
		node *n = ((sql_schema *) m->session->tr->schemas.set->h->data)->funcs.set->h;
		sql_func *f;
		Symbol s;
		while (n) {
			f = (sql_func *) n->data;
			if (f->sql > 1) {
				s = c->nspace->subscope[((unsigned char *) f->base.name)[0]];
				while (s) {
					if (strcmp(s->name, f->base.name) == 0)
						break;
					s = s->skip;
				}
				if (s == 0)
					f->sql = 1;
			}
			n = n->next;
		}
	}
	/* pass through credentials of the user if not console */
	schema = monet5_user_get_def_schema(m, c->user);
	if (!schema) {
		_DELETE(schema);
		throw(PERMD, "SQLinitClient", "schema authorization error");
	}
	_DELETE(schema);

	/*expect SQL text first */
	be->language = 'S';
	/* Set state, this indicates an initialized client scenario */
	c->state[MAL_SCENARIO_READER] = c;
	c->state[MAL_SCENARIO_PARSER] = be;
	c->state[MAL_SCENARIO_OPTIMIZE] = m;

	initSQLreferences();
	/* initialize the database with predefined SQL functions */
	if (SQLnewcatalog > 0) {
		char path[PATHLENGTH];
		str fullname;

		SQLnewcatalog = 0;
		snprintf(path, PATHLENGTH, "createdb");
		slash_2_dir_sep(path);
		fullname = MSP_locate_sqlscript(path, 1);
		if (fullname) {
			str filename = fullname;
{'fraction_non_alphanumeric': 0.14434682650463912, 'fraction_numerical': 0.010961491091222596, 'mean_word_length': 4.427584670231729, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 127, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2868416', 'n_tokens_mistral': 9713, 'n_tokens_neox': 9163, 'n_words': 2519}