import com; import string.list; import time.ole; class sqlServer { ctor( param,...){ var connectionString; if( type(param) == type.table ){ var tList = ..string.list(,";","=") tList.mixin(param) if( !tList.find("DSN") ){ if( !tList.find("Provider") ) tList.Provider="SQLOLEDB.1"; if( !tList.find("Data Source","Server") ) tList["Data Source"]="."; if( !tList.find("Integrated Security","Password","PWD") ){ tList["Integrated Security"]="SSPI"; //windows身份验证模式 } }; connectionString = tostring( tList ); } else { if( !param ) error("请指定数据库连接串",2) connectionString = ..string.format( param,... ); } var conn = ..com.CreateObject("ADODB.Connection") conn.ConnectionString = connectionString; var ok,err = call(conn.open,conn); if(!ok) return null,"打开数据库连接失败:" +err; this.connection = conn; ..com.SetPreferredArrayType(this.connection,0xC/*_VT_VARIANT*/); this._tables = {@{_weak="kv"}}; this.codepage = 0; ..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); }; findProcedureObject = function(name){ var cat = this.getCatalog(); cat.Procedures.Refresh(); if(!..string.find(name,"\;\d+$") ) name = name + ";1" for index,tbl in ..com.each(cat.Procedures) { if( tbl.Name = name ){ return tbl; } } }; existsProcedure = function(name){ return !!this.findProcedureObject(name); }; exec = function(sql,...){ if(!sql) error( "参数 @1 必须指定 SQL ,不能是 null 值!",2) if( ... )sql = format(sql,...); var ret,result,recordsAffected = call(this.connection.Execute,this.connection,sql,0,1 | 0x80/*_adCmdText | adExecuteNoRecords*/); if(!ret) error(result,2); return recordsAffected; }; openRecordSet = function(sql,cursorType,lockType,...){ var rs = ..com.CreateObject("ADODB.RecordSet"); ..com.SetPreferredArrayType(rs,0xC/*_VT_VARIANT*/); if( ... )sql = format(sql,...); var ok,err = call(rs.Open,rs,sql,this.connection,cursorType,lockType); if(!ok) return null,err; return _recordSet(rs); }; getRows = function(sql,...){ var rs,err = owner.openRecordSet(sql,,,...); if(!rs) error(err,2); return _getRows(rs,true); }; getTable = function(sql,...){ var data = this.getRows(sql,...); if(data) return _rows2table(data); }; stepQuery = function(...){ return this.getTable(...)[[1]]; }; rowCount = function(){ return this.stepQuery("SELECT @@rowcount")[[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"); ..com.SetPreferredArrayType(cmd,0xC/*_VT_VARIANT*/); 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" { if(..string.isUtf16(value)){ adType = 203/*adLongVarWChar*/ adSize = #value/2; } else { adType = 129 /*adChar*/; adSize = #..string.fromto(value,65001,this.codepage); } } case "number" { adType = 3/*adInteger*/ 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("自动识别类型遇到错误") } } } 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; } cmd.CommandText = ..string.replace(sql,"[\@\?](<:+>|<\w+>)", function(name){ cmd.addParameter(name,parameters[name]); return "?"; //OLEDB不支持命名参数 } ) return cmd; } //已废弃,勿用 return { parameters = function(tParam){ return cmd.bind(tParam); } } }; cmd["executeNoRecords"] = function(ra,parameters){ cmd.execute(ra,parameters,cmd.adExecuteNoRecords); }; cmd[["executeRecords"]] = function(ra,parameters){ var rs = cmd.execute(ra,parameters); if(rs){ return _recordSet(rs); } }; cmd["executeStream"] = function(ra,parameters){ cmd.execute(ra,parameters,cmd.adExecuteStream); }; cmd[["getRows"]] = function(...){ var rs,err = cmd.execute(...); if(!rs) error(err,2); return _getRows(rs,true); }; cmd[["getTable"]] = function(...){ var data = cmd.getRows(...); if(data) return _rows2table(data); }; cmd[["stepQuery"]] = function(...){ return cmd.getTable(...)[[1]]; }; cmd["rowCount"] = function(){ return this.stepQuery("SELECT @@rowcount")[[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; }; 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 sqlServer { _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,'mssql') } 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; return ..string.replace(sql,"<\?\?*>|<\@<:+>|<\w+>>", function(name) { if(name[1]=='@'#){ return escape(param[..string.right(name,-2)]) } 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; _getRows = function(rs,rsClose){ 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(rsClose && !closed ) rs.Close(); data@.recordCount = recordCount; data@.fields = fields; return data; } _rows2table = function(data){ var result = {}; var v; var rowCount = data@.recordCount; if(!rowCount) return result; 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; } _recordSet = function(rs){ rs[["getTable"]] = function(){ var data = _getRows(rs,false); if(data) return _rows2table(data); }; rs[["stepQuery"]] = function(...){ return rs.getTable(...)[[1]]; }; rs[["nextRecordset"]] = function(){ var rs = rs.NextRecordset(); if(rs)return _recordSet(rs); } return rs; } } /**intellisense() sqlServer( 连接参数表 ) = @sqlServer({ \n ["Data Source"] = "127.0.0.1,7788";\n ["Database"] = "数据库名__";\n ["User ID"] = "用户名";\n ["Password"] = "密码"; \n}) sqlServer( "数据库连接语句",其他格式化参数 ) = @sqlServer("Provider=SQLOLEDB.1;Data Source=.\SQLEXPRESS;Database=数据库名;User ID=登录名;Password=登录密码"); 打开数据库连接,\n如果指定的数据库不存在将自动创建数据库文件\n如果存在格式化参数,则调用string.fomat格式化连接串 sqlServer() = !sqlServer. sqlServer.escape(__) = 转义参数指定值为用于 SQL 查询语句的参数化值 sqlServer.escapeId(__) = 如果传入参数是文本则转换为 SQL 标识符,\n返回文本首尾会添加中括号,\n\n如果传入参数是表, 则格式化为 SQL 键值对并以 AND 为分隔符,\n如果表中的值为数组,则格式化为 IN 语句 sqlServer.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(!sqlServer) codepage = 非 Unicode 字符的默认编码 ? = 可直接使用对象的成员获取ADO组件提供的COM常量\n这些常量名的前缀都是 ad 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\n格式化规则请参考 sqlServer.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化 rowCount() = 返回上次执行增、删、改、查等SQL影响的记录行数 openRecordSet(.(sql,游标类型,锁定方法,其他格式化SQL参数) = 打开记录集,游标类型如下:\n0:默认值,启动一个只能向前移动的游标\n1:打开键集类型游标,允许任意移动,并且允许更改记录集\n2:打开动态类型游标,许所有操作,其他用户对记录集的添加、删除、更改在此记录集中\n3:打开原生类型游标\n\n锁定方法如下:\n1:默认值,不能改变数据\n2:悲观锁,编辑时立即锁定数据源的记录\n3:乐观锁,只在调用Update 方法时才锁定记\n4: 乐观批更新 exec(.(sql,其他格式化SQL参数) = 执行 SQL 语句\n格式化参数规则请参考 sqlServer.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化。\n\n执行失败抛出异常。\n执行成功返回受影响的记录行数 createCommand(.(sql,其他格式化SQL参数) = 创建命令对象\n格式化规则请参考 sqlServer.format 函数说明,\n格式化参数可以是一个表参数,用于替换SQL中占位符指定的参数,\nSQL语句用中@前缀标明命名参数使用表参数中的名值对格式化,\nSQL语句中的?或??占位符使用表参数中的数组值格式化 existsTable(.("表名") = 判断数据库中是否存在指定表 existsProcedure(.("存储过程名") = 判断数据库中是否存在指定存储过程 each(sql,其他格式化参数) = @for(rs in ??.each("select * from [表名]") ){ io.print( rs("字段名").value );__ } openRecordSet() = !sqlServer_rs. each() = !sqlServer_rs. createCommand() = !sqlServer_cmd. connection = 连接对象 connection.ConnectionString = 连接串 connection.Errors = 错误集合 connection.Errors.count = 错误数 connection.Errors(.(0) = 参数指定错误索引 connection.Errors() = !ado_conn_err. findTableObject() = !ADOX_Catalog_tbl. findTableObject(.("表名") = 返回表对象 findProcedureObject(.("存储过程名") = 返回存储过程对象 eachTableObject = @for(tbName,tbType,tbObj in ??.eachTableObject() ){ } eachTableObject() = !ADOX_Catalog_tbl. beginTrans() = 启动事务,返回嵌套层数\n注意如果未调用此函数开启事务,默认每条独立SQL语句仍然会自动创建独立事务 commitTrans() = 提交事务 rollbackTrans() = 回滚事务 end intellisense**/ /**intellisense(!sqlServer_rs) close() = 关闭记录集 eof = 是否移动到记录集尾部,没有更多数据了 bof = 是否已经到记录集开始,没有更多数据了 recordCount = 获取记录总数,\n如果该值为-1,请修改为静态方式打开记录集 move(.(numRecords,start) = 移动游标位置,\n注意第一条记录游标位置为0,\n@start为可选参数,可选使用以下数值:\n _adBookmarkCurrent 0 自当前记录开始\n _adBookmarkFirst 1 自首条记录开始\n _adBookmarkLast 2 自最后一条记录开始 GetRows( = 查询并返回包含结果数据的数组\n此函数返回的数据是先列后行,与getTable相反,\n数组由所有列的数据组成,每个列都是由多个行组成的数组\n可以使用 返回数据[ 列号 ][ 行号 ] 获取对应的值 GetRows(.(rows,start,fields) = 查询并返回包含结果数据的数组,所有参数可选,\nrows 指定获取行数,默认为-1,即获取所有剩下的行,0,\n@start为可选参数,可选使用以下数值:\n _adBookmarkCurrent 0 自当前记录开始\n _adBookmarkFirst 1 自首条记录开始\n _adBookmarkLast 2 自最后一条记录开始\n@ields 可选用一个字符串、或字符串数组指定要获取的列,\n也可用数值,或数值数组指定要获取的列 nextRecordset() = 关闭当前结果集并打开下一个结果集。\n注意要小写函数名的第一个字母。\nnextRecordset 封装了大写首字母的 COM 函数 NextRecordset,\n对返回的记录集对象添加了 getTable,stepQuery 等函数。\n!sqlServer_rs. getTable() = 执行SQL命令返回查询结果集数组\n每个元素代表一行,每行由N个键值对列组成\n返回值的fields成员记录了所有数据列的字段名\n结果集为空也会返回空数组,使用#操作符获取结果集长度 stepQuery() = 执行SQL命令\n返回查询结果集第一行的数据,由键值对列组成\n无数据返回null end intellisense**/ /**intellisense(!sqlServer_cmd) commandText = 要执行的SQL命令语句 commandType = 命令类型\n普通SQL为1,存储过程为4 execute(.(ra,parameters.option) = 执行查询,所有参数可选。\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值 \n参数options 一个或者多个 CommandTypeEnum 或 ExecuteOptionEnum 值。\n返回记录集对象,execute 返回的记录集没有 getTable,stepQuery 函数。\nexecuteRecords 返回的记录集提供 getTable,stepQuery 函数。 executeStream(.(ra,parameters) = 执行查询返回流,所有参数可选。\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值 executeNoRecords(.(ra,parameters) = 执行查询不返回记录集,所有参数可选。\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值 executeRecords(.(ra,parameters.option) = 执行查询,所有参数可选。\n参数ra 返回受查询影响的记录的数目 \n参数parameters 用 SQL 语句传递的参数值 \n参数options 一个或者多个 CommandTypeEnum 或 ExecuteOptionEnum 值。\n返回记录集对象,execute 返回的记录集没有 getTable,stepQuery 函数。\nexecuteRecords 返回的记录集提供 getTable,stepQuery 函数。 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 rowCount() = 返回上次执行增、删、改、查等SQL影响的记录行数 each() = @for(rs in ??.each() ){ io.print( rs(0).value );____/*执行SQL命令并遍历结果集\nrs为 ADODB.Recordset 对象,即ADO 表记录集\n可以使用 rs("字段名").value自记录集中提取数据,\n更多用法请参考 ADO 文档。*/ } 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注意如果直接使用这个函数添加参数,\nSQL语句中应当用?号作为参数占位符,\n参数只按添加顺序匹配SQL中的参数 addParameter(.(名字,值,参数类型,字段类型,字段大小) = 直接添加SQL执行参数\n除名字,值以外的其他参数不必要设置\n返回创建的参数对象\n\n\n注意access的参数对象虽然必须设置正确的名字,\n但实际上他只按添加顺序执行 bind() = !sqlServer_cmd. addParameter() = !sqlServer_parameter. parameters() = !sqlServer_parameter. execute() = !sqlServer_rs. executeRecords() = !sqlServer_rs. end intellisense**/ /**intellisense(!sqlServer_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**/