//默认库源代码
//修改源代码以后,在快手中点击"编译(F8)",存为"快手目录/lib/sqlite/_.aau" 即可

import raw; 

 //这里我是直接包含该dll，编译后可以脱离dll运行
//也可以重新编译该sqlite3.dll，将aau文件放到"LIB->IMPORT"的资源中，用DLL直接作为库文件
var dll = raw.loadDll($"/.res/sqlite3.dll","sqlite.dll"); 
var push = ..table.push;

class sqlite{
	ctor( filename,pwd,flag,vfsName ){
		this.db = open(filename,pwd,flag,vfsName) 
		var this_sqlite = this;
		
		..table.add_gc( this,"close");
	};
	rekey =  function(pwd){ 
		return rekey(this.db,pwd);
	};
	close = function(){
		close( this.db);
		this.db = null;
	};
	exec = function(sql,...){
		sql = ..string.fromto(sql,0,65001)
		var failed,err = exec(this.db,sql,...);
		if(failed) checkResult(failed,err,,this.db); 
	}; 
	changes = function(){
		return changes(this.db);
	};
	wal_checkpoint = function(){
		return wal_checkpoint(this.db);
	}; 
	enum = function(sql,proc){
		sql = ..string.fromto(sql,0,65001);
		
		//第2个参数代表数据结果集中有几列数据
		//第3个参数代表列数据的字符串数组，第4个参数代表列名的字符串数组
		var callback = function(notused,argc,value,name){ 
			tname = ..raw.toarray( argc,"string" )
			tvalue = ..raw.toarray( argc,"string" )
			..raw.convert( name, tname);
			..raw.convert( value, tvalue); 
			 
			return proc(tname.array,tvalue.array,argc);
		} 
		
