	public  prikey		'表的主键
	public  tableName	'表名
	public  auto_		'自动填充
	public  validate_	'自动验证
	Public db_type,access_type,db_path,db_host,db_user,db_name,db_pwd,db_prefix

	Public  patchValidate	'是否批处理验证
	public  [error]			'最近错误消息
	public 	pageSize		'默认每页取多少条记录
	
	public Version			'版本号
	
	Public isOnlySql		'是否只获取sql语句
	
	Public tool				'POPASP_DATABASE_TOOL实例
	
	Private options			'存储分配过来的参数，Dictionary对象
	Private parsedOptions	'存储解析后的参数，Dictionary对象
	Private lastSql			'存储最后一条sql语句，String
	Private ConnStr			'数据库连接字符串
	Private dTables			'存储已经取出的所有的数据表
	Private dTS				'存储数据表的结构，为dTableStructrues的简写
	public isTest
	Public Function getLastSql()
		getLastSql = lastSql
	End Function
	
	'获取parsedOptions("data")
	'如果使用了db.create，则可以使用getData获取最终过滤或添加后的数据，该数据为一维Dictionary对象
	Public Function getData()
		if isObject( parsedOptions("data") ) then
			set getData = parsedOptions("data")
		else
			getData = parsedOptions("data")
		end if
	end Function
	
	' select 查询多条记录
	' 与连贯操作相结合使用
	' db.select
	' db.field().order().where().page().select
	' 分页需要使用db.page
	Public Function [Select] ()	
		call parseOptions		
		lastSql = getSelectSql(parsedOptions)	
		if isOnlySql then [Select] = lastSql : Exit Function		
		set [Select] = tool.Select(lastSql,parsedOptions,Me.pageSize)
		call ResumeOpts
	End Function
	
	' select_map 查询多条记录，并使用回调函数处理
	' 与连贯操作相结合使用
	' db.select_map( callback )
	' db.field().order().where().page().select_map
	' 分页需要使用db.page
	Public Property Get select_map ( callback )
		dim rs
		call parseOptions		
		lastSql = getSelectSql(parsedOptions)	
		if isOnlySql then [select_map] = lastSql : Exit Property		
		set rs = tool.Select(lastSql,parsedOptions,Me.pageSize)
		do while not rs.eof
			Execute( "call " & callback & "(rs)" )
			rs.movenext
		loop
		rs.close : set rs = nothing
		call ResumeOpts
	End Property
	
	' find 查询一条记录
	' 与连贯操作相结合使用
	' db.find
	' db.field().order().where().position().find
	' 逐条显示需要使用db.position
	Public Function Find()
		call parseOptions
		lastSql = getFindSql( parsedOptions )
		if isOnlySql then Find = lastSql : Exit Function
		set Find = tool.Find( lastSql ,parsedOptions )		
		call ResumeOpts
	End Function
	
	' 根据查询条件，判断是否存在记录
	' 与连贯操作相结合使用
	' db.RecordExists
	' db.field().order().where().position().find
	' 逐条显示需要使用db.position
	Public Function RecordExists()
		dim rs
		set rs = Find
		if isOnlySql then
			RecordExists = Find
		else
			RecordExists = not rs.eof
		end if		
		Call tool.closeRS(rs)
	End Function
	
	' getObject 以实例化类的形式返回一条记录
	' 通过find来获取的，将字段名作为属性名
	Public Function getObject()
		dim rs
		if isOnlySql then getObject = Me.find : Exit Function
		set rs = Me.find : set getObject = P_("POPASP_SELF_OBJECT").rs2object(rs,isAccess)
		Call tool.closeRS(rs)
	End Function
	
	' getObject2 以实例化类的形式返回一条记录
	' 通过select来获取的，将记录的第一个值作为属性名，将记录第二个值作为值
	Public Function getObject2()
		dim rs
		if isOnlySql then getObject2 = Me.select : Exit Function
		set rs = Me.select
		set getObject2 = P_("POPASP_SELF_OBJECT").rs2object(rs,isAccess)
		Call tool.closeRS(rs)
	End Function
	
	'同find，返回一维Dictionary对象
	Public Function getRow( )
		dim rs
		if isOnlySql then getRow = Me.find : Exit Function
		set rs = Me.find 
		set getRow = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		Call tool.closeRS(rs)
	End Function
	
	'获取字符值,如果fields为""，则取field方法fieldRev中的值
	'返回数据为Dictionary对象
	Public Function getFields(fields)
		dim rs
		call field( fields )	
		if isOnlySql then getFields = Me.find : Exit Function
		set rs = Me.find : set getFields = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		Call tool.closeRS(rs)
	End Function
	
	'获取一个字段值
	'取一个字段的值，如果分配多个，只取第一个，取多个字段的值请使用getRow
	Public Function getOne()
		dim rs
		if isOnlySql then getOne = Me.find : Exit Function
		set rs = Me.find
		if not rs.BOF and not rs.EOF then
			if rs.fields.count > 0 then
				getOne = rs.Fields(0).Value
			end if
		end if
		Call tool.closeRS(rs)
	End Function
	
	'获取字段值,如果fields为""，则取field方法fieldRev中的值
	'取一个字段的值，如果分配多个，只取第一个，取多个字段的值请使用getFields
	Public Function getField(fields)
		call field( fields )
		getField = Me.getOne
	End Function
	
	'获取键值对，返回数据为Dictionary对象
	'结合field()使用，必须传入两个字段名，第1个为键，第2个为值。
	Public Function getKeyValue()
		dim rs
		if isOnlySql then getKeyValue = Me.Select : Exit Function
		set rs = Me.select
		set getKeyValue = P_("POPASP_RECORDSET").rs2dict2(rs)
		Call tool.closeRS(rs)
	End Function

	'使用同select，返回二维Dictionary对象，键名为主键值
	Public Function getPKAll()
		dim rs,bool
		if not isEmpty(options("page")) then
			bool = 1
		else
			bool = 0
		end if
		if isOnlySql then getPKAll = Me.Select : Exit Function
		set rs = Me.select
		set getPKAll = P_("POPASP_RECORDSET").rs2data(rs,getPK(),isAccess,bool)

		Call tool.closeRS(rs)
	End Function	
	
	'获取二维Dictionary对象
	Public Function getAll()
		on error resume next
		dim rs,dict,bool
		if not isEmpty(options("page")) then
			bool = 1
		else
			bool = 0
		end if
		if isOnlySql then getAll = Me.Select : Exit Function
		set rs = Me.select
		set dict = P_("POPASP_RECORDSET").rs2data(rs,"",isAccess,bool) 
		set getAll = dict
		Call tool.closeRS(rs)
	End Function
	
	'获取二维xml对象
	'neednull = 1 则去掉空白，否则保留内容为空的标签
	Public Function xml( ByRef packageName , neednull )
		on error resume next
		dim rs,dict,bool
		if isOnlySql then xml = Me.Select : Exit Function
		set rs = Me.select
		xml = P_("POPASP_RECORDSET").rs2xml(rs,packageName , neednull) 
		Call tool.closeRS(rs)
	End Function
	
	'getArr，同getAll，返回的是数组(数组内每个元素是Dictionary对象)
	Public Function getArr()
		dim rs,arr,temp
		if isOnlySql then getArr = Me.Select : Exit Function
		set rs = [Select]
		Do While Not rs.BOF And Not rs.EOF
			if rs.fields.count > 1 then
				temp = Array()
				for i = 0 to rs.fields.count-1
					POP_MVC.Arr.Push temp,rs.Fields(i).Value 
				next
				POP_MVC.Arr.Push arr,temp
			elseif rs.fields.count = 1 then
				POP_MVC.Arr.Push arr,rs.Fields(0).Value 
			end if
			rs.MoveNext
		LOOP
		Call tool.closeRS(rs)
		getArr = arr
	End Function

	'get1Arr，返回的是一维数组
	'只取第一个字段，且只取一页
	Public Function get1Arr()
		dim rs,arr,j
		if isOnlySql then get1Arr = Me.Select : Exit Function
		set rs = [Select]
		j = 0
		arr = Array()
		Do While Not rs.BOF And Not rs.EOF
			if j = rs.pagesize then exit do
			POP_MVC.Arr.Push arr,iif( isnull(rs.Fields(0).Value) , "" , rs.Fields(0).Value)
			rs.MoveNext
			j = j + 1
		LOOP
		Call tool.closeRS(rs)
		get1Arr = arr
	End Function		
	
	
	'通过连贯操作，从数据表中随机取出num行记录，返回二维Dictionary对象
	'arg如果为数字，则对应num取出N条记录，如果为数组Array(num,sort)
	'sort值分三种，0:打乱的顺序，-1:逆序，1:顺序
	Public Function getRand( byval arg )		
		dim rs : set rs = [Select]() 
		dim arr,dict,cnt,curRand,total,i,sort,num,bool
		dim startTime : startTime = timer()
		sort = 0
		
		if not isEmpty(options("page")) then
			bool = 1
		else
			bool = 0
		end if
		
		if TypeName( arg ) = "Dictionary" Then
			arg = arg.items
		End if
		
		if isArray( arg ) then
			if ubound( arg ) > 0 then
				sort = arg(1)
			end if
			num = arg(0)
		else
			num = arg			
		end if
		
		num = CInt(num)

		if isOnlySql then getRand = lastSql : Exit Function
		if rs.recordCount <= num then
			set getRand = P_("POPASP_RECORDSET").rs2data(rs,"",isAccess,bool)
		else
			arr = array()			
			cnt = 0
			total = rs.recordCount
			Randomize			
			Do Until cnt >= num				
				curRand = CLng((Rnd*(total-1))+1)
				if not POP_MVC.Arr.Exists( arr,curRand ) Then
					POP_MVC.Arr.Push arr,curRand
					cnt = cnt + 1
				end if
			Loop
			
			set dict = D_		
			if sort > 0 then
				Call POP_MVC.Arr.sort(arr)					
			elseif sort < 0 then
				Call POP_MVC.Arr.rsort(arr)
			end if			

			'不要将取数据与取随机数合并，这样做更省时
			for i = 0 to ubound(arr)
				rs.AbsolutePosition =arr(i)
				set dict( i ) = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
			next
			set getRand = dict
		end if
		Call tool.closeRS(rs)
		call POP_MVC.pushTime( startTime ,  "数据库随机取" & num & "条记录" )
	End Function
	
	' 定位查询，获取第N条记录，返回Dictionary对象
	' num正数从1开始计，负数则-1表示最后1条
	Public Function getN( num )
		dim rs
		if isOnlySql then getN = [Select]()  : Exit Function
		set rs = [Select]() 
		if rs.RecordCount = 0 then 
			set getN = D_ : exit Function
		end if
		if num < 0 then
			num = rs.RecordCount + num + 1
		end if
		if num < 1 then num = 1
		rs.absolutePosition = num
		set getN = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		Call tool.closeRS(rs)		
	End Function
	
	' 获取第一条记录，返回Dictionary对象
	Public Function First()
		dim rs
		if isOnlySql then First = [Select]() : Exit Function
		set rs = [Select]() 
		if not rs.BOF then rs.moveFirst
		set First = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		Call tool.closeRS(rs)
	end Function
	
	' 获取最后一条记录，返回Dictionary对象
	Public Function Last()
		dim rs
		if isOnlySql then Last = [Select]() : Exit Function
		set rs = [Select]()
		if not rs.EOF then rs.moveLast
		set Last = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		Call tool.closeRS(rs)
	end Function
	
	' count计数
	Public Property Get Count()				
		Count = Statistics( "0" , "COUNT")
	End Property
	
	' Max取最大值
	Public Property Get Max( field )
		Max = Statistics(field , "MAX")
	End Property
	
	' Min取最小值
	Public Property Get Min( field )
		Min = Statistics(field , "MIN")
	End Property
	
	' Avg取平均值
	Public Property Get Avg( field )	
		Avg = Statistics(field , "AVG")
	End Property
	
	' Sum求和
	Public Property Get Sum( field )
		Sum = Statistics(field , "SUM")
	End Property
	
	Public Function Statistics( ByVal oneField, ByVal stat )
		'去掉多余的`[]
		oneField = POP_MVC.String.trim(oneField,"`")
		oneField = POP_MVC.String.ltrim(oneField,"[")
		oneField = POP_MVC.String.rtrim(oneField,"]")

		if not isNumeric(oneField) then
			if tool.isMicroDB then
				oneField = "[" & oneField & "]"
			else
				oneField = "`" & oneField & "`"
			end if			
		end if		
		
		call field( stat & "( " & oneField & " ) as popasp___result")		

		If Me.db_type = "mysql" then
			Statistics =  Cstr( getOne )
		else
			Statistics = getOne	
		end if		
	End Function
	
	' mode为1时新增，2时修改，3时新增/修改 
	Public Property Get Create(  mode )
		On Error Resume Next
		dim key,data
		call parseOptions	'解析参数
		Create = false
		if isEmpty( parsedOptions("data") ) Then
			set parsedOptions("data") = POP_MVC.Form2Dict()
		end If
		
		set data = parsedOptions("data")
		
		if not AutoValidate( data , mode ) Then
			Create = False
			exit Property
		End If			
		call AutoComplete( data , mode )
		set parsedOptions("data") = data
		Create = true
		call L_( Me.db_type & " Create" )
	End Property
	
	' 字段增长 比如传参"views"，相当于 views = views + 1
	' 传参数组，比如 array("views",3)，相当于 views = views + 3
	' 只能处理一个字段的值
	Public Property Get SetInc( arg )
		SetInc = SetIncOrDec( arg, "+" )
	end Property
	
	' 字段增长 比如传参"views"，相当于 views = views + 1
	' 传参数组，比如 array("views",3)，相当于 views = views + 3 
	' 只能处理一个字段的值
	Public Property Get SetDec( arg )
		SetDec = SetIncOrDec( arg, "-" )
	end Property
	
	' 字段取反 比如传参"top"，相当于 top = Abs( Not top )
	' 传参数组，比如 array("top","rmp")，相当于 top = Abs( Not top ),rmp = Abs( Not rmp )
	' 字符串传多个参数，比如"top,rmp"，相当于 top = Abs( Not top ),rmp = Abs( Not rmp )
	Public Property Get SetNot( arg )
		dim arr,i,str		
		if typeName(arg) = "String" then 
			arr = split( arg , "," )
		end if
		if isArray( arr ) then
			str = ""
			if tool.isDbType("sqlserver") then
				for i = 0 to ubound( arr )
					str = str & arr(i) & " = ~ " & arr(i) & " , "
				next
			elseif tool.isDbType("access") then
				for i = 0 to ubound( arr )
					str = str & arr(i) & " = iif( isnull(" & arr(i) & ") , 1 , iif( " & arr(i) & " = 0 ,1, 0) ) " & " , "
				next
			else
				for i = 0 to ubound( arr )
					str = str & arr(i) & " = Abs( Not " & arr(i) & " ) , "
				next
			end if
			
			str = POP_MVC.String.rtrim( str , ", " )

			SetNot = SetField( array("exp", str ),null )
		end if
	end Property
	
	'多个字符用英文逗号分隔
	Private Function SetIncOrDec( ByVal arg , ByRef operator )
		dim arr,i,str
		if typeName(arg) = "String" then 
			arg = array(arg)
		elseif typeName(arg) = "Dictionary" then 
			arg = arg.items
		End if
		
		if isArray( arg ) then
			if ubound( arg ) = 0 then
				POP_MVC.Arr.Push arg , 1
			end if
			if is_numeric(arg(1)) then
				arr = split( arg(0) , "," )
				str = ""
				for i = 0 to ubound( arr )
					if tool.isDbType("excel") then
						str = str & arr(i) & " = Cint(" & arr(i) & ") " & operator & " " & arg(1) & " , "
					else
						str = str & arr(i) & " = " & arr(i) & " " & operator & " " & arg(1) & " , "
					end if					
				next
				str = POP_MVC.String.rtrim( str , ", " )
				SetIncOrDec = SetField( array("exp", str ),"" )
			end if
		end if	
	End Function
	
	'修改字段的值
	'field如果为字符串，则在数据表中设置 set field = value
	'如果为dictionary对象，则设置 set key1 = val1,key2 = val2,……。此时的value值不起作用，可以随便给个值
	Public Function SetField( field,value )
		dim stype: stype = typeName(field)
		dim dict				
		if tool.dataIsExp(field) OR  stype = "Dictionary" then
			Call data( field )
			SetField = Save()
		elseif stype = "String" Then	
			set dict = D_
			dict(field) = value			
			call data(dict)
			SetField = Save()
			set dict = nothing
		else
			call POP_MVC.error( Me.db_type & ".SetField")
		End If
	End Function
	
	' 将两个ID的相应字段进行交换
	' eg. db.where("2,5").field("...").swap()
	Public Property Get Swap()
		On Error Resume Next
		dim arr,dict1,dict2,rs
		
		'如果未进行where赋值，报错并退出
		if isEmpty( options("where") ) Then
			call ResumeOpts
			call POP_MVC.warning( Me.db_type & ".Swap 未进行交换ID赋值")
			Exit Property
		End if

		arr = options("where")
		set options("where") = nothing
		
		'如果分配的是字符串，用逗号将其炸开
		If typename( arr ) = "String" then
			arr = split( arr , "," )
			arr(0) = trim( arr(0) )
			arr(1) = trim( arr(1) )
		end if
		
		'必须是Array，且只能有两个ID
		if isArray( arr ) then
			'如果不是两个元素
			if ubound( arr ) <> 1 then
				call ResumeOpts
				call POP_MVC.warning( Me.db_type & ".Swap 有且只能有两个ID")
				Exit Property
			end if
			
			'如果两个ID不是数值表达形式
			if not is_numeric( arr(0) ) OR not is_numeric( arr(1) ) then
				call ResumeOpts
				call POP_MVC.warning( Me.db_type & ".Swap 交换ID必须是数字")
				Exit Property			
			end if
		else
			call ResumeOpts
			call POP_MVC.warning( Me.db_type & ".Swap ID分配错误")
			Exit Property		
		end if
		
		'获取第1个ID的字段值
		options("where") = arr(0)
		
		
		
		set rs = Me.find()	'第一个用find获取，可以兼顾db.field与db.fieldRev
		
		
		
		set dict1 = P_("POPASP_RECORDSET").rs2dict(rs,isAccess)
		

		Call tool.closeRS(rs)
		dict1.remove( Me.getPK )
		
		'获取第2个ID的字段值
		options("where") = arr(1)		
		
		set dict2 = Me.getFields( dict1.keys ) '第二个用getFields获取，则与第一个有相同字段
		
		'将第2个ID的字段值给第1个ID
		options("where") = arr(0)
		Swap = Me.setField( dict2,null )
		
		set dict2 = nothing		
		if Swap > 0 then		
			'将第1个ID的字段值给第2个ID
			options("where") = arr(1)
			Swap = Me.setField( dict1,null )
		end if
		set dict1 = nothing
		Call L_( Me.db_type & ".Swap" )
	End Property
	
	' 修改数据，根据sql修改
	Public Property Get Save()		
		On Error Resume Next
		dim popts,pk,ts	
		call parseOptions	'解析参数			
		set popts = parsedOptions

		if is_empty(popts("table")) then
			Call setPoptsTable( popts )	
		end if


		
		if isEmpty( popts("data") ) Then
			set popts("data") = POP_MVC.Form2Dict()
		end If
	
		if ( not is_empty( popts("data") )) Then
			isTest = 1	
			
			pk = getPrikey( POP_MVC.String.rtrim(popts("table"),"$"))
			
			popts("table") = getTableFromInput( popts("table") )

			if pk<>"" AND isEmpty( popts("where") ) Then
				if tool.isDbType("excel") then
					set ts = getTableStructure(popts("table"))
					If (Not isEmpty( popts("data")(pk) )) Then popts("where") = "[" & pk & "] = " & tool.getSqlStr( ts(pk),popts("data")(pk) )
				elseif tool.isMicroDB() then
					If (Not isEmpty( popts("data")(pk) ) ) Then popts("where") = "[" & pk & "] = " & popts("data")(pk)
					If (Not isEmpty( popts("data")( LCase(pk) ) ) ) Then popts("where") = "[" & pk & "] = " & popts("data")( LCase(pk) )
				else
					If (Not isEmpty( popts("data")(pk) )) Then popts("where") = "`" & pk & "` = " & popts("data")(pk)
				end if
			End If	
				

				
			if Not isEmpty( popts("where") ) Then				
				if pk<>"" and typename( popts("data") ) = "Dictionary" Then
					if popts("data").Exists( pk ) AND popts("data").count > 1 then
						Call popts("data").Remove( pk )
					end if
					if popts("data").Exists( LCase(pk) ) AND popts("data").count > 1 then
						Call popts("data").Remove( LCase(pk) )
					end if
				End If
				lastSql = getUpdateSql(popts)
	
				if isOnlySql then Save = lastSql : Exit Property
				Save = tool.Save( lastSql )
			else
				call POP_MVC.exit( "在你的修改操作中未发现where或ID限制条件，不含限制的修改操作被禁用！！" )
			End If
		else 
			call POP_MVC.error( Me.db_type & ".Save")
		end If	
		call ResumeOpts
	End Property
	
	' 删除数据
	Public Property Get Remove()
		call parseOptions	'解析参数
		Call setPoptsTable( parsedOptions )
		if tool.isDbType("excel") Then	'对于Excel不能真删除，只能将各个字段值设为null
			Remove = DeleteBySql( parsedOptions("table"),parsedOptions("where") )
		else
			lastSql = getDeleteSql(parsedOptions)
			if isOnlySql then Remove = lastSql : Exit Property
			Remove = tool.Remove(lastSql,parsedOptions)
			call ResumeOpts
		end if
	End Property
	
	' 更改记录，data为Dictionary对象，其键名与字段名相对应
	Public Function Update(ByVal table,ByVal data,ByVal where)
		dim pk,ts
		table = getTableFromInput(table)		
		call handlerData( table,data,0 ) '剔除不存在的字段
		
		pk = getPrikey(table)			'获取主键
		if is_empty(where) then			'如果where为空		
			if pk<>"" AND data.Exists( pk ) then
				if tool.isDbType("excel") then
					set ts = getTableStructure(table)
					where = "[" & pk & "] = " & tool.getSqlStr( ts(pk),data(pk) )
				elseif tool.isMicroDB() then
					where = "[" & pk & "] = '" & data( pk ) & "'"
				else
					where = "`" & pk & "` = '" & data( pk ) & "'"
				end if
			end if
		else
			where = getWhere( table,where )
		end if

		if where <> "" then
			lastSql = "UPDATE " & table & " (采用Recordset更新) " & " WHERE " & where
		else
			lastSql = "UPDATE " & table & " (采用Recordset更新) "
		end if
		if isOnlySql then Update = lastSql : Exit Function
		Update = tool.Update( table,data,where,pk )	'tool.update已经考虑where为空的报错
	End Function
	
	' 删除记录，一定要输入where
	Public Function Delete(table,where)
		table = getTableFromInput(table)
		where = getWhere( table,where )	
		if tool.isDbType("excel") Then
			Delete = DeleteBySql( table,where )
		else
			if where <> "" then
				lastSql = "DELETE FROM " & table & " WHERE " & where
			else
				lastSql = "DELETE FROM " & table
			end if
			if isOnlySql then Delete = lastSql : Exit Function
			Delete = tool.Delete( table,where )
		end if
	End Function
	
	' 执行sql语句，适用于update、delete，如果要添加数据，最好使用insert，可以返回最后生成的ID
	' 该函数还不太完善
	Public Property Get Exec( sql )
		lastSql = sql
		Exec = tool.Execute( sql )
	End Property
	
	' 根据类变量tableName来获取主键
	Public Function getPK()
		getPK = getPrikey(tableName)
	End Function	
	
	'根据SQL获取结果集
	Public Function getRS(sql)		
		lastSql = sql
		dim rs
		set rs = tool.getRS(sql)
		call parseOptions
		Call tool.setRsPageSize( rs , parsedOptions , Me.pageSize )
		Call tool.setRsPosition( rs , parsedOptions )
		call ResumeOpts
		set getRS = rs
	End Function
	
	' 获取主键，如果分配了主键，则取该值，否则返回第一个字段
	Public Function getPrikey( byval table_name )
		on error resume next
		table_name = getTrueTableName(table_name)
		if tool.isDbType("excel") Then table_name = POP_MVC.String.rtrim( table_name,"$" )		
		if POP_MVC.String.iEqual(tableName,table_name) AND not isEmpty( prikey ) Then
			getPrikey = prikey
		else
			call getTableStructure(table_name)	

			getPrikey = tool.getPrikey( table_name,dTS )
			
			if tableName = table_name then
				prikey = getPrikey
			end if
		end if
	End Function
	
	'获取select的sql语句
	'如果配置NOT_USE_RS_PAGE=1，则mysql与sqlite3使用limit分页而非rs，P_("page")("不使用rs，这儿要使用count获取总数").show
	'sqlserver2012使用offset分页， 其他仍然使用rs分页
	Public Function getSelectSql( byref popts )	
		if (  not isEmpty( popts("table") ) AND  popts("table") <> "" ) Then
			getSelectSql = tool.getSelectSql( popts ,popts("table")  )
		elseif (  not isEmpty( tableName ) AND  tableName <> "" ) Then
			getSelectSql = tool.getSelectSql( popts , tableName )
		else 
			call POP_MVC.error( db_type & ".getSelectSql")
		end If	
		
		if NOT is_empty( POP_MVC.config("NOT_USE_RS_PAGE") ) then
			getSelectSql = getSelectSql & getPageStr( popts )
		end if	
	End Function
	
	Private function getPageStr( popts )		
		dim pagesize_,bool,page
		if not isEmpty( popts("page") ) Then			
			if ubound( popts("page") ) >0 Then		'如果有两个元素
				if POP_MVC.String.reg_test(  popts("page")(1) , "^[1-9]\d*$" , "" ) then
					pagesize_ = popts("page")(1)	
				end if
			elseif POP_MVC.String.reg_test( POP_MVC.get( POP_MVC.config("VAR_PAGESIZE") ) , "^[1-9]\d*$" , "" ) then
				pagesize_ =  POP_MVC.get( POP_MVC.config("VAR_PAGESIZE") )
			End If
			
			'如果分配页码为null，则取当前页AbsolutePage
			if isNull(popts("page")(0)) then	
				bool = true
			elseif typename( popts("page")(0) ) = "String" then	'如果是字符串
				if LCase( popts("page")(0) ) = "null" then
					bool = true
				end if
			end if
			
			if bool then				 
				if POP_MVC.String.reg_test( POP_MVC.Get( POP_MVC.config("VAR_PAGE") ) , "^[1-9]\d*$" , "" ) then	
					page = CLng( POP_MVC.Get( POP_MVC.config("VAR_PAGE") ) )
					
					if err.number <> 0 then
						page = 1
					end if
					
					if page < 1 then
						page = 1
					end if
				else	'如果不是数字，则取第1页
					page = 1
				end if					
			end if
			if tool.isSqlEgt2012 then
				getPageStr = " OFFSET " & (page-1) * pagesize_ & " ROWS FETCH NEXT " & pagesize_ & " ROWS ONLY"
			elseif tool.isDbType("mysql") or tool.isDbType("sqlite3") then
				getPageStr = " LIMIT " & (page-1) * pagesize_ & " , " & pagesize_ 
				if popts.exists( "limit" ) then
					popts.remove( "limit" )
				end if
			end if
		elseif ( tool.isDbType("mysql") or tool.isDbType("sqlite3") ) and popts.exists( "limit" ) then
			if not isArray( popts("limit") ) Then
				popts("limit") = array( popts("limit") )
			end if
			if ubound( popts("limit") ) > 0 Then			
				getPageStr = " LIMIT " & popts("limit")(0) & " , " & popts("limit")(1) & " "
			else
				getPageStr = " LIMIT " & popts("limit")(0) & " "
			end if
		End If
	End Function
	
	'获取find的sql语句
	Public Function getFindSql( byref popts )
		If not popts.exists( "position" ) then
			If tool.isMicroDB() then
				popts("top") = 1
			else
				popts("limit") = 1
			end if
		end if
		getFindSql = getSelectSql(popts)
	End Function
	
	'DELETE FROM `post` WHERE post_id>10
	Public Function getDeleteSql( popts )
		if Not isObject(popts) Then
			if popts = "" Then
				call parseOptions
				set popts = parsedOptions
			End if
		End If
		getDeleteSql = tool.getDeleteSql(popts)
	End Function
	
	Private Function getUpdateOrInsertSql( popts , mode)		
		on error resume next
		dim data
		if Not isObject(popts) Then
			if popts = "" Then
				call parseOptions
				set popts = parsedOptions
			End if
		End If
		set data = popts("data")
		call handlerData( popts("table"),data , 0 )
		set popts("data") = data
		if mode = "update" Then	
			getUpdateOrInsertSql = tool.getUpdateSql( popts("table"), popts("data"), popts("where") , getTableStructure( popts("table") ) ) 
		else
			getUpdateOrInsertSql = tool.getInsertSql( popts("table"), popts("data") , getTableStructure( popts("table") ) ) 
		end if
		Call L_( Me.db_type & ".getUpdateOrInsertSql" )
	End Function
	
	'UPDATE `post` SET `title` = '测试3',`add_time` = #2016/6/28 9:57:20#,`is_display` = True WHERE post_id>10
	Public Function getUpdateSql( popts )		
		getUpdateSql = getUpdateOrInsertSql( popts, "update" ) 
	End Function
	
	Public Function getInsertSql( popts )
		getInsertSql = getUpdateOrInsertSql(  popts, "insert" ) 
	End Function
	
	public function safe( str )
		safe = tool.safe(str)
	end function

	' mode为数据表中对应字段的类型，val为添加或修改的值
	Private Function getSqlStr(mode,val)
		getSqlStr = tool.getSqlStr(mode,val)
	End Function
	
	'为了减轻类的重量，将ParseWhere方法中的代码单独写到了一个文件中
	Public Function ParseWhere(opts)		
		'POPASP_REPLACE_CLASS_PARSEWHERE_POPASP'		
	End Function
	
	Public Function ParseHaving( opts )
		ParseHaving = ParseWhere(opts)
	End Function
	
	Public Function ParseField( opts )		
		if opts.Exists("field") then
			ParseField = tool.ParseField( opts,null )
		elseif opts.Exists("fieldRev") then			
			ParseField = tool.ParseField( opts,getTableStructure(getTableFromOptions(opts)) )
		end if		
	End Function
	
	private function getTableFromOptions(opts)		
		if not isEmpty(  opts("table") ) and opts("table") <> "" then
			getTableFromOptions = opts("table")
		elseif not isEmpty(  tableName ) and tableName <> "" then
			getTableFromOptions = tableName
		else
			call POP_MVC.error( db_type & ".getTableFromOptions")
		end if
	end function
	
	private Sub setOption(key,opts)
		POP_MVC.dict.edit options,key,opts
	End Sub	
	
	Public Property Get onlySql( bool )
		isOnlySql = (Not is_empty(bool) ) : set onlySql = Me
	End Property
	
	Public Property Get getTrueTableName( byVal table_name )
		if table_name = "" then
			table_name = Me.tableName
		end if
		if POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) <> "" then
			if Not POP_MVC.String.iStartsWith( table_name , POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) )  then
				table_name = POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) & table_name
			end if
		elseif db_prefix <> "" then
			if Not POP_MVC.String.iStartsWith( table_name , db_prefix )  then
				table_name = db_prefix & table_name
			end if
		end if
		getTrueTableName = table_name
	End Property
	
	' 设置表名，可链式操作
	Public Property Get table( ByVal opts )
		dim table_name : table_name = opts
		table_name = getTrueTableName( table_name )
		Call setOption("table",table_name) : set table = Me
	End Property
	
	Public Property Get top( opts )
		Call setOption("top",opts) : Set top = Me
	End Property
	
	Public Property Get position( opts )
		Call setOption("position",opts) : Set position = Me
	End Property
	
	Public Property Get where( opts )		
		Call setOption("where",opts) : set where = Me		
	End Property
	
	Public Property Get fieldRev( opts )
		Call setOption("fieldRev",opts) : set fieldRev = Me		
	End Property
	
	Public Property Get field( opts )		
		Call setOption("field",opts) : set field = Me
	End Property
	
	Public Property Get [auto]( opts )
		Call setOption("auto",opts) : set [auto] = Me
	End Property
	
	Public Property Get validate( opts )
		Call setOption("validate",opts) : set validate = Me
	End Property
	
	Public Property Get order( opts )
		Call setOption("order",opts) : set order = Me
	End Property
	
	Public Property Get from( ByVal opts )
		if POP_MVC.String.reg_test( opts, "^\w+$" , "" ) then
			if POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) <> "" then
				if Not POP_MVC.String.iStartsWith( opts , POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) )  then
					opts = POP_MVC.config( UCase( db_type ) & "_PREFIX"  ) & opts
				end if
			elseif POP_MVC.config( "DB_PREFIX"  ) <> "" then
				if Not POP_MVC.String.iStartsWith( opts , POP_MVC.config( "DB_PREFIX"  ) )  then
					opts = POP_MVC.config( "DB_PREFIX"  ) & opts
				end if
			end if
		end if
		Call setOption("from",opts) : set from = Me
	End Property
	
	Public Property Get limit( opts )
		Call setOption("limit",opts) : set limit = Me
	End Property
	
	Public Property Get group( opts )
		Call setOption("group",opts) : set group = Me
	End Property
	
	Public Property Get data( opts )
		if typename( opts ) = "Dictionary" then	
			Call setOption("data", POP_MVC.Dict.Clone(opts) ) 
		elseif typename( opts ) = "Recordset" then
			Call setOption("data", P_("POPASP_RECORDSET").rs2data(opts,"",isAccess,0) ) 
		else
			Call setOption("data", opts ) 
		end if
		set data = Me	
	End Property
	
	Public Property Get having( opts )
		Call setOption("having",opts) : set having = Me
	End Property
	
	Public Property Get page( opts )
		if typeName( opts ) = "Dictionary" then
			opts = opts.items
		end if
		Call setOption("page",opts) : set page = Me
	End Property
	
	'暂时只支持一层left join
	Public Property Get leftJoin( opts )
		Call setOption("leftJoin",opts) : set leftJoin = Me
	End Property
	
	Public Property Get union( opts )
		if typename(options) <> "Dictionary" then
			set options = POP_MVC.dict.Create()
		end if
		
		if not options.Exists("union") or typename(options("union") ) <> "Dictionary" then
			set options("union") = POP_MVC.dict.Create()
		end if
		options("union")( options("union").Count ) = opts
		set union = Me
	End Property
	
	Public Property Get unionAll( opts )
		if typename(options) <> "Dictionary" then
			set options = POP_MVC.dict.Create()
		end if
		
		if not options.Exists("unionAll") or typename(options("unionAll") ) <> "Dictionary" then
			set options("unionAll") = POP_MVC.dict.Create()		
		end if
		options("unionAll")( options("unionAll").Count ) = opts
		set unionAll = Me
	End Property
	
	'获取某个表的所有字段，以数组返回
	Function getTableAllFields( ByVal table_name )
		dim ts
		table_name = getTrueTableName(table_name)
		set ts = getTableStructure( table_name)
		getTableAllFields = ts.keys
	end function
	
	'切换数据库
	'文本型数据库可传入密码,若要传入密码，须传入数组Array( dbPath,password )
	'服务器型数据库，无须再设密码，传入空字符串就行
	Public Function db( dbPath )
		if tool.isDbType( "excel" ) or tool.isDbType( "access" ) or tool.isDbType( "sqlite3" ) then
			if isArray( dbPath ) then
				if ubound( dbPath ) > 0 then
					call tool.SwitchDB( dbPath(0) , dbPath(1) )
				else
					call tool.SwitchDB( dbPath(0), "" )
				end if
			else
				call tool.SwitchDB( dbPath, "" )
			end if
		else
			call tool.SwitchSqlDB( dbPath )
		end if
		set db = Me
	end Function
	
	'判断某个字段是否存在
	Public Property Get FieldExists( fieldName )
		dim dFields,arrFields
		FieldExists = False
		set dFields =  getTableFields( Me.tableName )
		arrFields = dFields.keys
		if POP_MVC.arr.iExists( arrFields, fieldName ) then
			FieldExists = True
		end if
	End Property	
	
	'删除一列
	'适用于sqlserver mysql access sqlte
	Public Property Get DropField( fieldName )
		dim dFields,arrFields
		set dFields =  getTableFields( Me.tableName )
		arrFields = dFields.keys
		if POP_MVC.arr.iExists( arrFields, fieldName ) then
			lastSql = "ALTER TABLE " & Me.tableName & " DROP COLUMN "&fieldName
			DropField = exec( lastSql )
		end if
	End Property
	
	'添加一列
	'适用于sqlserver mysql access sqlte
	Public Function AddField( ByVal table_name , fieldName , stype )
		if is_empty(table_name) then 
			table_name = tableName
		end if
		dim dFields,arrFields
		set dFields =  getTableFields( Me.tableName )
		arrFields = dFields.keys

		table_name = getTrueTableName(table_name)
		if Not POP_MVC.arr.iExists( arrFields, fieldName ) then
			lastSql = "ALTER TABLE " & table_name & " ADD " & fieldName & " " & stype
			AddField = exec( lastSql )
		end if
	End Function
	
	'解析查询条件
	Private Sub parseOptions		
		on error resume next
		dim key	,ts , pk
		
		for each key in options				
			if key<>"auto" AND key<>"validate" AND Not isEmpty(options(key)) Then	
				select case key	
					case "unionAll","union"
						Execute( "set parsedOptions(key) = tool.Parse" & key & "( options(key) )" )	
					case "data"
						POP_MVC.Dict.edit parsedOptions , key , options(key)
					case "from","table","leftJoin","order","group","page","position"
						Execute( "parsedOptions(key) = tool.Parse" & key & "( options(key) )" )
					case "having"						
						parsedOptions(key) = ParseHaving( options(key) )
					case "top","limit"
						if tool.isMicroDB then	'如果是微软数据为库，则赋值top
							Execute( "parsedOptions(""top"") = tool.Parse" & key & "( options(key) )" )							
						else	'如果不是微软数据为库，则赋值limit
							Execute( "parsedOptions(""limit"") = tool.Parse" & key & "( options(key) )" )	
						end if
					case "field","fieldRev"	
						parsedOptions(key) = ParseField( options )
					case "where"
						if is_numeric( options(key) ) then						
							if not isEmpty(  parsedOptions("table") ) and parsedOptions("table") <> "" then
								if not tool.isDbType("excel") Then 												
									set ts = getTableStructure( parsedOptions("table") )
									pk = getPrikey(parsedOptions("table"))
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )
									if tool.isMicroDB then
										parsedOptions(key) = "[" & pk & "]" & " = " & getSqlStr( ts(pk) , options(key) ) 
									else
										parsedOptions(key) = "`" & pk & "`" & " = " & getSqlStr( ts(pk) , options(key) ) 
									end if
								else	
									pk = getPrikey(parsedOptions("table"))								
									
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )									
									parsedOptions(key) = "[" & pk & "]" & " = " & options(key) & ""
								end if
							elseif not isEmpty(  tableName ) and tableName <> "" then
								if not tool.isDbType("excel") Then 
									set ts = getTableStructure( tableName )
									pk = getPK()
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )
									if tool.isMicroDB then
										parsedOptions(key) = "[" & pk & "]" & " = " & getSqlStr( ts(pk) , options(key) ) 
									else
										parsedOptions(key) = "`" & pk & "`" & " = " & getSqlStr( ts(pk) , options(key) ) 
									end if
								else
									pk = getPK()
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )
									parsedOptions(key) = "[" & pk & "]" & " = " & options(key)
								end if
							end if
						elseif typename( options(key) ) = "String" or typename( options(key) ) = "IStringList" Then
							If POP_MVC.String.reg_test( options(key) , "^\s*[1-9]\d*\s*(,\s*[1-9]\d*\s*)*$" , "") then
								if not isEmpty(  parsedOptions("table") ) and parsedOptions("table") <> "" then
									pk = getPrikey(parsedOptions("table"))
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )
									if tool.isMicroDB then
										parsedOptions(key) = "[" & pk & "]" & " IN (" & options(key) & ") "
									else
										parsedOptions(key) = "`" & pk & "`" & " IN (" & options(key) & ") "
									end if
								elseif not isEmpty(  tableName ) and tableName <> "" then
									pk = getPK()
									if pk = "" then POP_MVC.exit( "主键为空，不能使用 .where(" & options(key) & ")" )
									if tool.isMicroDB then
										parsedOptions(key) = "[" & pk & "]" & " IN (" & options(key) & ") "
									else
										parsedOptions(key) = "`" & pk & "`" & " IN (" & options(key) & ") "
									end if
								end if							
							else
								parsedOptions(key) = ParseWhere( options(key) )
							end if
						else
							parsedOptions(key) = ParseWhere( options(key) )
						end if	
				End Select
			End If
		next
		if isEmpty( parsedOptions("field") ) Then
			parsedOptions("field") = "*"
		End If
		
		call L_( Me.db_type & " parseOptions" )	
	End sub
	
	Public Sub ResumeOptions()
		set options = nothing  : set options = POP_MVC.dict.Create()
	End Sub
	
	Public Sub ResumeParsedOptions()
		set parsedOptions = nothing	 : set parsedOptions = POP_MVC.dict.Create()
	End Sub
	
	' 重置链接操作的参数，可链式操作
	Public Function ResumeOpts()
		isOnlySql = False : call ResumeOptions : call ResumeParsedOptions : set ResumeOpts = Me
	End Function
	
	'自动验证
	Public Function AutoValidate ( ByVal data,ByRef mode )

		on error resume next
		dim s_valid
		
		if Not isEmpty( parsedOptions("validate") ) Then
			s_valid = parsedOptions("validate")
		ElseIf Not isEmpty( validate_ ) Then
			s_valid = validate_
		End If
	
		POP_MVC.import( "POPASP_AUTOVALIDATE" ).tableName = tableName
		POP_MVC.import( "POPASP_AUTOVALIDATE" ).patchValidate = patchValidate
		AutoValidate = POP_MVC.import( "POPASP_AUTOVALIDATE" ).handle(s_valid,data,mode)	
		
		[error] = POP_MVC.import( "POPASP_AUTOVALIDATE" ).error
		Call L_( Me.db_type & ".AutoValidate" )
	End Function
	
	'自动完成
	Public Sub AutoComplete( ByRef data, ByRef mode )
		on error resume next
		dim s_auto
		if Not isEmpty( parsedOptions("auto") ) Then
			s_auto = parsedOptions("auto")
		ElseIf Not isEmpty( auto_ ) Then
			s_auto = auto_
		End If
	
		call POP_MVC.import( "POPASP_AUTOCOMPLETE" ).handle(s_auto,data,mode)
		call L_( Me.db_type & ".AutoComplete" )
	End Sub
	
	' 设置表名
	Private Sub setPoptsTable( ByRef popts )
		call tool.setPoptsTable(popts,tableName)		
	End Sub
	
	'处理 增/改 中的数据
	'bRemovePk是否删除主键 
	Private Sub handlerData ( ByRef tableName, ByRef data , ByRef bRemovePk )		
		call tool.handlerData(getTableStructure( tableName ),data )
		if not is_empty( bRemovePk ) then
			'剔除主键，2.4版本中增加
			dim pk,pos,keys
			pk = getPrikey( tableName )			
			if not isEmpty( pk ) then
				keys = data.keys
				pos  = POP_MVC.Arr.iSearch( keys,pk )
				if pos > -1 then
					data.remove( keys(pos) )
				end if
			end if
		end if
	End Sub
	
	'获取表名，如果参数为""，则取Me.tableName
	private function getTableFromInput( ByVal table )
		if table = "" Then table = Me.tableName	'如果为""，采用全局变量的表名	
		table = POP_MVC.trim(table,"`")
		table = POP_MVC.ltrim(table,"[")
		table = POP_MVC.rtrim(table,"]")
		getTableFromInput = table
		if tool.isDbType("excel") Then	'Excel的表名还要在后面加"$"
			getTableFromInput = getTableName(table)
		end if
	end function
	
	Private Function getWhere( table,where )
		dim pk,ts
		if is_numeric(where) then
			pk = getPrikey(table)
			if pk<>"" then
				if tool.isDbType("excel") then					
					set ts = getTableStructure(table)
					getWhere = pk & " = " &  getSqlStr( ts(pk) , where ) 
				else
					getWhere = pk & " = " & where
				end if
			end if
		else
			getWhere = parseWhere(where)
		end if		
	End Function
	
	Private Sub Class_Terminate
		on error resume next
		set dTables = nothing
		set dTS = nothing
		set tool = nothing
	End Sub
	
	Private Sub Class_Initialize
		db_type = LCase(POP_MVC.String.ltrim(typename(Me),"POPASP_"))
		
		if db_type = POP_MVC.config("DB_TYPE") then
			access_type = POP_MVC.config("ACCESS_TYPE")
			db_path = POP_MVC.config("DB_PATH")
			db_name = POP_MVC.config("DB_NAME")
			db_host = POP_MVC.config("DB_HOST")
			db_user = POP_MVC.config("DB_USER")
			db_pwd  = POP_MVC.config("DB_PWD")
			db_prefix  = POP_MVC.config("DB_PREFIX")
			Call assignTool( db_type,access_type,db_path,db_host,db_user,db_name,db_pwd,db_prefix )
		end if
		
		Call assignTool( db_type,access_type,db_path,db_host,db_user,db_name,db_pwd , db_prefix )

		call ResumeOpts
		set dTS = POP_MVC.dict.Create()

		patchValidate = False
		pageSize = POP_MVC.config("PAGE_PAGESIZE")

		isOnlySql = false
	End Sub
	
	Public sub assignTool( db_type,access_type,db_path,db_host,db_user,db_name,db_pwd , db_prefix )
		if db_type = "access" or db_type = "excel" or db_type = "sqlite3" or db_type = "txt" then
			set tool = P_( array("database_tool" , db_type & "#" & POP_MVC.realPath(db_path)) )
		else
			set tool = P_( array("database_tool" , db_type & "#" & db_name) )
		end if
		tool.db_type = db_type
		tool.access_type = access_type
		if db_path <> "" then
			tool.db_path = POP_MVC.realPath(db_path)
		end if
		tool.db_name = db_name
		tool.db_host = db_host
		tool.db_user = db_user
		tool.db_pwd  = db_pwd
		tool.db_prefix  = db_prefix
		version = tool.version		
	end sub
	
	'搜索功能，q为搜索的字符串，fields为指定的搜索字段（多个用英文逗号分隔）
	'返回Recordset对象
	'fields不能用""或者*代替
	function Search( ByVal q,ByVal fields )
		dim dict,where_		
		set dict = Me.parseSearch( q,fields )	
		call parseOptions
		if not isEmpty( parsedOptions("where") ) then
			parsedOptions("where") = "( " & parsedOptions("where") & " )  AND " & dict( "sql" )
		else
			parsedOptions("where") = dict( "sql" )
		end if
		lastSql = getSelectSql(parsedOptions)
		if isOnlySql then Search = lastSql : Exit Function		
		set Search = tool.Select(lastSql,parsedOptions,Me.pageSize)
		call ResumeOpts
	end function
	
	
	
	'搜索功能，q为搜索的字符串,table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于search，search是指定字符串进行搜索，该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如search方法
	'即类型为201 202 203的字符串
	'返回sql语句
	function tSearch4sql( q , ByVal table_name )
		dim dict,soFields,fields
		
		call parseOptions
		if isNul( table_name ) then
			table_name = parsedOptions("table")
		end if
		
		set fields = getTableFields( table_name )
		
		for each key in fields
			if fields(key) = 201 or fields(key) = 202 or fields(key) = 203 then
				POP_MVC.Arr.push soFields , key
			end if
		next
		
		if isEmpty(soFields) then
			parsedOptions("where") = "1 = 2"
			parsedOptions("top") = 1
		else		
			set dict = parseSearch( q,soFields )
			
			if not isEmpty( parsedOptions("where") ) then
				parsedOptions("where") = "( " & parsedOptions("where") & " )  AND " & dict( "sql" )
			else
				parsedOptions("where") = dict( "sql" )
			end if
		end if
		lastSql = getSelectSql(parsedOptions)
		tSearch4sql = lastSql
	end function	
	
	
	'全字段搜索功能，类似于Select
	'q为搜索的字符串, table_name 为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于search，search是指定字符串进行搜索，该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如search方法
	'即类型为201 202 203的字符串
	'返回Recordset对象
	function soSelect( q , ByVal table_name )
		Call tSearch4sql( q , table_name )
		if isOnlySql then soSelect = lastSql : Exit Function		
		set soSelect = tool.Select(lastSql,parsedOptions,Me.pageSize)
		call ResumeOpts
	end function
	
	'全字段搜索功能，类似于find，只返回一条记录
	'q为搜索的字符串,table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于 find ，find 是指定字符串进行搜索，该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如find方法
	'即类型为201 202 203的字符串
	'返回Recordset对象
	function soFind( q , ByVal table_name )
		Call tSearch4sql( q , table_name )
		if isOnlySql then soFind = lastSql : Exit Function		
		set soFind = tool.find(lastSql,parsedOptions )
		call ResumeOpts
	end function

	'全字段搜索功能，类似于find，只返回一条记录
	'q为搜索的字符串,table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于 getKeyValue ，getKeyValue是指定字符串进行搜索，该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如 getKeyValue 方法
	'即类型为201 202 203的字符串
	'返回Recordset对象
	function soKeyValue( q , ByVal table_name )
		dim rs
		if isOnlySql then soKeyValue = soFind( q , table_name ) : Exit Function	
		set rs = soFind( q , table_name )
		set soKeyValue = P_("POPASP_RECORDSET").rs2dict2(rs)
		Call tool.closeRS(rs)
	end function
	
	'全字段搜索计数功能，类似于count，返回存在的记录个数
	'q为搜索的字符串,table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于 count ，该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如count方法
	'即类型为201 202 203的字符串
	'返回Recordset对象
	function soCount( q , ByVal table_name )
		dim rs
		call field( "COUNT(0) as popasp___result")
		Call tSearch4sql( q , table_name )
		if isOnlySql then soCount = lastSql : Exit Function	
		set rs = Me.find
		If Me.db_type = "mysql" then
			soCount =  Cstr( rs(0) )
		else
			soCount = rs(0)	
		end if			
		Call tool.closeRS(rs)
		call ResumeOpts
	end function
	
	'全字段搜索功能，类似于RecordExists，返回记录是否存在
	'q为搜索的字符串,table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'该函数不同于RecordExists，RecordExists是指定字符串进行搜索
	'该函数用于搜索所有的文本型字符串
	'由于该函数搜索的是所有文本型字符串，效率不如find方法
	'即类型为201 202 203的字符串
	'返回Recordset对象
	function soExists( q , ByVal table_name )
		dim rs
		set rs = soFind( q , table_name )
		if isOnlySql then 
			soExists = lastSql
		else
			soExists = not rs.eof			
		end if
		Call tool.closeRS( rs )
	end function
	
	'替换功能，查找某个表的所有文本型字段，并进行替换。
	'table_name为搜索的表名,如果为空,则取 B_() 中的表名
	'findStr为查找字符串，repStr为替换字符串
	'由于该函数搜索的是所有文本型字符串，效率较低
	'即类型为201 202 203的字符串
	'返回影响行数
	function soReplace( ByVal table_name , ByVal findStr, ByVal repStr )
		dim dict,repFields,fields
		
		if tool.isDbType("access") and not isOnlySql then		
			soReplace = soReplace4access( table_name , findStr , repStr )
		else		
			call parseOptions
			if isNul( table_name ) then
				table_name = parsedOptions("table")
			end if
			
			set fields =getTableFields( table_name )		

			findStr = tool.safe( findStr )
			repStr = tool.safe( repStr )		

			for each key in fields
				if fields(key) = 201 or fields(key) = 202 or fields(key) = 203 then
					POP_MVC.Arr.push repFields , "[" & key & "] = REPLACE( [" & key & "] , '" & findStr & "' , '" & repStr & "' ) "
				end if
			next
			
			if not isEmpty(repFields) then
				soReplace = SetField( array("exp", join(repFields , " , " ) ),null )
			end if
		end if
	end function	
	
	'解析搜索字符串，返回Dictionary对象
	'比如搜索q="张三 李四"，返回{ "operator": "AND", "sql": "( ( CMS_Content LIKE '%张三%' OR CMS_Tag LIKE '%张三%' ) AND ( CMS_Content LIKE '%李四%' OR CMS_Tag LIKE '%李四%' ) )", "option": [ "张三", "李四" ] }
	'搜索字符串中的空格会被当作and来查询
	function parseSearch( ByVal q,ByVal fields )
		dim strAnd,reg,str,arr_q,sqls,temp,options,ret,j		
		strAnd = " + "
		
		'第1步，将查询字符串中的and与+，都替换为+
		set reg = POP_MVC.reg
		reg.Global = true : reg.IgnoreCase = true : reg.MultiLine = false
		reg.Pattern = "\s+and\s+"
		str = reg.replace( q, strAnd  )

		reg.Pattern = "\s+\+\s+"
		str = reg.replace( str, strAnd  )
	
		' 第2步，将查询字符串用+符号分解成数组，如果没有+，则生成一个元素的数组
		arr_q = split( str , strAnd)
		
		' 第3步，循环数组，是标签则不再分解，不是标签还要用空格炸开
		arr_q = POP_MVC.Arr.Unique(arr_q)
		
		temp = Array()
		reg.Pattern = "\s+"
		
		for each item in arr_q
			item = reg.replace(item," ")			
			temp = POP_MVC.Arr.Merge( temp,split( item," " ) )
		next
		
		arr_q = temp
		arr_q = POP_MVC.Arr.Unique(arr_q)
		
		if not isArray( fields ) then
			fields = split( fields, "," )
		end if	
		
		for each item in arr_q
			item = trim(item)
			temp = array()
			for j = 0 to ubound( fields )	
				POP_MVC.Arr.push temp,  " " & fields(j) & " LIKE '%" & item & "%' "
			next
			POP_MVC.Arr.Push sqls, " ( " & join( temp," OR " ) & " ) "
			POP_MVC.Arr.Push options,item
		next
		set ret = D_		

		if ubound(options) > 0 then
			ret("operator") = "AND"
			ret("sql") = "( " & join( sqls," AND " ) & " )"
			ret("option") = options
		else
			ret("operator") = ""
			ret("sql") = POP_MVC.Arr.shift(sqls)
			ret("option") = POP_MVC.Arr.shift(options)			
		end if
		

		
		set parseSearch = ret
	end Function
	
	'连贯操作，将数据以表格的形式进行展示，表格的最后一行是分页
	'界面粗糙，只可作为演示或者管理员临时修改数据使用
	'例如: db.htmlTable()
	Public Function htmlTable( )
		dim rs,page
		If isEmpty( Options( "page" )) Then
			Call Me.Page( Array( null, POP_MVC.config( "PAGE_PAGESIZE" ) ) )
		End If
		set rs = Me.Select
		page = P_( Array("POPASP_PAGE" , POP_MVC.dClass.count ) )(rs).show		
		HtmlTable = POP_MVC.import("POPASP_HTML").Table( rs,"","",page )
		Call tool.closeRS(rs)
	End Function
	
	'连贯操作，将一条记录以表格的形式进行展示，表格的最后一行是分页
	'界面粗糙，只可作为演示或者管理员临时修改数据使用
	'例如: db.positionTable()
	Public Function positionTable( )
		dim rs,page
		If isEmpty( Options( "position" )) Then
			Call Me.Position( null )
		End If
		set rs = Me.Find
		page = P_( Array("POPASP_POSITIONPAGE" , POP_MVC.dClass.count ) )(rs).show		
		positionTable = POP_MVC.import("POPASP_HTML").TableInfo( rs,"","",page )
		Call tool.closeRS(rs)
	End Function
	
	'连贯操作，将数据以表格的形式进行展示，表格的最后一行是分页，表格的最右行是操作（修改|删除）
	'界面粗糙，只可作为演示或者管理员临时修改数据使用
	'例如: db.field().where().htmlList( save_asp ) 
	Public Function htmlList( save_asp )
		dim dict,rs,page
		If isEmpty( Options( "page" )) Then
			Call Me.Page( Array( null, POP_MVC.config( "PAGE_PAGESIZE" ) ) )
		End If
		set rs = Me.Select
		htmlList = POP_MVC.import("POPASP_AUTO").htmlList( rs , tableName , getPrikey(tableName) , save_asp )
		Call tool.closeRS(rs)		
	End Function
	
	'将查询数据下载为Excel表格
	'style为数组或用英文逗号分隔的字符串，代表各字段类型
	'filename为文件名，如果给""，则取当前表名
	'1） 文本(text,t)：vnd.ms-excel.numberformat:@
	'2） 日期(date,d)：vnd.ms-excel.numberformat:yyyy/mm/dd hh:mm:ss
	'3） 数字(number,n)：vnd.ms-excel.numberformat:#,##0.00
	'4） 货币(currency,c,￥,$)：vnd.ms-excel.numberformat:￥#,##0.00
	'5） 百分比(percent,p)：vnd.ms-excel.numberformat: #0.00%	
	'style为数组，分别对应各字段的类型，默认空字符串""为文本类型
	Public Sub Excel( style , filename )
		dim rs,bool
		set rs = Me.select()
		if isNul( filename ) then
			if Me.tableName <> "" then
				filename = Me.tableName
			else
				filename = now()
			end if
		end if
		
		if db_type = "access" then
			bool = false
		else
			bool = true
		end if

		Call P_("POPASP_RECORDSET").Excel( rs, style , filename  , bool )
		Call tool.closeRS(rs)
	End Sub
	
	'开启事务处理
	Property Get [Begin]
		tool.Execute( Array("BEGIN TRANSACTION" , 0 , 0 ) )
	End Property
	
	'执行事务处理
	Property Get Commit
		tool.Execute( Array("COMMIT TRANSACTION" , 0 , 0 ) )
	End Property
	
	'回滚事务处理
	Property Get Rollback
		tool.Execute( Array("ROLLBACK TRANSACTION" , 0 , 0 ) )
	End Property
	
	' 向数据表中添加多条数据，如果数据含主键ID，则剔除主键ID。
	' data应该是一个二维Dictionary对象
	' 会自动剔除表中不存在的字段所对应的数据
	' 返回值：返回没有添加进去的键名(格式为数组)
	Public Property Get mAdd()
		On Error Resume Next
		call parseOptions	'解析参数	

		if is_empty(parsedOptions("table")) then 
			Call setPoptsTable( parsedOptions )	
		end if
		
		dim key , i , dict , sql , startTime , arr , pk , keys , pos , maxID , bAutoInc , rs 
		
		arr = Array()		
		i = 0		
		pk = getPrikey( POP_MVC.String.rtrim(parsedOptions("table"),"$"))
		startTime = timer()
		
		'对于Excel要获取最大的ID值
		if db_type = "excel" and not isEmpty( pk ) then
			sql =  "SELECT max(" & pk & ") as popasp_result FROM [" & parsedOptions("table") & "]" & " WHERE NOT ISNULL([" & pk & "])"
			set rs = getRS(sql)
			if not rs.eof then
				maxID = rs( "popasp_result" )
			else
				maxID = 0
			end if
			tool.closeRS rs
			bAutoInc = true
		end if
		
		'对于txt要获取最大的ID值
		if db_type = "txt" and not isEmpty( pk ) then
			sql =  "SELECT max(" & pk & ") as popasp_result FROM [" & parsedOptions("table") & "]"
			set rs = getRS(sql)
			if not rs.eof then
				maxID = rs( "popasp_result" )
			else
				maxID = 0
			end if
			tool.closeRS rs
			bAutoInc = true
		end if
		
		Call Me.Begin
		
		for each key in parsedOptions("data")
			set dict = parsedOptions("data")(key)
			if typeName( dict ) = "Dictionary" then
				call handlerData( parsedOptions("table"),dict, 0 ) '剔除不存在的字段
				
				'剔除主键
				if db_type <> "excel" and not isEmpty( pk ) then
					keys = dict.keys
					pos  = POP_MVC.Arr.iSearch( keys,pk )
					if pos > -1 then
						dict.remove( keys(pos) )
					end if
				end if
				
				if bAutoInc then
					keys = dict.keys
					pos  = POP_MVC.Arr.iSearch( keys,pk )
					if pos < 0 then
						dict(pk) = maxID + i + 1
					end if
				end if
							
				if dict.count > 0 then					
					sql = tool.getInsertSql( parsedOptions("table"), dict , getTableStructure( parsedOptions("table") ) )
					if tool.Execute( array(sql , 0 , 0) ) then
						i = i + 1
					else
						POP_MVC.Arr.Push arr,key
					end if
				else
					POP_MVC.Arr.Push arr,key
				end if
			else
				POP_MVC.Arr.Push arr,key
			end if
		next
		
		Call Me.Commit
		
		call POP_MVC.pushTime( startTime , "批量添加 " & i & "条数据" )
		
		lastSql = sql
		mAdd = arr
		Call L_( "" )
	End Property
	
	' 向空数据表（非空，可以清空）中添加多条数据，如果数据有主键ID，则保持ID值相同
	' data应该是一个二维Dictionary对象
	' isTruncateTable 如果为0则不清空表，如果为1则清空表
	' 会自动剔除表中不存在的字段所对应的数据
	' 返回值：无返回值
	Public Property Get multiAdd( isTruncateTable )
		On Error Resume Next
		call parseOptions	'解析参数	

		if is_empty(parsedOptions("table")) then 
			Call setPoptsTable( parsedOptions )	
		end if
		
		dim key , i , j , dict , sql , startTime , arr , pk , keys , pos , num , data , bool , prevID,currentID,beforeID,theID
		dim idArr,idArrs,k,m
		arr = Array()		
		i = 0		
		pk = getPrikey( POP_MVC.String.rtrim(parsedOptions("table"),"$"))
		startTime = timer()
		
		set data = parsedOptions
		
		if isTruncateTable <> 0 then
			'清空表
			Call Me.truncate( parsedOptions("table") )
		end if
		
		if db_type = "mysql" then
			Me.exec("SET SQL_MODE = ""NO_AUTO_VALUE_ON_ZERO""")
		elseif db_type = "sqlite3" then
			'sqlite3不需要
		elseif db_type = "sqlserver" then
			Me.exec( "set identity_insert " & getTrueTableName( parsedOptions("table") ) & " on" )
		elseif db_type = "access" then
			'access不能执行 set identity_insert table on/off 语句
		else
			'txt、excel作数据库时只清空就可以了
		end if
		
		

		num = 1

		set parsedOptions = data
		
		Call Me.Begin
		
		'由于mdb不能处理alter table tblName alter column field counter(25,1)这种情况，只能进行添加删除来达到ID一致		
		if db_type = "access" and POP_MVC.String.iEndsWith( db_path , ".mdb" ) AND not isEmpty( pk ) then
			bool = true
			prevID = Me.table( data("table") ).max( pk )

			if isNul(prevID) then
				prevID = Clng(0)
			end if

			
			beforeID = prevID

			for each key in data("data")
				idArr  = Array()
				idArrs = Array()
				j      = -1
				k      = 0
				
				set dict = data("data")(key)
				if typeName( dict ) = "Dictionary" then
					call handlerData( data("table"),dict, 0 ) '剔除不存在的字段
					keys = dict.keys
					pos  = POP_MVC.Arr.iSearch( keys,pk )	
					if pos = -1 then
						bool = false
						Call Me.truncate( data("table") )
						exit for
					end if					
					currentID = CLng(dict( keys(pos) ))
					
					if beforeID > currentID then
						POP_MVC.exit( "要求在使用db.data(data).multiAdd时，其中的data必须按照主键增序排列" )
					end if
					
					dict.remove( keys(pos) )
					lastSql = tool.getInsertSql( data("table"), dict , getTableStructure( data("table") ) )		
					 
					do while prevID < currentID	
						if j > 500 then
							POP_MVC.Arr.push idArrs , Array(idArr(0) , idArr( ubound(idArr) ))
							idArr = Array()
							j = -1
						end if
						Call tool.Execute( array(lastSql , 0 , 0) )
						prevID = prevID + 1						
						if prevID < currentID then
							POP_MVC.Arr.push idArr,prevID	
							j = j + 1
						end if
					loop
					
					
					for m = 0 to ubound( idarrs ) 
						Me.table( data("table") ).where( pk & " BETWEEN " & idArrs(m)(0) & " AND " & idArrs(m)(1) ).remove
					next

					if  j > -1 then
						Me.table( data("table") ).where( join(idArr,",") ).remove
						idArr = Array()
						j = - 1
					end if

					if prevID <> currentID then
						POP_MVC.exit( lastSql & ";--" & db_type & " 添加失败")
					end if
				end if
			next
			if  ubound( idArr ) > -1 then
				Me.table( data("table") ).where( join(idArr,",") ).remove
				idArr = Array()
			end if
		end if
		


		if not bool then
			for each key in parsedOptions("data")
				set dict = parsedOptions("data")(key)
				if typeName( dict ) = "Dictionary" then
					call handlerData( parsedOptions("table"),dict, 0 ) '剔除不存在的字段
					
					'剔除主键
					if db_type = "access" AND not isEmpty( pk ) then
						keys = dict.keys					
						pos  = POP_MVC.Arr.iSearch( keys,pk )	
						if pos > -1 then
							if CStr(dict( keys(pos) )) <> CStr(num) then							
								num =  dict( keys(pos) )
								Call Me.setCounter( parsedOptions("table") , dict( keys(pos)) , 1  )
							end if
							dict.remove( keys(pos) )
						end if
					end if
								
					if dict.count > 0 then					
						sql = tool.getInsertSql( parsedOptions("table"), dict , getTableStructure( parsedOptions("table") ) )
						if tool.Execute( array(sql , 0 , 0) ) then
							i = i + 1
						else
							POP_MVC.Arr.Push arr,key
						end if
					else
						POP_MVC.Arr.Push arr,key
					end if
				else
					POP_MVC.Arr.Push arr,key
				end if
				num = num + 1
			next
		end if
		
		Call Me.Commit	
		
		call POP_MVC.pushTime( startTime , "批量添加 " & i & "条数据" )
		
		lastSql = sql
		Call L_( "" )
	End Property
	
	' 根据查询条件获取的N条数据，然后向数据表中复制这么多条数据
	' data中的值，是要固定的值
	' 返回值：复制的个数
	Public Property Get copy()	
		dim data2,arr,dict,key,key2,maxID
		
		if not isEmpty( options("data") ) then
			set dict = options("data")
		end if
		
		set data2 = Me.getAll		
		
		Call getPK()
		if db_type = "excel" and not isEmpty( prikey ) then
			maxID = getMaxID( tableName )
		end if
			
		if not isEmpty( dict ) or not isEmpty( maxID ) then
			for each key in data2
				if not isEmpty( maxID ) then
					if data2(key).exists( prikey ) then
						maxID = maxID + 1
						data2(key)(prikey) = maxID
					end if
				end if			
			
				if not isEmpty( dict ) then
					for each key2 in dict
						data2(key)(key2) = dict(key2)
					next
				end if
			next
		end if

		arr = Me.data(data2).mAdd
		
		copy = data2.count - ubound(arr) + 1
		
		set data2 = nothing
	End Property
	
	' 在数据表中批量修改N条数据，每条数据必须得含有主键，否则不予修改
	' data应该是一个二维Dictionary对象
	' 会自动剔除表中不存在的字段所对应的数据
	' 返回值：返回没有成功修改的每条数据的ID值(数组)，注意跟mAdd返回值不同
	Public Property Get mSave()		
		On Error Resume Next
		call parseOptions	'解析参数		
		
		if is_empty(parsedOptions("table")) then
			Call setPoptsTable( parsedOptions )	
		end if
		
		dim key , i , dict , sql , pk , startTime , temp , arr , keys , pos
		
		arr = Array()		
		i = 0		
		pk = getPrikey( POP_MVC.String.rtrim(parsedOptions("table"),"$"))
		startTime = timer()
		
		Call Me.Begin
		
		for each key in parsedOptions("data")
			set dict = parsedOptions("data")(key)
			if typeName( dict ) = "Dictionary" then			
				
				call handlerData( parsedOptions("table"),dict , 0 ) '剔除不存在的字段
				keys = dict.keys
				pos  = POP_MVC.Arr.iSearch( keys,pk )
				if pos > -1 then
					temp = dict( keys(pos) )
					dict.remove( keys(pos) )

					if dict.count > 0 then						
						sql = tool.getUpdateSql( parsedOptions("table"), dict , pk & " = " & temp ,  getTableStructure( parsedOptions("table") ) )
						if tool.Execute( array(sql , 0 , 0) ) then
							i = i + 1 
						else
							POP_MVC.Arr.Push arr,temp
						end if
					else
						POP_MVC.Arr.Push arr,temp
					end if
				end if
			end if
		next		

		Call Me.Commit
		
		call POP_MVC.pushTime( startTime , "批量修改 " & i & " 条数据" )
		lastSql = sql
		mSave = arr
	End Property
	
	'以数组返回所有表名，含表名前缀
	Property Get getAllTables
		dim tables
		set tables = getTables
		getAllTables = tables.items
	End Property
	
	'以数组返回所有字段名
	Property Get getAllFields
		dim fields
		set fields = getFields("*")
		getAllFields = fields.keys
	End Property
	
	'判断某个表是否存在于数据库
	Property Get tableExists( table_name )
		tableExists = POP_MVC.Arr.iExists( getAllTables , getTrueTableName(table_name) )
	End Property
	
	'判断是否为该种数据库
	Property Get isAccess
		isAccess = POP_MVC.String.iEqual( Me.db_type , "access" )
	End Property	