1 /* *****************************************************************************
  2  *	ITSV GmbH
  3  *
  4  *	Software Operations
  5  *	Zentrale Daten und Services
  6  *
  7  *	Product:	CCDB
  8  *	Module:		db
  9  *	Description:	This module contains database functions for CCDB
 10  *	History:
 11  *	Date       |  Author   | Version    | DESCRIPTION
 12  *  -----------+-----------+------------+-------------------------------------------
 13  *  04.11.2015 | WSC       | 0.6        | extracted from ccdb.js
 14  *	12.01.2016 | WSC       | 0.8        | start structures for dbtype
 15  *             |           |            |
 16  *             |           |            |
 17  *             |           |            |
 18  *             |           |            |
 19  *             |           |            |
 20  */
 21 
 22 // var oracledb = require("oracledb");
 23 // var mysqldb = require("mysql");
 24 var oracledb = null;
 25 var mysqldb = null;
 26 
 27 var flow = require('flow');
 28 
 29 var aux  = require('./auxiliary.js');
 30 var dbConfig = require('./dbConfig.js');
 31 
 32 var logger = null;
 33 var mastername = "ccdb.db.mastername not set";
 34 var prefs = {"default" : "db.prefs not set"};
 35 
 36 var currentConfig = {};
 37 
 38 function init_database(cfg) {
 39 	var did_it = false;
 40 	cfg.dbtype = cfg.dbtype || "not_defined";
 41 	if (cfg.dbtype=="oracledb") {
 42 		if (!oracledb) {
 43 			oracledb = require("oracledb");
 44 			did_it = true;
 45 		}
 46 	} else if (cfg.dbtype=="mysql") {
 47 		if (!mysqldb) {
 48 			mysqldb = require("mysql");
 49 			did_it = true;
 50 		}
 51 	} else {
 52 		logger.error("Illegal database type \""+cfg.dbtype+"\"");
 53 	}
 54 	if (did_it) {
 55 		logger.debug("Database "+cfg.dbname+" of type "+cfg.dbtype+" initialized");
 56 	}
 57 }
 58 
 59 /*
 60  *	CLASS:	Connection
 61  *	CONSTRUCTOR:
 62  *		INPUT:		il			-	object with initial values for attributes:
 63  *						cfgname		-	name of configuration set in dbConfig to be used
 64  */
 65 function Connection(il) {
 66 	if (il) {
 67 		if (il.cfgname) {
 68 			this.cfgname = il.cfgname;
 69 		} else if (il.dbcfg) {
 70 			this.cfg = il.dbcfg;
 71 			this.cfgname = this.cfg.getName();
 72 		}
 73 		if (!this.cfg && this.cfgname) {
 74 			this.cfg = getConfigByName(this.cfgname);
 75 		}
 76 	} else {
 77 		this.cfgname = null;
 78 	}
 79 	if (!this.cfgname) {
 80 		this.cfg = getCurrentConfig();
 81 		this.cfgname = this.cfg.getName();
 82 	}
 83 	init_database(this.cfg);
 84 	this.status = "initialized";
 85 	this.getConfig = function() {
 86 		return this.cfg;
 87 	}
 88 	this.connect = function(rfunc) {
 89 		// logger.debug("DB.Connection.connect.BEGIN.CFGNAME=\""+this.cfg.getName()+"\".TYPE="+this.cfg.getType()+"\".CFGSTATUS=\""+this.cfg.getStatus()+"\".CONNSTATUS=\""+this.status+"\"");
 90 		this.callback = rfunc;
 91 		this.status = "connecting";
 92 		switch (this.cfg.dbtype) {
 93 			case "oracledb":
 94 				oracledb.getConnection(
 95 						{
 96 							user			:	this.cfg.getUser(),
 97 							password		:	this.cfg.getPassword(),
 98 							connectString	:	this.cfg.getConnectString(),
 99 						},
100 						function(err,conn) {
101 							this.oracleConnCompl(err,conn);
102 							}.bind(this));
103 				break;
104 			case "mysql":
105 				this.dbconnection = mysqldb.createConnection({
106 					user	:	this.cfg.user,
107 					password:	this.cfg.password,
108 					database:	this.cfg.database
109 				});
110 				// logger.debug("DB.Connection.connect.have_connection_object_will_connect");
111 				this.dbconnection.connect(function(err) { 
112 											this.mysqlConnCompl(err); 
113 											}.bind(this));
114 				break;
115 			default:
116 				throw new Error("Illegal dbtype \""+this.cfg.dbtype+"\"");
117 		}
118 	}
119 	this.mysqlConnCompl = function(err) {
120 		// logger.debug("DB.Connection.mysqlconncomplete");
121 		if (err) {
122 			aux.error_callback(this.callback,err,"Error connecting via mysql");
123 			return;
124 		}
125 		this.status = "connected";
126 		this.callback(null,this.dbconnection);
127 	}
128 	this.oracleConnCompl = function(err,conn) {
129 		if (err) {
130 			aux.error_callback(this.callback,err,"Error connecting via oracledb",conn);
131 			return;
132 		}
133 		this.dbconnection = conn;
134 		this.status = "connected";
135 		this.callback(null,this);
136 	}
137 	this.execute = function(dbr,rfunc) {
138 		this.callback = rfunc;
139 		this.mydbr = dbr;
140 		if (!this.dbconnection) {
141 			aux.error_callback(this.callback,null,"no database connection to execute upon",this);
142 			return;
143 		}
144 		if (this.mydbr.options && this.mydbr.options.notranslate) {					
145 			this.translated_sql = this.mydbr.sql;									// translation explicitely disabled => take the default SQL 
146 		} else {
147 			if (this.mydbr['sql_'+this.cfg.dbtype]) {								// there is a CFG-specific SQL in the DBR => no translation, take dbr.sql_<dbtype>
148 				this.translated_sql = this.mydbr['sql_'+this.cfg.dbtype];
149 			} else {
150 				this.translated_sql = this.translateSQL(this.mydbr.sql,this.mydbr.reqid,this.cfg.dbtype,this.mydbr.sqldbtype);	// translate, if possible
151 			}
152 		}
153 		/*
154 		if (this.mydbr.sql!=this.translated_sql) {
155 			logger.debug("db.Connection.execute.TRANSLATED_SQL=\""+this.translated_sql+"\"");
156 		}
157 		*/
158 		switch (this.cfg.dbtype) {
159 			case "oracledb":
160 				this.dbconnection.execute(this.translated_sql,this.mydbr.params,{resultSet: this.mydbr.options.resultSet},
161 										function(err,result) {
162 											this.oracledbExeCompl(err,result);
163 											}.bind(this));
164 				break;
165 			case "mysql":
166 				// logger.debug("MYSQL.Execute: params: ",this.mydbr.params);
167 				this.dbconnection.query(this.translated_sql,this.mydbr.params, 
168 										function(err,results,fields) { 
169 											this.mysqlExeCompl(err,results,fields); 
170 											}.bind(this)
171 										);
172 				break;
173 			default:
174 				aux.error_callback(this.callback,null,"Illegal dbytpe \""+this.cfg.dbtype+"\" in db.Connection.execute");
175 				return;
176 		}
177 	}
178 	this.oracledbExeCompl = function(err,result) {
179 		this.callback(err,result);
180 	}
181 	this.mysqlExeCompl = function(err,results,fields) {
182 		// logger.debug("MySQL exec complete:");
183 		// logger.debug("RESULTS: ",results);
184 		// logger.debug("FIELDS: ",fields);
185 		if (err) {
186 			logger.error("MYSQL.Execute-error: ",err);
187 		}
188 		this.result = {resulttype: "dbresult"};
189 		if (results && fields) {
190 			var curow = null;
191 			var rn = 0;
192 			var ci = 0;
193 			this.result.rows = new Array();
194 			this.result.metaData = new Array();
195 			for (ci = 0; ci<fields.length; ci++) {						// get column names from fields array, array index determines sequence of field values in results object
196 				this.result.metaData.push({name: fields[ci].name});
197 			}
198 			this.result.rowsinresult = results.length;
199 			for (var rn = 0; rn<results.length; rn++) {		// iterate over all rows in the result
200 				if (this.mydbr.options && this.mydbr.options.maxrowstoget && (rn>=this.mydbr.options.maxrowstoget)) {
201 					break;												// if limit specified, cut off 
202 				}
203 				curow = new Array();
204 				for (ci=0; ci<this.result.metaData.length; ci++) {					// iterate over all fields in each row object as specified by fields array
205 					curow.push(results[rn][this.result.metaData[ci].name]);
206 				}
207 				this.result.rows.push(curow);
208 			}
209 			this.result.rowsgot = this.result.rows.length;
210 		}
211 		this.callback(err,this.result);
212 	}
213 	this.translateSQL = function(sql,reqid,targetdbtype,sourcedbtype) {
214 		if ((targetdbtype==sourcedbtype) && (sql)) { // if to be translated to the same DB type AND sql is known ...
215 			return sql;							     // ... no action required
216 		}
217 		ttab = dbConfig.sqlstrings;
218 		for (var i = 0; i<ttab.length; i++) {                               // search sqlstrings entry with matching REQID and DBTYPE
219 			// logger.debug("translateSQL: checking TTAB DBTYPE/REQID \""+ttab[i].dbtype+"/"+ttab[i].reqid+"\" against requested DBTYPE/REQID \""+targetdbtype+"/"+reqid+"\"");
220 			if ((reqid==ttab[i].reqid) && (targetdbtype==ttab[i].dbtype)) {
221 				return ttab[i].sql;
222 			}
223 		}
224 		logger.warn("No target SQL found for TARGETDBTYPE=\""+targetdbtype+"\".SOURCEDBTYPE=\""+sourcedbtype+"\".REQID=\""+reqid+"\".SQL:\n\""+sql+"\"\n, default to source SQL");
225 		return sql;
226 	}
227 	this.commit = function(rfunc) {
228 		this.callback = rfunc;
229 		this.status = "committing";
230 		switch (this.cfg.dbtype) {
231 			case "oracledb":
232 				this.dbconnection.commit(this.callback);
233 				break;
234 			case "mysql":
235 				this.dbconnection.query("COMMIT",[],
236 										function(err,results,fields) {
237 											// logger.debug("MYSQLCOMMITCOMPLERR:",err);
238 											this.mysqlCommitCompl(err,results,fields);
239 											}.bind(this)
240 										);
241 				break;
242 			default:
243 				aux.error_callback(this.callback,null,"Illegal dbtype \""+this.cfg.dbtype+"\" in db.Connection.commit");
244 				return;
245 		}
246 	}
247 	this.mysqlCommitCompl = function(err,results,fields) {
248 		// logger.debug("MySQL commit complete:");
249 		// logger.debug("RESULTS: ",results);
250 		// logger.debug("FIELDS: ",fields);
251 		this.callback(err,null);
252 	}
253 	this.release = function(rfunc) {
254 		this.callback = rfunc;
255 		// logger.debug("DB.Connection.release.DBTYPE=\""+this.cfg.dbtype+"\"");
256 		switch (this.cfg.dbtype) {
257 			case "oracledb":
258 				this.dbconnection.release(function(err) {
259 											if (err) logger.debug("ORACLEDBENDCOMPLERR:",err);
260 											this.oracleEndCompl(err);
261 										}.bind(this)
262 										);
263 				break;											
264 			case "mysql":
265 				this.dbconnection.end(function(err) {
266 										if (err) logger.debug("MYSQLENDCOMPLERR:",err);
267 										this.mysqlEndCompl(err);
268 									}.bind(this)
269 									);
270 				break;
271 			default:
272 				aux.error_callback(this.callback,null,"Illegal dbtype \""+this.cfg.dbtype+"\" in db.Connection.commit");
273 				return;
274 		}
275 	}
276 	this.oracleEndCompl = function(err) {
277 		this.callback(err);
278 	}	
279 	this.mysqlEndCompl = function(err) {
280 		this.callback(err);
281 	}
282 }
283  
284 /*
285  *	CLASS:	Request
286  */
287 function Request(info,headertext,desctext,sql,params,options) {
288 	if (typeof info === 'string') {				// positional parameters, single strings
289 		this.info = info;
290 		this.headertext = headertext;
291 		this.desctext = desctext;
292 		this.sql = sql;
293 		this.params = params;
294 		this.options = options || {status: "no options provided to Request-constructor with positional parameters"};
295 		if (this.options.reqid) {				// positional parameters do not allow reqid, but can be placed in options
296 			this.reqid = this.options.reqid;
297 		}
298 	} else {									// non-positional parameters, one object
299 		var dbt = getCurrentConfigDBtype();
300 		this.sql 		= info.sql 			|| "SELECT 'NO SQL PROVIDED in DBrequest Constructor' as ERROR FROM DUAL";
301 		if (info['sql_'+dbt]) {
302 			this['sql_'+dbt] = info['sql_'+dbt];
303 		}
304 		this.sqldbtype	= info.sqldbtype	|| "oracledb";
305 		this.reqid		= info.reqid		|| "RQID_NOTSET";
306 		this.info 		= info.info			|| "SQL: \""+this.sql+"\"";
307 		this.headertext = info.headertext	|| "Result of "+this.info+":";
308 		this.desctext 	= info.desctext		|| "Result of "+this.info;
309 		this.params 	= info.params		|| new Array();
310 		this.options 	= info.options		|| {status: "no options provided to Request-constructor with non-positional parameters"};
311 	}
312 	if (this.reqid=="RQID_NOTSET") {
313 		logger.error("Error in DBrequest: no REQID supplied, compatibility violated");
314 	}
315 	this.config = this.options.config || getCurrentConfig();
316 	// ... methods ...
317 	this.setParams = function(theParams) {
318 		this.params = theParams;
319 	}
320 	this.getSQL = function() {
321 		return this.sql;
322 	}
323 	this.setSQL = function(theSQL) {
324 		this.sql = theSQL;
325 	}
326 	this.getConfig = function() {
327 	  return this.config;
328 	}
329 	this.getDBtype = function() {
330 		return this.config.getType();
331 	}
332 }
333 
334 /*
335  *	CLASS:	DBresult
336  */
337 function Result() {
338 	this.resulttype = 'dbresult';
339 	this.metaData = new Array();
340 	this.rows = new Array();
341 }
342 
343 /*
344  *	CLASS:	Config
345  */
346  function Config(constrObj) {
347 	 this.status = "uninitialized";
348 	 if (constrObj) {
349 		 if (typeof(constrObj)==="string") {		// parameter is configuration name
350 			this.cfgname = constrObj;
351 		 } else if (typeof(constrObj)==="object") {
352 			 if (constrObj.configName) {
353 				 this.cfgname = constrObj.configName
354 			 }
355 		 } else {
356 			 throw new Error("Illegal type of db.Config constructor parameter");
357 		 }
358 		 if (this.cfgname && dbConfig.dbconfigs[this.cfgname]) {
359 			this.configName	= this.cfgname;
360 			var o = dbConfig.dbconfigs[constrObj.configName];
361 			for (var a in o) {
362 				this[a] = o[a];
363 			}
364 			this.dbname			= o.dbname;
365 			this.user			= o.user;
366 			this.password 		= o.password;
367 			this.connectString	= o.connectString;
368 			this.status			= 'initialized';
369 		 }
370 	 }
371 	 this.getAtt = function(attnam) {
372 		 return this[attnam];
373 	 }
374 	 this.getName = function() {
375 		 return this.configName;
376 	 }
377 	 this.getConnectString = function() {
378 		 return this.connectString;
379 	 }
380 	 this.getHost = function() {
381 		 return this.host;
382 	 }
383 	 this.getDatabase = function() {
384 		 return this.database;
385 	 }
386 	 this.getUser = function() {
387 		 return this.user;
388 	 }
389 	 this.getPassword = function() {
390 		 return this.password;
391 	 }
392 	 this.setStatus = function(statxt) {
393 		 this.status = statxt;
394 	 }
395 	 this.getStatus = function() {
396 		 return this.status;
397 	 }
398 	 this.getType = function() {
399 		 return this.dbtype;
400 	 }
401  }
402  
403 function getConfigByName(cfgname) {
404 	return new Config(cfgname);
405 }
406 
407 /* *****************************************************************************
408  *
409  *	FLOW:			db.select
410  *	INPUT:			DBR		- DBrequest object 
411  *					RFUNC	- callback function to be called upon completion
412  *						parameters:
413  *							ERR 	- an Error Object, if undefined, everything OK
414  *							RESULT 	- a RESULT-Object
415  *					PFUNC	- callback function to be called for progress info
416  *						parameters:
417  *							PROG	-	a hierarchical object describing the progress of the operation
418  *	RETURNS:		-
419  *	DESCRIPTION:	performs an SQL Select (or other DML statement)
420  *					upon completion, RFUNC is called with an error object and a DBresult object as parameters
421  */
422  
423 function dbselect_keepalive(that) {
424 	that.keepalivecounter++;
425 	var kam = {action: "DBSelect",
426 				stage: "execute",
427 				stateunit: "seconds",
428 				state: that.keepalivecounter};
429 	// logger.debug(kam);
430 	if (that.dbr.pcallback) that.dbr.pcallback(kam);
431 	that.keepalive = setTimeout(dbselect_keepalive,1000,that);
432 }
433 
434 var select = flow.define(
435 	// step 1: request database connection
436 	function(dbr,rfunc,pfunc) {
437 		aux.default_param(dbr,"sql","SELECT 'Error: no SQL-Statement given to DBSelect' from DUAL");
438 		aux.default_param(dbr,"params",new Array());
439 		aux.default_param(dbr,"options",{});
440 		aux.default_param(dbr,"sqldbtype","oracledb");
441 		aux.default_param(dbr,"dbcfg",getCurrentConfig());
442 		aux.default_param(dbr.options,"docommit",(dbr.sql.toUpperCase().substring(0,6)!='SELECT'));
443 		if ((dbr.options.docommit) && (typeof dbr.options.docommit === 'string') && (dbr.options.docommit.toUpperCase()!='TRUE')) {
444 			dbr.options.docommit = false;
445 		}
446 		aux.default_param(dbr.options,"maxrowstoget",1000);
447 		aux.default_param(dbr.options,"numrows",200);
448 		aux.default_param(dbr.options,"resultSet",!dbr.options.docommit);		// resultSet can only be used if select statement, therefore we check if there is a commit
449 		aux.default_param(dbr.options,"status_override",false);
450 		aux.default_param(dbr,'headertext',dbr.sql);
451 		aux.default_param(dbr,'info',"SQL=\""+(dbr.sql.length>10?dbr.sql.substr(0,10)+"...":dbr.sql)+"\"");
452 		dbr.callback = rfunc;
453 		dbr.pcallback = pfunc;
454 		this.dbr = dbr;
455 		this.dbr.starttime = Date.now();									// measure elapsed time in milliseconds starting here
456 		// logger.debug("db.select.BEGIN.INFO="+this.dbr.info);
457 		// logger.debug("db.select.OPTIONS:");
458 		// logger.debug(this.dbr.options);
459 		if ((dbr.dbcfg.getStatus()!='ready') && !(dbr.options.status_override)) {
460 			var err = new Error("DBSelect: cannot access database "+getCurrentConfig().getName()+", status is \""+getCurrentConfig().getStatus()+"\"");
461 			logger.error(err.message);
462 			this.dbr.callback(err,undefined);
463 			return;
464 		}
465 		if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "1/6 - connecting"});
466 		this.dbconnection = new Connection();
467 		this.dbconnection.connect(this);
468 	},
469 	// step 2: got connection, execute SQL 
470 	function(err) {
471 		if (err) {
472 			aux.error_callback(this.dbr.callback,err,"DBSelect: Error in Connect",this.dbconnection);
473 			return;
474 		}
475 		// logger.debug("DBSelect.SQL=\""+this.dbr.sql+"\""+(this.dbr.params.length>0?".PARAMS=[\""+this.dbr.params.join("\",\"")+"\"]":".NOPARAMS"));
476 		if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "2/6 - executing"});
477 		this.keepalivecounter = 0;
478 		this.keepalive = setTimeout(dbselect_keepalive,1000,this);
479 		this.dbconnection.execute(this.dbr,this);
480 	},
481 	// step 3: executed, start fetching result
482 	function(err,result) {
483 		clearTimeout(this.keepalive);
484 		if (err) {
485 			aux.error_callback(this.dbr.callback,err,"DBSelect: Error in SQL-Execute",result);
486 			return;
487 		}
488 		this.dbr.result = result;
489 		this.dbr.result.timing = { unit: '1000ms_keepalive_cycles', count: this.keepalivecounter };
490 		if (this.dbr.result.resultSet) {										// do we have results in a [resultSet] object?
491 			this.dbr.result.rows = new Array();
492 			this.dbr.result.rowsgot = 0;
493 			this.dbr.result.rowsinresult = 0;
494 			if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "3/6 - fetch_result_1st_part", stateunit: "rowsgot", state: this.rowsgot});
495 			this.dbr.result.resultSet.getRows(this.dbr.options.numrows,this);	// yes, begin fetching
496 		} else {
497 			this();											// no results, possibly result data in rows object, simulate fetch with no rows object to satisfy next step
498 		}
499 	},
500 	// step 4: one fetch complete
501 	function(err,rows) {
502 		if (err) {
503 			logger.error("DBSelect: Error in getRows:");
504 			logger.error(err.message);
505 			this.dbr.callback(new Error("DBSelect: Error in fetching result chunk row no. "+(this.dbr.result.rowsgot+1)+": "+err.message),rows);
506 			return;
507 		}
508 		if ((rows) && rows.length>0) {      						// if we got a rows object with data, collect them in dbr.result.rows (up to options.maxrowstoget)
509 			// logger.debug("DBSelect.ROWS_GOT="+rows.length);
510 			for (var rn=0; rn<rows.length; rn++) {
511 				if (this.dbr.result.rows.length<this.dbr.options.maxrowstoget) {
512 					this.dbr.result.rows.push(rows[rn]);
513 					this.dbr.result.rowsgot++;
514 				}
515 				this.dbr.result.rowsinresult++;
516 			}
517 			this.REWIND();								// stay at same step
518 			if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "4/6 - fetch_result_following_part", stateunit: "rowsgot", state: this.dbr.result.rowsgot});
519 			this.dbr.result.resultSet.getRows(this.dbr.options.numrows,this);	// request next junk of data
520 		} else {
521 			if (this.dbr.result.resultSet) {			// there is a resultSet to close
522 				if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "close_resultSet", stateunit: "step", state: "wait_close"});
523 				this.dbr.result.resultSet.close(this);								// close the resultSet
524 			} else {
525 				this();																// no resultSet to close, proceed to next step immediately
526 			}
527 		}
528 	},
529 	// step 4a: result set closed
530 	function(err) {
531 		if (err) {
532 			var nerr = new Error("DBSelect: error in closing resultSet: "+e.message);
533 			logger.error(nerr.message);
534 			this.dbr.callback(nerr);
535 			return;
536 		}
537 		if (this.dbr.result.resultSet) {
538 			delete this.dbr.result.resultSet;
539 		}
540 		if (this.dbr.options.docommit) {		// proceed with commit, if requested
541 			if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "5/6 - committing"});
542 			// logger.debug("DBSelect.COMMITTING");
543 			this.dbconnection.commit(this);
544 		} else { 								// no commit requested, push on to next step
545 			this();
546 		}
547 	},
548 	// step 5: commit complete
549 	function(err) {
550 		if (err) {
551 			logger.error("DBSelect: Error in COMMIT");
552 			logger.error(err.message);
553 			this.dbr.callback(new Error("DBSelect: Error in Commit: "+err.message),this.dbr.result);
554 			return;
555 		}
556 		if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "6/6 - releasing"});
557 		// logger.debug("DB.select.before-release.connection:",this.dbconnection);
558 		this.dbconnection.release(this);
559 	},
560 	// step 5: release complete
561 	function(err) {
562 		if (err) {
563 			logger.error("DBSelect: Error in RELEASE");
564 			logger.error(err.message);
565 			this.dbr.callback(new Error("DBSelect: Error in Release: "+err.message),this.dbr.result);
566 			return;
567 		}
568 		delete this.dbr.connection;
569 		if (this.dbr.pcallback) this.dbr.pcallback({action: "DBSelect", stage: "END - complete"});
570 		this.dbr.result.timing = {level: 'DBSelect', unit: 'ms', count: Date.now()-this.dbr.starttime};
571 		this.dbr.result.resulttype = 'dbresult';
572 		// logger.debug("db.select.COMPLETE.ROWSINRESULT="+this.dbr.result.rowsinresult+".ROWSRETURNED="+this.dbr.result.rowsgot);
573 		this.dbr.callback(undefined,this.dbr.result);
574 	});
575 
576 	 
577  /*
578   *	FUNCTION:		sync_database
579   *	INPUT:			thedb	-	db.Config object containing the current database configuration to set
580   *	DESCRIPTION:	synchronize the database configuration in <thedb> from the actual database we're connecting to
581   */
582  var sync_database = flow.define( 
583 	function(thedb) {
584 		logger.debug("SYNC_DATABASE.BEGIN.CFGNAME=\""+thedb.getName()+"\"");
585 		this.thedb = thedb;
586 		var dbr = new Request({info:		"sync_database.scan_tables",
587 								reqid:		'RQID_SCANTABLES',
588 								sql:		"select TABLE_NAME from ALL_TABLES where TABLE_NAME like 'CC%'",
589 								params:		[],
590 								options:	{status_override: true}});
591 		select(dbr,this);
592 	},
593 	function(err,result) {
594 		if (err) {
595 			var errmsg = "Error in sync_database()/1 for "+this.thedb.getName()+": "+err.message;
596 			logger.error(errmsg);
597 			this.thedb.setStatus(errmsg);
598 			return;
599 		}
600 		
601 		var dbr = new Request({info:		"sync_database.scan_columns",
602 								reqid:		'RQID_SCANCOLS',
603 								sql:		"select TABLE_NAME,COLUMN_NAME,DATA_TYPE,DATA_LENGTH from ALL_TAB_COLUMNS where TABLE_NAME like 'CC%'",
604 								params:		[],
605 								options:	{status_override: true}});
606 		select(dbr,this);	
607 	},
608 	function(err,result) {
609 		if (err) {
610 			var errmsg = "Error in sync_database()/2 for "+this.thedb.getName()+": "+err.message;
611 			logger.error(errmsg);
612 			this.thedb.setStatus(errmsg);
613 			return;
614 		}
615 		var dbr = new Request({	info:	"sync_database.scan_views",
616 									reqid:	'RQID_SCANVIEWS',
617 									sql:	"select VIEW_NAME from ALL_VIEWS where VIEW_NAME like 'CC%'",
618 									params:	[],
619 									options:	{status_override: true}
620 								});
621 		select(dbr,this);	
622 	},
623 	function(err,result) {
624 		if (err) {
625 			var errmsg = "Error in sync_database()/3 for "+this.thedb.getName()+": "+err.message;
626 			logger.error(errmsg);
627 			this.thedb.setStatus(errmsg);
628 			return;
629 		}
630 		//
631 		// all OK, let database be accessible
632 		//
633 		logger.debug("SYNC_DATABASE.READY.DATABASE=\""+this.thedb.getName()+"\"");
634 		this.thedb.setStatus('ready');
635 	});
636  
637  /******************************************************************************
638   *
639   *	FUNCTION:		setConfig
640   *	INPUT:			dbn		-	name of DB Configuration to be set
641   *	RESULT:			text describing the outcome of the operation
642   * GLOBALS:		db.currentConfig	- global current DB configuration
643   *					dbConfig			- global configuration structure  
644   * DESCRIPTION:	sets the current DB configuration to the one with name <dbn>
645   *					configuration parameters are taken from global dbConfig
646   *					if <dbn> is not given, the default configuration is used
647   *					the name of the default configuration is taken from dbConfig.default
648   */
649  function setConfig(dbn) {
650 	if (dbn.length==0) { 		// use default
651 		dbn = dbConfig.default;
652 		logger.debug("CCDB.DB.SET_CONFIG.USING_DEFAULT=\""+dbn+"\"");
653 	}
654 	if (dbConfig.dbconfigs.hasOwnProperty(dbn)) {
655 		currentConfig = new Config({configName: dbn});
656 		currentConfig.setStatus('not ready');
657 		init_database(currentConfig);
658 		sync_database(currentConfig);
659 		return "Database set to "+currentConfig.getName();
660 	} else {
661 		var errtxt = "Database "+dbn+" cannot be set, nothing changed";
662 		logger.error(errtxt);
663 		return errtxt;
664 	}
665  }
666  
667  function getCurrentConfig() {
668 	 return currentConfig;
669  }
670  
671  function getCurrentConfigDBtype() {
672 	 return currentConfig.dbtype;
673  }
674  
675  function listConfigs() {
676 	var dbnames = new Array();
677 	for (dbn in dbConfig.dbconfigs) {
678 			dbnames.push(dbn);
679 		}
680 	return dbnames;
681  }
682 
683 /* *****************************************************************************
684  *		The module interface of CCDB.db
685  */
686 
687 var localexportobjects = {
688 	Connection			:	Connection,
689 	Request				:	Request,
690 	Result				:	Result,
691 	select				:	select,
692 	setConfig			:	setConfig,
693 	getCurrentConfig	:	getCurrentConfig,
694 	listConfigs			:	listConfigs,
695 	getCurrentConfigDBtype	: getCurrentConfigDBtype
696 }
697 
698 var logger;
699 var mastername;
700 
701 module.exports = function(theLogger,theMasterName,thePrefs) {
702 	logger = theLogger;
703 	mastername = theMasterName;
704 	prefs = thePrefs;
705 	logger.debug("CCDB: this is the instantiation followup of "+__filename+", the master is "+mastername+"/"+theMasterName);
706 	var c = 0;
707 	var oal = "";
708 	for (var oa in module.exports) {
709 		oal += ((c<1)?"":",")+oa;
710 		c++;
711 	}
712 	// logger.debug("CCDB: module.exports has "+c+" attributes: "+oal);
713 	return module.exports;
714 }
715 
716 for (var oa in localexportobjects) {
717 		module.exports[oa] = localexportobjects[oa];
718 }