		//创建回调函数 cdcel调用约定
		callback_c = ..raw.tocdecl( callback,"int(pointer,int,pointer argv,pointer azColName)") 
		var failed,err = exec(this.db,sql,callback_c, /*第4个参数是个随意指针，用途可用户指定，它将传给回调函数的第1个参数*/  );
		checkResult(failed,err,,this.db);;
		return failed;
	};
	prepare = class {
		ctor( sql,cond,... ){
			var failed,pstmt;
			var bind = ..sqlite.bind; 
			this.bind = {
				//i表示?号的位置
				parameter_index = function(name,prefix){
					if(!prefix){
						return bind.parameter_index(pstmt,name);
					}
					else {
						return bind.parameter_index(pstmt,prefix++name);
					};
				};  
				blob = function( s,i=1){
					if(! bind.blob(pstmt,i,s,#s) )
						return this;
				};
				double = function( n,i=1){
					if(! bind.double(pstmt,i,n) )
						return this;
				}; 
				int = function( n,i=1){
					if(! bind.int(pstmt,i,n) )
						return this;
				}; 
				long64 = function( n,i=1){
					if(! bind.long64(pstmt,i,n) )
						return this;
				};  
				text = function( str,i=1){
					str = ..string.fromto(str,0,65001)
					if(! bind.text(pstmt,i,str,#str) )
						return this;
				};
				text16 = function( str,i=1){
					if(! bind.text16(pstmt,i,str,#str) )
						return this;
				}; 
				//aauto type begin
				["null"] = function( v,i){
					if(! bind.null(pstmt,i : v : 1) ) 
						return this;
				};
				string = function(s,i=1){
					if( ..string.find(s,'\0') )
						return owner.blob(s,i);
					else 
						return owner.text(s,i); 
				}; 
				number = function(n,i=1){
					if( ..math.floor(n) === v )
						return owner.int(n,i);
					else {
						return owner.double(n,i);
					} 
				};
				boolean = function(v,i=1){
					return owner.int(tonumber(!!v),i)
				};
				cdata = function(v,i=1){
					return owner.blob(v,i)
				}; 
				table = function(v,i=1){
					if( v@ && v@._tostring){
						if( ..time.istime(v) ){
							v.format = "%Y-%m-%d %H:%M:%S"
						}
						return owner.string( tostring(v),i )
					} 
					return owner.blob('/*AAuto.Table=*/' + ..table.tostring(v),i)
				}; 
				//aauto type end;
				parameter = function(v,i){
					var t = type(v)
					var b = owner[t];
					if(b){ 
						var ok,err = call(b,owner,v,i);
						if(ok)
							return err;
						else 
							error(err,2)
					} 
					else {  
						error("Sqlite不支持此数据类型:" + t + " 参数:#" + i,2) 
					}  	
				}; 
				parameterByNames = function(args,prefix){
					var count = 0;
					var ind;
					for(k,v in args){ 
						ind = owner.parameter_index(k,prefix);
						if( ind ? owner.parameter(v,ind ) )
							count++; 
					}  
					return this,count;
				};
				parameterByNamesAt = function(args){
					return owner.parameterByNames(args,"@");
				};
				parameterByName = function(v,k){
					return owner.parameter(v,(owner.parameter_index(k)) ); 
				};
				parameters = function(...){
					var p = {...}
					var v,succeeded;
					for(i=1;#p;1){ 
						if(! owner.parameter(p[i],i)  ) 
							return;
					}  
					return this;
				};
				clear = function(){
					return bind.clear(pstmt);
				};  
			}; 
			
			this.prepare = function( sql,cond,... ) { 
				var sqlCondHeader,paramCond;
				if(cond){
					sqlCondHeader = ..string.find(sql,"\s<WHERE>|<where>\s") ?" and ":" where ";
					 
					if( type(cond) == "table" ){ 
						var t = {} 
						for(k,v in cond){ 
							push( t,  k + "=@" + k  );
						} 
						paramCond = cond;  
						cond =  ..string.join(t," and ");   
					};
					
					if(!#cond)
						sqlCondHeader = null;
				}; 
				sql = ..string.concat( sql,sqlCondHeader,cond,... );
				this.sql = sql;
			 	
				sql = ..string.fromto(sql,0,65001)
				failed,pstmt = ..sqlite.prepare(this_sqlite.db, sql, #sql,pstmt );  
				 
				if( failed ) { 
					var lv = this_sqlite.prepare_throwLevel : 2;
					if( lv != 2 )
						this_sqlite.prepare_throwLevel = null;
						
					error( ( this_sqlite.lasterr() ) + '\nSQL:'+ sql ,lv ); 
				}; 
				this_sqlite.prepare_throwLevel = null;
				
				if(paramCond){
					this.bind.parameterByNamesAt(paramCond);
				}
				return this;
			}; 
			
			this_sqlite.prepare_throwLevel = 3;
			this.prepare(sql,cond,...);
		 
			var column = ..sqlite.column; 
			var column_count =  column.count;
			var column_name = column.name
            var column_value =  column.queryValue;
            var column_type = column.type;
		}; 
		column = {
			count = function(){
				return column_count(pstmt);
			}; 
			name = function(iCol){
				return column_name(pstmt,iCol);
			};
			type = function(iCol){
				return column_type(pstmt,iCol);
			}; 
			queryValue = function(iCol){
				return column_value(pstmt,iCol);
			}; 
		}; 
		step = function(){
			return  ..sqlite.step(pstmt);
		}; 
		stepQuery = function(){
			var row = {} 
			var ret = this.step();
			if( ret == 100/*..sqlite.ROW*/ ){
				for(i=0;column_count(pstmt)-1 ){ 
                	row[ column_name(pstmt,i) ] = column_value(pstmt,i);
                }
                return row;
			}
			return null,ret;
		};
		finalize = function(){
			..sqlite.finalize(pstmt);
			pstmt = null;
		};
		reset = function(){
			..sqlite.reset(pstmt)
		}; 
		each = function(){ 
			var ub = column_count(pstmt);  
			var unpack = ..table.unpack;
			var row = {};
			
			return function(){ 
				var ret = this.step();  
				if( ret !=  100/*..sqlite.ROW*/ )  
					return; 
					 
                for(col=1;ub){ 
                    row[col] = column_value(pstmt,col-1)
                }; 
				return unpack(row,1,ub);
			},function(){
				this.reset(); 
			};	
		}; 
		getColumns = function(){
			var ub = column_count(pstmt) ; 
			var names = {};
			for(i=1;ub){ 
				names[i] = column_name(pstmt,i-1) ;
			}
			return names;
		}; 
        getTable = function(){
            var tab = {};
            var names = this.getColumns();
            var ub = #names;  
            while( this.step() == 100/*..sqlite.ROW*/ ){   
                var row = {};
                for(i=1;ub){ 
                    row[ names[i] ] = column_value(pstmt,i-1);
                }
                push(tab,row);    
            } 
            return tab; 
        };	
	};  
	each = function(sql){
		stmt = this.prepare( sql );
		return stmt.each(),function(){ stmt.finalize(); };
	};
	stepQuery = function(sql,cond,... ){
		this.prepare_throwLevel = 3;
		var stmt = this.prepare( sql,cond,... );
		var row,ret = stmt.stepQuery();
		stmt.finalize();
		return row,ret;
	}; 
	getTable = function(sql,cond,... ){
		this.prepare_throwLevel = 3;
		var stmt = this.prepare( sql,cond,... );
		var tab = stmt.getTable();
		stmt.finalize();
		return tab;
	};
	existsTable = function(tName){
		var row = this.stepQuery("SELECT COUNT(*) FROM sqlite_master where type='table' and name='"+tName+"';")
		return !!tonumber( row["COUNT(*)"] );
	};
	busy_timeout = function(ms=0){
		return busy_timeout( this.db,ms) != 0
	};
	busy_handler = function(func,str){
		if( type(func) ==  type.function) {
			sqlite3_busy_handler( this.db,null);//safe remove this.busy_handle_c
			this.busy_handle_c =  ..raw.tocdecl(func,"bool(string ptr,int count)",owner)
			this.busy_handle_str = str;//void gc;
			return sqlite3_busy_handler( this.db,this.busy_handle_c,this.busy_handle_str) != 0
		};
		else {
			return sqlite3_busy_handler( this.db,null) != 0
		};
	}; 
	last_insert_rowid = function(){
    	return last_insert_rowid(this.db); 
    };
    lasterr = function(){
    	return lasterr(this.db);
    };
};

/**intellisense() 
!sqliteConn.each = @for 字段名,字段名2 in ??.each("select * from [表名] ORDER BY 排序字段 DESC LIMIT 长度 OFFSET 开始位置") {
	io.print( 字段名,字段名2 )
}
!sqliteConn.each_sqlite_master = @for Type,name,tbl_name,rootpage,sql in ??.each("select * from [sqlite_master]") {
	io.print( Type,name,tbl_name,rootpage,sql )
}
!sqliteConn.enum(sql) = @.enum( \n	__/*sql*/,\n	function(tname,tvalue){\n		for(i=1;#tname;1){ \n			io.print(tname[i],tvalue[i])\n		}\n		 \n	}\n)
!sqliteConn.exec("__") = 执行sql语句,\n出错则抛出异常.
!sqliteConn.exec_insert = @.exec("insert into 表名__(字段名) values( 值 )")\n//插入数据到表中
!sqliteConn.exec_insert_where_not_exists = @.exec("insert into 表名(字段名) select '插入值' where not exists(select * from 表名 where 条件字段名=查询值);")\n//如果符合条件的数据不存在则插入新的数据
!sqliteConn.exec_create_table = @.exec("CREATE TABLE 表名__( \n	ID INTEGER PRIMARY KEY AUTOINCREMENT,  \n	数值字段名 INTEGER,\n	浮点字段名 REAL,\n	文本字段名 TEXT,\n	二进制字段名 BLOB, \n	非空字段名 NOT NULL DEFAULT '默认值',\n	动态类型字段名,\n	UNIQUE (ID) \n	);"\n)
!sqliteConn.exec_update = @.exec("UPDATE 表名 SET 更新字段 = '更新值' WHERE 条件字段 = 条件值;');
!sqliteConn.exec_create_index = @.exec("create index 索引名字 on 表名字(索引字段名字)")\n//建立索引可加快该字段查询速度.
!sqliteConn.exec_journal_mode_wal = @.exec("PRAGMA journal_mode=WAL;")
!sqliteConn.exec_journal_mode_delete = @.exec("journal_mode=DELETE;")
!sqliteConn.exec_delete_table = @.exec("DELETE FROM 表名__ ")\n//清空表
!sqliteConn.exec_drop_table = @.exec("drop table 表名__ ")\n//删除表
!sqliteConn.exec_delete_sequence = @.exec("DELETE FROM sqlite_sequence WHERE name = '表名__'")\n//自增ID置0
!sqliteConn.exec_free_memory = @.exec("VACUUM")//DELETE表后必须调用此语句才能释放空间
!sqliteConn.exec_replace = @.exec("REPLACE INTO [表名__] (字段名, 字段名2) VALUES (值, 值2)")\n//如果该表有一个主键,那么当主键值相等的时候,该行数据不存在执行插入,存在则执行更新操作
!sqliteConn.exec_begin = @.exec("BEGIN;")\n//开启事务
!sqliteConn.exec_rollback = @.exec("ROLLBACK;")\n//回滚事务,还原BEGIN之后的所有操作.
!sqliteConn.exec_begin = @.exec("COMMIT;")\n//提交事务 
!sqliteConn.db = 当前打开的数据库连接对象
!sqliteConn.changes() = 返回数据库最近一次运行exec()所改变的行数
!sqliteConn.getTable("SELECT * FROM [表名] __/*sql语句*/") = 返回包含行记录组成的table数组对象,\n每行是由列名、值组成的table表对象.\n可选参数2:指定查询条件(table对象或字符串),\n可选增加任意个附加sql参数.
!sqliteConn.stepQuery("SELECT * FROM [表名] __/*sql语句*/") = 查询并返回首行数据,失败返回null,状态码.\n可选参数2:指定查询条件(table对象或字符串),\n可选增加任意个附加sql参数.
!sqliteConn.close() = 关闭数据库连接\n在线程结束时,此函数也会自动调用
!sqliteConn.existsTable("__/*表名*/") = 判断指定的表是否存在
!sqliteConn.rekey("__/*密钥*/") = 添加、清空、重设数据库密钥\n该函数需要使用支持加密的DLL组件重新编译sqlite库
!sqliteConn.wal_checkpoint() = 执行checkpoint操作时，WAL日志文件内容被写回数据库文件
!sqliteConn.busy_handler = @.busy_handler( \n	function(strBack,count) {\n		sleep(1);\n		return count < 1000; /*重试次数*/\n	},strBack\n)
!sqliteConn.busy_timeout(10000) = 数据锁定冲突时的重试时间,以毫秒为单位,成功返回true\nbusy_handler()函数控制重试次数,busy_timeout()函数控制重试时间\n这两个函数可相互影响,设置一个必然然取消另一个
!sqliteConn.last_insert_rowid() = 获取最后一次插入操作添加记录的ID\n作用类似MSSQL的@@IDENTITY 
!sqliteConn.lasterr() = 返回最后一次发生错误的错误信息,以及错误代码
?sqlite = !sqliteConn.
sqlite() = !sqliteConn.
sqlite(":memory:") = 创建内存数据库
sqlite("\数据库路径") =  参数一指定数据库路径,支持自动创建数据库文件.\n第二个参数(可选参数)指定数据库密钥,sqlite.aes128或sqlite.aes256支持该参数.\n第三个参数(可选参数)使用一个或多个_SQLITE_OPEN_前缀常量指定连接选项\n默认启用多线程模式,多线程共用单个数据连接不安全，否则就是安全的\n多线程模式可并发读,但不可同时写入

!sqliteConn.prepare("SELECT * FROM [表名__] ") = 编译SQL预处理命令,\n可选参数2:指定查询条件(table对象或字符串),\n可选增加任意个附加sql参数.\n---------------------\n查询SQL示例:\n"SELECT * FROM [表名] WHERE 条件字段=12  ORDER BY 排序字段 DESC LIMIT 1 OFFSET 0"\n上面SQL语句中LIMIT n OFFSET i 指定第i条记录开始取n条记录\n也可以直接写为 limit i,n
!sqliteConn.prepare_insert = @.prepare("insert into [表名__] values (?1,?2,?3,?4);")\n//创建插入数据SQL命令对象,问号表示参数,可在问号后指定索引
!sqliteConn.prepare_select_distinct = @.prepare("SELECT DISTINCT 去重字段 FROM [表名__]") 
?.prepare =!sqlite_cmd.
!sqliteConn.prepare() = !sqlite_cmd.
!sqlite_cmd.sql = sql指令
!sqlite_cmd.bind.parameter_index("@__/*参数名*/") = 返回命名参数的索引值,参数名可以'@',':','$'等符号作为首字符,\n成功返回索引,如果在sql语句中未找到该名字则返回0
!sqlite_cmd.bind.blob(__/*二进制字符串*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.cdata(__) = 将CDATA类型转换为blob类型存储
!sqlite_cmd.bind.number(__) = 如果是整数则调用bind.int()\n是浮点数则调用bind.double;
!sqlite_cmd.bind.string(__) = 如果包含'\0'则存为blob类型,\n否则转换为UTF8存为text类型
!sqlite_cmd.bind.boolean(__) = 将bool值转换为数值存储\nfalse存为0,true存为1;
!sqlite_cmd.bind.double(__/*浮点数值*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.int(__/*数值*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.long64(__/*64位数值*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.null() = 绑定一个空值参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身\n!sqlite_cmd.
!sqlite_cmd.bind.text(__/*UTF8编码文本*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.text16(__/*UTF16编码文本*/) = 绑定一个参数,参数在sql语句中用问号表示\n可使用第二个参数指定问号的位置\n返回命令对象自身
!sqlite_cmd.bind.table(__/*table对象*/) = 如果定义了tostring元方法,则调用并转换为文本存储,\n如果是一个时间对象,则使用标准格式转换为文本存储\n否则序列化为blob类型存储
!sqlite_cmd.bind.parameters( = 绑定多个动态类型SQL命令参数
!sqlite_cmd.bind.parameters(.(任意多个命令参数) = 绑定一个或多个动态类型SQL命令参数,自动绑定合适的数据类型.\n参数位置对应sql语句中的问号位置\n返回命令对象自身
!sqlite_cmd.bind.parameter( = 绑定一个动态类型SQL命令参数
!sqlite_cmd.bind.parameterByName(.(参数值,"@参数名") = 绑定一个命令动态类型SQL命令参数,自动绑定合适的数据类型.\n参数2指定命名参数,参数名可以'@',':','$'等符号作为首字符
!sqlite_cmd.bind.parameter(.(参数值,参数索引) = 绑定一个命令动态类型SQL命令参数,自动绑定合适的数据类型.\n参数2指定索引位置,默认为1\n返回命令对象自身
!sqlite_cmd.bind.parameterByNames(.(参数表) = 绑定一个表中所有键值对到相应的命名参数,\n可使用第二个参数指定'@',':','$'等符号作为参数名前缀,\n返回命令对象自身,以及成功绑定的参数数目.
!sqlite_cmd.bind.parameterByNamesAt(.(参数表) = 绑定一个表中所有键值对到相应的命名参数,\n对所有键名添加"@"字符作为SQL参数名,\n返回命令对象自身,以及成功绑定的参数数目.
!sqlite_cmd.bind.parameterByName()  = !sqlite_cmd.
!sqlite_cmd.bind.parameterByNames()  = !sqlite_cmd.
!sqlite_cmd.bind.parameterAtNames()  = !sqlite_cmd.
!sqlite_cmd.bind.parameter_index() = !sqlite_cmd.
!sqlite_cmd.bind.blob() = !sqlite_cmd.
!sqlite_cmd.bind.double() = !sqlite_cmd.
!sqlite_cmd.bind.int() = !sqlite_cmd.
!sqlite_cmd.bind.long64() = !sqlite_cmd.
!sqlite_cmd.bind.text() = !sqlite_cmd.
!sqlite_cmd.bind.text16() = !sqlite_cmd.
!sqlite_cmd.bind.parameters() = !sqlite_cmd.
!sqlite_cmd.clear() = 清除所有绑定值\n成功返回0

!sqlite_cmd.column.count() = 返回列数目
!sqlite_cmd.column.name(iCol) = 返回指定列名字
!sqlite_cmd.column.type(iCol) = 返回指定列字段类型
!sqlite_cmd.column.queryValue(iCol) = 返回指定列的数据值\n此函数自动识别并转换类型 

!sqlite_cmd.prepare("SELECT * FROM [表名] __/*sql语句*/") = 重新编译SQL预处理命令,\n可选参数2:指定查询条件(table对象或字符串),\n可选增加任意个附加sql参数.
!sqlite_cmd.prepare() = !sqlite_cmd.
!sqlite_cmd.finalize() = 每一个预处理语句都必须调用这个接口进行销毁以避免内存泄漏
!sqlite_cmd.reset() = 重置该预处理语句并继续解析执行
!sqlite_cmd.step() = 向下移动一行\n成功返回值大于等于100
!sqlite_cmd.stepQuery() = 向后移动一行,并返回当前数据,失败返回null,状态码.\n返回table对象,键为列名,值为当前行数据
!sqlite_cmd.getTable() = 返回全部数据,\n返回值为table数组,每行记录为一个数组元素.
!sqlite_cmd.getColumns() = 返回当前查询的列名字数组.
!sqlite_cmd.each = @for 字段名,字段名2 in ??.each() {
	io.print( 字段名,字段名2 )
}
end intellisense**/

namespace sqlite;

//打开关闭数据库
//sqlite3_open = dll.api("sqlite3_open","int(STRING filename,pointer &ppDB)","cdecl");
sqlite3_open_v2 = dll.api("sqlite3_open_v2","int(STRING filename,pointer &ppDB,int flags,pointer zVfs)","cdecl");
//sqlite3_config = dll.api("sqlite3_config","int(int)","cdecl");
//sqlite3_config(2/*SQLITE_CONFIG_MULTITHREAD */)

//检测编译模式
//=1(默认) 串行模式支持单连接多线程 ＝2多线程模式支持多连接多线程(不支持单连接多线程) =0单线程模式不支持线程安全并不可改变
//sqlite3_threadsafe = dll.api("sqlite3_threadsafe","int()","cdecl")
 
open = function(filename,pwd,flag=0x10006/*SQLITE_OPEN_NOMUTEX|OPEN_CREATE|SQLITE_OPEN_READWRITE*/,vfsName ){
	filename = ..io.localpath(filename) : filename
	filename = ..string.fromto(filename,0,65001) //sqllite内部使用UTF8编码,如果路径包含中文名,要转换为UTF8
	var failed,db =  sqlite3_open_v2( filename,null,flag, vfsName )  ;//如果库不存在，会自动创建文件 
	checkResult(failed,,4);
 
	if( pwd ){
		key(db,pwd);
	} 
	return db;
}

wal_checkpoint = dll.api("sqlite3_wal_checkpoint","int(POINTER pDB,pointer zDb)","cdecl");
sqlite3_busy_handler = dll.api("sqlite3_busy_handler","int(POINTER pDB,pointer handle,pointer)","cdecl"); 

err_message ={
	[0] = "成功执行";
	[1] = "SQL错误或数据库不存在(SQLITE_ERROR)";
	[2] = "SQLite内部逻辑错误（没有使用）";
	[3] = "访问许可禁止";
	[4] = "回调函数请求中止";
	[5] = "数据库文件被锁定(SQLITE_BUSY)";
	[6] = "数据库中的一表被锁定";
	[7] = "malloc()分配堆失败";
	[8] = "企图写只读数据库";
	[9] = "操作被sqlie3_interrupt()中止";
	[10] = "磁盘I=O错误";
	[11] = "数据库文件变形";
	[12] = "表或纪录没有找到（没有使用）";
	[13] = "插入失败因为数据库满";
	[14] = "不能打开数据库文件";
	[15] = "数据库锁协议错误";
	[16] = "数据库是空的";
	[17] = "数据库模式改变";
	[18] = "单行数据过多（没有使用）";
	[19] = "因约束违背而中止";
	[20] = "数据类型不匹配";
	[21] = "库使用方法不当";
	[22] = "主机不支持库中操作系统功能";
	[23] = "认证禁止";
	[24] = "附属数据库格式错误";
	[25] = "sqlite3_bind()第2个参数超出范围";
	[26] = "此数据库已加密或不是一个数据库文件";
	[100] = "sqlite3_step()有另一行数据就绪";
	[101] = "sqlite3_step()完成执行，已无数据";
}
self.formatResult = function(failed,err,db){
	if( (!failed) || ( failed>=100) ) return;
	var msg = lasterr(db,failed);
	if(type(err)==type.pointer){
		var str = ..string.concat(msg,'\r\n', ..raw.tostring(err) );
		free(err);
		msg = ..string.concat( msg," ", ..string.fromto(str) );
	} 
	return msg;
};

self.assertf = function(failed,...){ 
	if(!failed)
		return ... 
	error( formatResult(failed) ,2);
};

checkResult = function(failed,err,level=3,db){ 
	if( failed && ( failed<100) ) 
		error( formatResult(failed,err,db) ,level);
};  
 
try{
	sqlite3_key = dll.api("sqlite3_key","int(POINTER db,string key,int nkey)","cdecl") 
	sqlite3_rekey = dll.api("sqlite3_rekey","int(POINTER db,string key,int nkey)","cdecl")  
	key = function(db,pwd){
		var err = sqlite3_key(db,pwd,#pwd) 
		if(err)
			error(formatResult(failed,err,db),3)
	}
	rekey = function(db,pwd){ 
		var err = sqlite3_rekey(db,pwd,#pwd)
		if(err)
			error(formatResult(failed,err,db),3)
	}
} 
catch(e){
	key = function(){
		error('请导入支持加密的 sqlite.aes256 或 sqlite.aes128 扩展库',2)
	}
	rekey = key; 
};

time = function(t,f="%Y-%m-%d %H:%M:%S"){
	var tm = ..time(t,f);
	tm.format = f;
	return tm;
};

//获取最后调用API的错误信息
errcode = dll.api("sqlite3_errcode","int(POINTER db)","cdecl") 
//The application does not need to worry about freeing the result
errmsg = dll.api("sqlite3_errmsg","string(pointer db)","cdecl");
free = dll.api("sqlite3_free","void(pointer errmsg)","cdecl"); 
lasterr = function(db,code){
	var code = code : errcode(db); 
	var msg = err_message[code]:"未知错误";
    return db ? ..string.concat( ..string.fromto(errmsg(db)) ,'\n',msg) : msg;
};

/**intellisense(sqlite)
lasterr(.(db,errcode) = 参数1:指定数据库连接句柄,\n参数2:可选使用此参数指定错误代码,\n返回最后一次发生错误的错误信息,以及错误代码.
assertf(__/*调用sqlite API函数*/) = 第一个参数是API返回的状态值 \n如果发生错误则抛出异常终断程序,并显示错误信息
open("/__/*数据库路径*/") =  参数一指定数据库路径,支持自动创建数据库文件.\n第二个参数(可选参数)指定数据库密钥,需要加密DLL支持.\n第三个参数(可选参数)使用一个或多个_SQLITE_OPEN_前缀常量指定连接选项\n默认启用多线程模式,多线程共用单个数据连接不安全，否则就是安全的\n多线程模式可并发读,但不可同时写入
open(":memory:") = 打开内存数据库
time(__/*表示时间的字符串或数值*/) = 参数可以是时间数值、时间字符串、或其他datetime对象\n返回time对象,默认使用格式化串"%Y-%m-%d %H:%M:%S" 
key(.(数据库句柄,密钥) = 验证数据库密钥\n加密的数据库必须首先调用此函数输入正确密钥
rekey(.(数据库句柄,密钥) = 添加密钥\n变更或清空密钥( 必须在此之前执行sqlite3.key()函数输入旧密码 )
wal_checkpoint(__/*数据库句柄*/) = 执行checkpoint操作时，WAL日志文件内容被写回数据库文件
checkResult(.(failed,err,level) = 参数一: sqlite API的第一个返回值\n参数二(可选参数):sqlite返回的错误信息指针\n参数三(可选参数): 抛出异常的调用级别,2为调用checkResult的函数,3为调用当前函数的函数
formatResult.(failed,err) = 参数一: sqlite API的第一个返回值\n参数二(可选参数):sqlite返回的错误信息指针\n对于原始Sqlite API返回的err指针,必须调用此函数转换为字符串并释放该指针
end intellisense**/

/**intellisense()
sqlite.aes128 = 支持AES128位数据加密版本sqlite支持库
sqlite.aes256 = 支持AES256位数据加密版本sqlite支持库
sqlite.aes256() = !sqliteConn.
sqlite.aes128() = !sqliteConn.
sqlite.aes128("\数据库路径") =  参数一指定数据库路径,支持自动创建数据库文件.\n第二个参数(可选参数)指定数据库密钥,sqlite.aes128或sqlite.aes256支持该参数.
sqlite.aes256("\数据库路径") =  参数一指定数据库路径,支持自动创建数据库文件.\n第二个参数(可选参数)指定数据库密钥,sqlite.aes128或sqlite.aes256支持该参数.
end intellisense**/

/*intellisense(sqlite)*/ 
OK=0 //成功执行
ERROR=1 //SQL错误或数据库不存在
INTERNAL=2 //SQLite内部逻辑错误（没有使用）
PERM=3 //访问许可禁止
ABORT=4 //回调函数请求中止
BUSY=5 //数据库文件被锁定
LOCKED=6 //数据库中的一表被锁定
NOMEM=7 //malloc()分配堆失败
READONLY=8 //企图写只读数据库
INTERRUPT=9 //操作被sqlie3_interrupt()中止
IOERR=10 //磁盘I=O错误
CORRUPT=11 //数据库文件变形
NOTFOUND=12 //表或纪录没有找到（没有使用）
FULL=13 //插入失败因为数据库满
CANTOPEN=14 //不能打开数据库文件
PROTOCOL=15 //数据库锁协议错误
EMPTY=16 //数据库是空的
SCHEMA=17 //数据库模式改变
TOOBIG=18 //单行数据过多（没有使用）
CONSTRAINT=19 //因约束违背而中止
MISMATCH=20 //数据类型不匹配
MISUSE=21 //库使用方法不当
NOLFS=22 //主机不支持库中操作系统功能
AUTH=23 //认证禁止
FORMAT=24 //附属数据库格式错误
RANGE=25 //sqlite3_bind()第2个参数超出范围
NOTADB=26 //打开非数据库文件
ROW=100 //sqlite3_step()有另一行数据就绪
DONE=101 //sqlite3_step()完成执行，已无数据

close = dll.api("sqlite3_close","int(pointer db)","cdecl");
 
//使用回调函数查询数据库
exec = dll.api("sqlite3_exec","int(POINTER db,STRING sql,pointer callback,pointer callback_arg,pointer &)","cdecl");
changes = dll.api("sqlite3_changes","int(pointer db)","cdecl");
last_insert_rowid = dll.api("sqlite3_last_insert_rowid","int(pointer db)","cdecl");
busy_timeout = dll.api("sqlite3_busy_timeout","int(POINTER pDB,int ms)","cdecl"); 

//不使用回调函数查询数据库 方法之一
prepare= dll.api("sqlite3_prepare","int(POINTER db,string szSql,int nByte,pointer &stmt,pointer& pzTail)","cdecl");
prepare2= dll.api("sqlite3_prepare_v2","int(POINTER db,string szSql,int nByte,pointer &stmt,pointer& pzTail)","cdecl");
step= dll.api("sqlite3_step","int(pointer stmt)","cdecl");
finalize= dll.api("sqlite3_finalize","int(pointer stmt)","cdecl");
reset= dll.api("sqlite3_reset","int(pointer stmt)","cdecl");

/*end intellisense*/ 
namespace column{
	/*intellisense(sqlite.column)*/ 
	_blob = dll.api("sqlite3_column_blob","pointer(POINTER stmt, int iCol)","cdecl");
	bytes= dll.api("sqlite3_column_bytes","int(POINTER stmt, int iCol)","cdecl");
	bytes16 = dll.api("sqlite3_column_bytes16","int(POINTER stmt, int iCol)","cdecl");
	double = dll.api("sqlite3_column_double","double(POINTER stmt, int iCol)","cdecl");
	int = dll.api("sqlite3_column_int","int(POINTER stmt, int iCol)","cdecl");
	long64 = dll.api("sqlite3_column_int64","long(POINTER stmt, int iCol)","cdecl"); 
	text16 = dll.api("sqlite3_column_text16","pointer(POINTER stmt, int iCol)","cdecl"); 
	value = dll.api("sqlite3_column_value","int(POINTER stmt, int iCol)","cdecl");
	count = dll.api("sqlite3_column_count","int(POINTER stmt)","cdecl"); 
	name = dll.api("sqlite3_column_name","string(POINTER stmt, int iCol)","cdecl"); 
	/*end intellisense*/
	
	sqlite3_column_type = dll.api("sqlite3_column_type","int(POINTER stmt, int iCol)","cdecl");
	typeName = { [1]="int";[2]="double";[3]="text";[4]="blob";[5]="null" } 
	self.type = function(stmt,iCol){ 
		var t = sqlite3_column_type(stmt,iCol) 
		return t,typeName[t];
	}
	
	sqlite3_column_text= dll.api("sqlite3_column_text","string(POINTER stmt, int iCol)","cdecl");
	text = function(pstmt,iCol){
		var v = sqlite3_column_text(pstmt,iCol);
        return #v?..string.fromto(v):"";
	} 
	 
	var raw_totring = ..raw.tostring
	blob = function(pstmt,iCol){
		var p = _blob(pstmt,iCol)
		var len = bytes(pstmt,iCol)
		if( p && len ){
			var str = raw_totring(p,1,len);
			if( str[1] == '/'#  ){
				if( ..string.startWith(str,'/*AAuto.Table=*/' ) ){
					return eval(str);
				}
			}
			return str;
		}
	} 
	 
	queryValue = function(pstmt,iCol){
        var t,tname = self.type(pstmt, iCol);
        var get = self[tname];  
        return get? get(pstmt,iCol) ;      
    };
	/**intellisense(sqlite.column)
	queryValue(iCol) = 返回指定列的数据,自动识别数据类型.
	text(stmt,iCol) = 返回文本,自动由UTF8转换为ANSI
	blob(stmt,iCol) = 读取二进制数据,并返回字符串,\n如果存储的是一个序列化后的table对象,则返回table对象.
	type(stmt,iCol) =  返回类型ID,以及类型名
	typeName[__/*类型ID*/] = 根据类型ID,返回类型名
	end intellisense**/
	
}

//不使用回调函数查询数据库 方法之二
namespace table{
	/*intellisense(sqlite.table)*/
	get = dll.api("sqlite3_get_table","int(POINTER db,string zSql,pointer &pazResult,int &pnRow,int &pnColumn,pointer &errmsg)","cdecl");
	free = dll.api("sqlite3_free_table","int(pointer azResult)","cdecl")
	/*end intellisense*/
}

 
namespace bind{
	/*intellisense(sqlite.bind)*/
	blob = dll.api("sqlite3_bind_blob","int(pointer stmt, int, pointer, int n, pointer )","cdecl");
	double = dll.api("sqlite3_bind_double","int(pointer stmt, int, double)","cdecl");
	int = dll.api("sqlite3_bind_int","int(pointer stmt, int, int)","cdecl");
	long64 = dll.api("sqlite3_bind_int64","int(pointer stmt, int, long)","cdecl");
	text = dll.api("sqlite3_bind_text","int(pointer stmt, int, string, int n,pointer )","cdecl");
	text16 = dll.api("sqlite3_bind_text16","int(pointer stmt, int,string, int, pointer )","cdecl");
	value = dll.api("sqlite3_bind_value","int(pointer stmt, int, pointer)","cdecl");
	zeroblob = dll.api("sqlite3_bind_zeroblob","int(pointer stmt, int, int n)","cdecl");
	parameter_index = dll.api("sqlite3_bind_parameter_index","int(pointer stmt, string zName)","cdecl"); 
	clear = dll.api("sqlite3_clear_bindings","int(pointer stmt)","cdecl"); 
	/*end intellisense*/ 
}   
/*intellisense(sqlite.bind)*/
bind.null = dll.api("sqlite3_bind_null","int(pointer stmt, int)","cdecl");
/*end intellisense*/

..sqlite.aes256 = ..sqlite 
..sqlite.aes128 = ..sqlite 
//-------------------------------------------------------------------
//API声明 结束;
//-------------------------------------------------------------------

 /**intellisense()
_SQLITE_OPEN_READONLY=@1/*_SQLITE_OPEN_READONLY*/
_SQLITE_OPEN_READWRITE=@2/*_SQLITE_OPEN_READWRITE*/
_SQLITE_OPEN_CREATE=@4/*_SQLITE_OPEN_CREATE*/
_SQLITE_OPEN_DELETEONCLOSE=@8/*_SQLITE_OPEN_DELETEONCLOSE*/
_SQLITE_OPEN_EXCLUSIVE=@0x10/*_SQLITE_OPEN_EXCLUSIVE*/
_SQLITE_OPEN_AUTOPROXY=@0x20/*_SQLITE_OPEN_AUTOPROXY*/
_SQLITE_OPEN_MAIN_DB=@0x100/*_SQLITE_OPEN_MAIN_DB*/
_SQLITE_OPEN_TEMP_DB=@0x200/*_SQLITE_OPEN_TEMP_DB*/
_SQLITE_OPEN_TRANSIENT_DB=@0x400/*_SQLITE_OPEN_TRANSIENT_DB*/
_SQLITE_OPEN_MAIN_JOURNAL=@0x800/*_SQLITE_OPEN_MAIN_JOURNAL*/
_SQLITE_OPEN_TEMP_JOURNAL=@0x1000/*_SQLITE_OPEN_TEMP_JOURNAL*/
_SQLITE_OPEN_SUBJOURNAL=@0x2000/*_SQLITE_OPEN_SUBJOURNAL*/
_SQLITE_OPEN_MASTER_JOURNAL=@0x4000/*_SQLITE_OPEN_MASTER_JOURNAL*/
_SQLITE_OPEN_NOMUTEX=@0x8000/*_SQLITE_OPEN_NOMUTEX*/
_SQLITE_OPEN_FULLMUTEX=@0x10000/*_SQLITE_OPEN_FULLMUTEX*/
_SQLITE_OPEN_SHAREDCACHE=@0x20000/*_SQLITE_OPEN_SHAREDCACHE*/
_SQLITE_OPEN_PRIVATECACHE=@0x40000/*_SQLITE_OPEN_PRIVATECACHE*/
end intellisense**/