import com;
import fsys;
import string.list;
import time.ole;

class access {

	ctor( path,param,...){
	
		if(!path) error("请指定数据库路径",2);
		var connectionString = path;
		this.dataSourceName = ..string.match( connectionString,"!\w<@@DSN@>\s*\=([^;]+)");
		
		if(!this.dataSourceName){
			var ext =  ..string.match(path,"[^.]+$");
			ext = ext ? ..string.lower(ext); 
			var excelFormat = ({xls="xls";xlsx="xlsx";xlsm="xlsm";xlsb="xlsb"}) [ext]; 
			
			
	
			if( ( ! ..string.find(path,"\;") && !... )
				&& ( ( param === null ) || ( type(param) == type.table ) )
	    	) { 
				var tList = ..string.list(,";","=")
				tList.mixin(param) 
				
				for i,k,v in tList.each() { 
					if( ( ..string.indexOf(v,";") || ..string.indexOf(v,"=") ) && !..string.match(v,"^\s*%{}\s*$") ){
						tList[k] = ..string.format("'%s'",v);
					}
				}
	 		
				if(  !tList.find("Provider")  && !tList.find("Driver") ) {
					var isText = ({csv=1;tab=1;txt=1;asc=1})[ext]; 
					if( ! ( ..fsys.isDir(path) || isText ) ){
						if( (excelFormat && excelFormat != "xls")  or ( ext == "accdb" ) ){
							tList.Provider="Microsoft.ACE.OLEDB.12.0"; 
						}
						else{
							tList.Provider="Microsoft.Jet.OLEDB.4.0"; 
						}
					}
					else {
						tList.Driver="{Microsoft Text Driver (*.txt; *.csv)}";
						if(!tList.Extensions && !tList.extensions ){
							tList.Extensions = "asc,csv,tab,txt";
						} 
					}
				 	
				}
				if(excelFormat && !tList.find("Extended Properties") ) {
					if( excelFormat == "xls" ){
						tList["Extended Properties"] = "'Excel 8.0;HDR=Yes'";
					}
					else {
						if( excelFormat == "xlsx" ){
							tList["Extended Properties"] = '"Excel 12.0 Xml;HDR=YES"';
						}
						elseif( excelFormat == "xlsm" ){
							tList["Extended Properties"] = '"Excel 12.0 Macro;HDR=YES"';
						} 
						else {
							tList["Extended Properties"] = '"Excel 12.0;HDR=YES"';
						}
					}
				}
				
				path = ..io.fullpath(path); 
				if( tList.find("Driver")  ){
					tList["Dbq"] = path;
				}
				else {
					tList["Data Source"] = path;
				} 
				
				connectionString = tostring( tList );
			}
			else {  
				if( param ) {
					connectionString = ..string.format(connectionString, param,... );
				};
				
				var count;
				for(i,p in {
					"(D|data S|source\s*)\=\s*(.+?)\;";
					"(I|initial C|catalog\s*)\=\s*(.+?)\;";
					"(D|database\s*)\=\s*(.+?)\;";
					"(Dbq\s*)\=\s*(.+?)\;";
					"(DBQ\s*)\=\s*(.+?)\;";
					"(dbq\s*)\=\s*(.+?)\;"
				} ){
					connectionString,count = ..string.replace(connectionString,p,function(k,p){ 
						path = ..io.fullpath(p)
						return ..string.format("%s=%s;", k,path)
					} )	
					if(count) break;
				} 
			}
		}

		if( (!this.dataSourceName) 
			&& ( !excelFormat ) 
			&& ( ! ..io.exist(path) ) 
			) {
			var cat = ..com.CreateObject( "ADOX.Catalog")  
			var ok,c = call( cat.Create,cat, connectionString )
			cat = null;
			
			if(ok) this.connection = c;
			else {
				this.connection = ..com.CreateObject("ADODB.Connection");
				this.connection.ConnectionString = connectionString;
				var ok,e = call(this.connection.open,this.connection);
				if(!ok) return null,"打开数据库连接失败:" + c; 
			} 
		}
		else {
			this.connection = ..com.CreateObject("ADODB.Connection");
			this.connection.ConnectionString = connectionString;
			var ok,e = call(this.connection.open,this.connection);
			if(!ok) return null,"打开数据库连接失败:" + e;
		} 
		
		this._tables = {@{_weak="kv"}}
		this.connectionString = connectionString;
		..table.gc(this,"close");
	};  
	getCatalog = function(){
		if( this.catalog ) { 
			return this.catalog;
		}
		this.catalog = ..com.CreateObject("ADOX.Catalog")  
		this.catalog.ActiveConnection = this.connection;   
		return this.catalog;	
	};
	eachTableObject = function(){
		var cat = this.getCatalog();
	 	
		var next,items,idx = ..com.each(cat.Tables);  
		var tbl;
		return function(){
			idx,tbl = invoke(next,items,idx) 
			if( tbl ) return tbl.getName(),tbl.getType(),tbl;
		} 
	}; 
	findTableObject = function(name,cache=true){
		if(cache && this._tables[name] ) return this._tables[name];
		
		var cat = this.getCatalog();
		cat.Tables.Refresh();
		for index,tbl in ..com.each(cat.Tables) {
			if( tbl.Type = "TABLE" and tbl.Name = name ){
				if(cache) this._tables[name] = tbl;
				return tbl;
			}
		}	
	};
	existsTable = function(name){ 
		return !!this.findTableObject(name);
	}; 
	exec = function(sql,...){ 
		if( ... )sql = format(sql,...); 
		var ret,err = call(this.connection.Execute,this.connection,sql );
		if(!ret) error(err,2); 
		return ret;
	}; 
	openRecordSet = function(sql,cursorType,lockType,...){
		var rs = ..com.CreateObject("ADODB.RecordSet");
		if( ... )sql = format(sql,...);
		var ok,err = call(rs.Open,rs,sql,this.connection,cursorType,lockType);
		if(!ok) return null,err;
		return rs;
	}; 
	getRows = function(sql,...){
		var rs,err = owner.openRecordSet(sql,,,...);
		if(!rs) error(err,2);
		 
		var recordCount = 0;
		var eof = rs.eof;
		var data = eof ? { @{} } : rs.GetRows();  
		
		var fields = {};
		var name; 
		for( i=1;rs.Fields.Count ){
			var name = rs.Fields(i-1).Name;
			if(#name){
				fields[i]  = name;
				data[ name ] = data[ i ]; 
			};
			if(!eof){
				var _,len = ..table.range(data[ i ]);
				recordCount = ..math.max(len,recordCount);
			}
		} 
		
		rs.close();
		
		data@.recordCount = recordCount;
		data@.fields = fields; 
		return data;
	};
	getTable = function(sql,...){
		var data,err = this.getRows(sql,...);
		if(!data) error(err,2);
		
        var result = {}; 
        var v; 
        var rowCount = data@.recordCount;
        var trim = ..string.trimright; 
        var fields = data@.fields;
        for(c=1;#data){
            var col = data[c];
            var name = fields[c];
            if(!#name) name = c;
            
            if(c>1){
            	for(r=1;rowCount){
        			v = col[r];
        			if( type( v ) == "string" ) v = trim(v);
        			elseif( ..time.istime(v) ) v = ..time.ole(v);
        		
        			result[r][name]  = v;
        		} 
        	}
        	else {
        		for(r=1;rowCount){
        			v = col[r];
        			if( type( v ) == "string" ) v = trim(v);
        			elseif( ..time.istime(v) ) v = ..time.ole(v);
        			
        			result[r] = { [name]  = v };
        		} 
        	}
        }
        
        result.fields = fields;
		return result;
	};
	stepQuery = function(...){
		return this.getTable(...)[[1]];
	};
	each = function(sql,...){  
        var rs,err = this.openRecordSet(sql,,,...);
       	var first = true;
       	if(!rs)error(err,2);
       	
        return function(){ 
        	if(!first) rs.movenext(); 
        	else first = false;
   
            if( rs.eof )   return;
 			return rs;
 			
        },function(){ rs.close();   } ;
    }; 
	createCommand = function(sql,...){
		var cmd = ..com.CreateObject("ADODB.Command") 
		cmd.activeConnection = this.connection;
		cmd.commandType = ..string.find(sql,"\s\S") ? 1/*adCmdText*/ : 4/*adCmdStoredProc*/;
		if( ... )sql = format(sql,...);
		cmd.commandText = sql;
		cmd.namedParameters = true;
		
		var getParameterType = function(value){
			var adType,adSize; 
			var t = type(value);
			
			select(t) {
				case "string" {
					adType = 203/*adLongVarWChar*/
					value = ..string.toUnicode(value);
					adSize = #value/2;
				}
				case "number" {
					adType = 5/*adDouble*/
					adSize = 8;
				}
				case "buffer" {
					adType = 204/*adVarBinaryr*/
					adSize = #value;
				}
				case "boolean" {
					adType = 11/*adBoolean*/
					adSize = 1;
				}
				case "null"{
					adType = 3/*adInteger*/;
					adSize = 0;
					value = ..com.Variant(null);
				}
				else {
					if( ..time.istime(value) ){
						adType = 7/*adDate*/;
						adSize = 8;
						value = tonumber(..time.ole(value));
					}
					else {
						error("自动识别类型遇到错误",3)
					} 
				}	
			}
			
			return adType,adSize,value;
		} 
		
		cmd[["addParameter"]] = function(name,value,opt=1/*_adParamInput*/,adType,adSize){
			var adType,adSize,value = getParameterType(value);  
			var param = cmd.CreateParameter(name,adType,opt,adSize,value); 
			cmd.Parameters.Append(param);  
			return param;	
		};
 
		cmd[["bind"]] = function(parameters){ 
			if( type(parameters) == type.table ){
				if( cmd.parameters.Count ){
					var p,n,v;
					for(i=1;cmd.parameters.Count;1){
						p = cmd.parameters(i-1);
						n = ..string.right(p.name,-2) 
						v = parameters[n];
						
						if(v===null){
							p.Value = ..com.Variant(null);
							continue;
						}
						
						if(cmd.commandType != 4){
							p.Type,p.Size,v = getParameterType(v);   
						}
						p.Value = v; 
					}
					return;
				}
				
				if( cmd.commandType == 4 ){
					for(name,value in parameters){
						cmd.addParameter(name,value); 
					}
					return cmd;
				} 
				
				//access的参数虽然必须指定正确的名字,但实际上他是忽略名字按SQL中出现的顺序设置参数的
				..string.replace(cmd.commandText,"\@(<:+>|<\w+>)",
						function(name){ 
							cmd.addParameter(name,parameters[name]); 
						}
					) 
					
				return cmd;
			}
			
			//已废弃,勿用	
			return {
				parameters = function(tParam){ return cmd.bind(tParam); }
			}
		};
		
		cmd[["executeNoRecords"]] = function(ra,parameters){
			cmd.execute(ra,parameters,cmd.adExecuteNoRecords);
		};
		
		cmd[["executeStream"]] = function(ra,parameters){
			cmd.execute(ra,parameters,cmd.adExecuteStream);
		};
		cmd[["getRows"]] = function(...){
			var rs,err = cmd.execute(...); 
			if(!rs) error(err,2);
		 	
			var recordCount = 0;
			var closed = (rs.State == rs.AdStateClosed);
			var eof = closed || rs.eof;
			var data = eof ? { @{} } : rs.GetRows();  
			
			var fields = {};
			var name; 
			for( i=1;rs.Fields.Count ){
				var name = rs.Fields(i-1).Name;
				if(#name){
					fields[i]  = name;
					data[ name ] = data[ i ]; 
				}
				
				if(!eof){
					var _,len = ..table.range(data[ i ]);
					recordCount = ..math.max(len,recordCount);
				}
			} 
			
			if(!closed)rs.close();
			data@.recordCount = recordCount;
			data@.fields = fields; 
			return data;
		};
		cmd[["getTable"]] = function(...){
			var data,err = cmd.getRows(...);
			if(!data) error(err,2);
			
        	var result = {}; 
        	var rowCount = data@.recordCount;
        	if(!rowCount) return result;
        	
        	var v; 
        	var trim = ..string.trimright; 
        	var fields = data@.fields;
        	for(c=1;#data){
            	var col = data[c];
            	var name = fields[c];
            	if(!#name) name = c;
            	
            	if(c>1){
            		for(r=1;rowCount){
        				v = col[r];
        				if( type( v ) == "string" ) v = trim(v);
        				elseif( ..time.istime(v) ) v = ..time.ole(v);
        			
        				result[r][name]  = v;
        			} 
        		}
        		else {
        			for(r=1;rowCount){
        				v = col[r];
        				if( type( v ) == "string" ) v = trim(v);
        				elseif( ..time.istime(v) ) v = ..time.ole(v);
        				
        				result[r] = { [name]  = v };
        			} 
        		}
        	}
        	
        	result.fields = fields;
			return result;
		};
		cmd["stepQuery"]  = function(...){
			return cmd.getTable(...)[[1]];
		};
		cmd["each"] = function(...){  
        	var rs,err = cmd.execute(...); 
       		var first = true;
       		if(!rs)error(err,2);
       		
        	return function(){ 
        		if(!first) rs.movenext(); 
        		else first = false;
   	
            	if( rs.eof )   return;
 				return rs;
 				
        	},function(){ rs.close();   } ;
    	}; 
    	
		return cmd;
	}; 
    setPassword = function(pwd,oldPwd){
    	return this.exec("ALTER DATABASE PASSWORD %s %s",pwd,oldPwd : "``");
    }; 
	beginTrans = function(){
		return this.connection.BeginTrans();
	};
	commitTrans = function(){
		return this.connection.CommitTrans();
	};
	rollbackTrans = function () {
		return this.connection.RollbackTrans();
	};
	close = function(){
		if(this.connection){
			this.connection.close();
			this.connection = null;
		}
	};
	@_meta;
} 

namespace access {
    
	_meta = {
		_get = function(k){
			if( ..string.cmp(k,"ad",2) == 0 ){
				owner[[k]] = owner.connection[k];
				return owner[[k]];	
			};
		}
	};
	
	escapeId = function(id){
		if(type(id)==="table"){
			if(#id){
				var ret = {}
				for(i=1;#id;1){
					..table.push(ret,escapeId(id[i])); 
				}
				return ..string.join(ret,",");  
			}
			else {
				var ret = {}
				for(k,v in id){
					if(type(v)!="table")
						..table.push(ret,escapeId(k) + " = " + escape(v));
					else
						..table.push(ret,escapeId(k) + " IN " + escape(v,true));
				} 
				return ..string.join(ret," AND ");
			}
		}

		return '[' + ..string.replace(id,"\.", '].[')+ ']' 
	};
	
	escape = function(v,nested){
		var t  = type(v);
			 	
		if( t = "string" ){
			return "'" +  ..string.replace(v,"'","''") + "'";
		}
		elseif( t = "number" ){
			return tostring(v);
		}
		elseif( t = "boolean" ){
			return v ? "1" : "0";
		}
		elseif( t = "table" ) {
			if(..time.istime(v)){
				var f = v.format;
				v.format = "%Y-%m-%d %H:%M:%S";
				var ret  = "#" +  tostring(v) + "#";
				v.format = f;
				return ret;
			}
			elseif(#v){
				var ret = {}
				for(i=1;#v;1){
					..table.push(ret,escape(v[i],true)); 
				}
				ret = ..string.join(ret,",");
				if(nested) ret = "(" + ret + ")";
				return ret;
			} 
			elseif( v@[["_toSqlString"]] ){
				return invoke(v@[["_toSqlString"]],v,'access')
			}
			else {
				var ret = {}
				for(k,vv in v){
					..table.push(ret,escapeId(k) + " = " + escape(vv));
				} 
				return ..string.join(ret,",");
			}
		} 
		elseif( t=='buffer'){
			return ..string.concat("0x",..string.hex(v,"") );
		} 
		return ..string.replace(tostring(v),"\p",""); 
	};
	
	format = function(sql,...) { 
		if( type( ... ) == "table" ){
			var param = ...;
			if(#param){
				var i = 0;
				sql = ..string.replace(sql,"\?\?*",
					function(name) {
						i++;
						if( name == "?" ) return escape(param[i]);
						else return escapeId(param[i])
					}
				); 
			}
				
			return (..string.replace(sql,"\@(<:+>|<\w+>)",
				lambda(name) escape(param[name])
			)); 	
		}
		return ..string.format(sql,...);
	}
	
	formatSqlParameters = format; 
}

/**intellisense()
access = 使用系统自带组件连接access数据库,可支持excel\n[数据库连接串大全](https://www.connectionstrings.com/access/)
access("数据库路径") = 打开数据库连接,\n数据库路径可以指定mdb,xls等文件格式,\n如果指定的数据库不存在将自动创建数据库文件
access("数据库路径",其他连接参数) = @ access("__/*数据库路径(mdb,xls等)*/",{ \n	["Mode"]="Share Deny Read|Share Deny Write"; \n	["Jet OLEDB:Database Password"] = "123456"; \n}) 
access("数据库连接语句",其他格式化参数) = @access("Provider=Microsoft.Jet.OleDb.4.0;Data Source=%s;Jet OLEDB:Database Password=%s;",\n	"/test.db","__/*密码*/" );
打开数据库连接,\n如果指定的数据库不存在将自动创建数据库文件\n如果存在格式化参数，则调用string.fomat格式化连接串
access() = !access.
access.escape(__) = 转义参数指定值为用于 SQL 查询语句的参数化值
access.escapeId(__) = 如果传入参数是文本则转换为 SQL 标识符,\n返回文本首尾会添加中括号,\n\n如果传入参数是表, 则格式化为 SQL 键值对并以 AND 为分隔符,\n如果表中的值为数组,则格式化为 IN 语句
access.format(.(SQL语句,格式化参数->->->) = 格式化 SQL 查询语句。\n所有需要格式化 SQL 语句的函数调用此函数格式化 SQL语句,\n如果格式化参数不是表则调用 string.format 格式化,否则按以下规则格式化:\n\nSQL语句中?或??占位符使用表的数组元素格式化,\n@字符开始的命名参数使用表的名值对元素格式化,\n其中??格式化为标识符,其他占位符格式化为参数值。\n\n字符串转为SQL安全转义字符串,buffer转为 0x4D7953514C 格式,\n数组则自动展开为列表,例如{'a', 'b'}格式化为'a', 'b'\n嵌套数组则格式化为分组列表,例如{{'a', 'b'}, {'c', 'd'}} 格式化为 ('a', 'b'), ('c', 'd')\n非数组的命名表，则格式化为 SQL 键值对,默认以逗号为分隔符,\n??占位符格式化 SQL 键值对则以 AND 为分隔符,并将数组值转换为IN语句
end intellisense**/

/**intellisense(!access)
? = 可直接使用对象的成员获取ADO组件提供的COM常量\n这些常量名的前缀都是 ad
dataSourceName = 数据库连接串中指定的ODBC数据源
close() = 关闭数据库连接
getTable(.("select * from [表名]",其他格式化参数) = 返回查询结果集数组\n每个元素代表一行,每行由N个键值对列组成\n返回值的fields成员记录了所有数据列的字段名\n结果集为空也会返回空数组,使用#操作符获取结果集长度
getRows() = 查询并返回包含结果数据的数组\n此函数返回的数据是先列后行,与getTable相反,\n数组由所有列的数据组成,每个列都是由多个行组成的数组\n可以使用 返回数据[ 列号 ][ 行号 ] 获取对应的值,\n也可以使用 数据.字段名[列号] 获取对应的值\n\n返回数组的元表中使用recordCount记录了总行数,\n结果集为空也会返回空数组,可使用 @.recordCount判断记录集的行数\n元表中的fields字段则是记录了所有字段名的数组.\n\n所有值后面的占位空白没有去除\n可使用getTable函数获取处理过空白占位符的数据,\n或参考该函数源码自行处理尾部空白
stepQuery(.("select * from [表名]",其他格式化参数) = 返回查询结果集第一行的数据,由键值对列组成,\n无数据返回null\n\n\n格式化规则请参考 access.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化
openRecordSet(.(sql,游标类型,锁定方法,其他格式化SQL参数) = 打开记录集,游标类型如下:\n0:默认值,启动一个只能向前移动的游标\n1:打开键集类型游标,允许任意移动，并且允许更改记录集\n2:打开动态类型游标,许所有操作，其他用户对记录集的添加、删除、更改在此记录集中\n3:打开静态类型游标\n\n锁定方法如下：\n1:默认值,不能改变数据\n2:悲观锁,编辑时立即锁定数据源的记录\n3:乐观锁,只在调用Update 方法时才锁定记\n4: 乐观批更新
exec(.(sql,其他格式化SQL参数) =  执条SQL语句\n格式化参数规则请参考 access.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化
createCommand(.(sql,其他格式化SQL参数) = 创建命令对象\n格式化规则请参考 access.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化
existsTable(.("表名") = 判断数据库中是否存在指定表
each(sql,其他格式化参数) = @for(rs in ??.each("select * from [表名]") ){ 
	io.print( rs("字段名").value );__
}
each() = !access_rs.
createCommand() = !access_cmd.
connection = 连接对象
connectionString = 打开数据库时指定的连接串\n[数据库连接串写法]（https://www.connectionstrings.com/access/）
connection.CursorLocation = @.CursorLocation = 3/*adUseClient*/
connection.ConnectionString = 连接串
connection.Errors = 错误集合
connection.Errors.count = 错误数
connection.Errors(.(0) = 参数指定错误索引
connection.Errors() = !ado_conn_err.
findTableObject() = !ADOX_Catalog_tbl.
findTableObject(.("表名") = 返回表对象
eachTableObject = @for(tbName,tbType,tbObj in ??.eachTableObject() ){
	
}
eachTableObject() = !ADOX_Catalog_tbl.
setPassword(.("新密码","旧密码") = 修改数据库密码,\n如果没有旧密码第二个参数可以不写
beginTrans() = 启动事务,返回嵌套层数\n注意如果未调用此函数开启事务，默认每条独立SQL语句仍然会自动创建独立事务
commitTrans() = 提交事务
rollbackTrans() = 回滚事务
end intellisense**/

/**intellisense(!access_rs)
close() = 关闭记录集 
eof = 是否移动到尾部 
end intellisense**/

/**intellisense(!access_cmd)
commandText = 要执行的SQL命令语句
commandType = 命令类型\n普通SQL为1，存储过程为4
execute(.(ra,parameters.option) = 执行查询,所有参数可选\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值 \n参数options 一个或者多个 CommandTypeEnum 或 ExecuteOptionEnum 值 
executeStream(.(ra,parameters) = 执行查询返回流,所有参数可选\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值
executeNoRecords(.(ra,parameters) = 执行查询不返回记录集,所有参数可选\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值
cancel() = 取消执行
getTable() = 执行SQL命令返回查询结果集数组\n每个元素代表一行,每行由N个键值对列组成\n返回值的fields成员记录了所有数据列的字段名\n结果集为空也会返回空数组,使用#操作符获取结果集长度
getRows() = 查询并返回包含结果数据的SafeArray数组\n此函数返回的数据是先列后行,与getTable相反,\n数组由所有列的数据组成,每个列都是由多个行组成的数组\n可以使用 返回数据[ 列号 ][ 行号 ] 获取对应的值,\n也可以使用 数据.字段名[列号] 获取对应的值\n\n返回数组的元表中使用recordCount记录了总行数,\n结果集为空也会返回空数组,可使用 @.recordCount判断记录集的行数\n元表中的fields字段则是记录了所有字段名的数组.\n\n所有值后面的占位空白没有去除\n可使用getTable函数获取处理过空白占位符的数据,\n或参考该函数源码自行处理尾部空白
stepQuery() = 执行SQL命令\n返回查询结果集第一行的数据,由键值对列组成\n无数据返回null
each() = @for(rs in ??.each() ){ 
	io.print( rs(0).value );__/*执行SQL命令并遍历结果集*/
}
createParameter(.(名字,字段类型,参数类型,字段大小,参数值) = 创建参数\n参数中用到的常量请使用COM对象的成员常量获取,\n例如cmd.adLongVarWChar\n这个函数创建的参数还需要调用parameters.append添加到执行参数中\n\n不建议直接调用此函数，可以使用更简单的addParameter或bind函数添加参数
parameters = 参数列表
parameters.append(.(参数对象) = 添加参数\n注意access的参数对象虽然必须设置正确的名字,\n但实际上他只按添加顺序执行
parameters.count() = 返回参数长度
parameters.delete(.(索引) = 删除参数
parameters.refresh() = 刷新参数
parameters("__/*参数名*/") = 返回已添加的参数
bind(__) = 参数是一个表,\n可使用多个键值对批量指定SQL命令对象的多个参数,\n此函数会按参数在SQL中出现的顺序逐个添加参数,\nSQL语句中所有参数必须使用@前缀命名,\n此函数返回命令对象自身
addParameter( = 调用createParameter创建参数对象,\n设置值并添加到SQL执行参数中\n注意access的参数对象虽然必须设置正确的名字,\n但实际上他只按添加顺序执行
addParameter(.(名字,值,参数类型,字段类型,字段大小) = 直接添加SQL执行参数\n除名字，值以外的其他参数不必要设置\n返回创建的参数对象\n\n\n注意access的参数对象虽然必须设置正确的名字,\n但实际上他只按添加顺序执行
bind() = !access_cmd.
addParameter() = !access_parameter.
end intellisense**/

/**intellisense(!access_parameter) 
value = 参数值
direction = 参数的类型，默认是adParamInput
name = 参数名称
size  = 数据长度
end intellisense**/

/**intellisense(!ado_conn_err)
Description = 错误描述
Number = 错误代码
Source = 错误对象
SQLState = 数据源 
NativeError = 数据源错误代码
end intellisense**/

/**intellisense(!ADOX_Catalog_tbl)
Columns = 列集合
Columns.Count = 总数
Columns(.("列名") = 参数也可以写数值索引
Columns() = !ADOX_Catalog_column.
end intellisense**/

/**intellisense(!ADOX_Catalog_column)
Name = 列名
Type = 类型
DefinedSize = 字段大小 
end intellisense**/