|
RSFast - Jscript Version
by Sterling Bates
Sterling has kindly re-written the library Rsfast
in server Jscript. Here is a Jscript program calling the Library:
filename=/learn/rsfast_current/test_js_rsfast.asp
<%@ LANGUAGE="JSCRIPT"%>
<%
var rp = Response;
rp.Buffer = true;
Server.ScriptTimeout = 40;
%>
<!--#include file="lib_rsfast_js.asp"-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>rsfast-templates</title>
</head>
<body bgcolor="#FFFFFF">
<%
var conntest = new String( "DSN=student" );
var rsparms = Server.CreateObject( "Scripting.Dictionary" );
rsparms.Add( "conn", conntest );
rsparms.Add( "sql", "select * from publishers where state='NY'" );
rsparms.Add( "accdb","students.mdb" );
rsparms.Add( "template_header", "<table border=1>" );
rsparms.Add( "template_row_header", "<tr>" );
rsparms.Add( "template_row_footer", "</tr>" );
rsparms.Add( "template_col_header", "<td>" );
rsparms.Add( "template_col_footer", "</td>" );
rsparms.Add( "template_footer", "</table>" );
rsparms.Add( "fieldnull", " " );
rsparms.Add( "fieldblank", " " );
rsparms.Add( "fld_city", "<td bgcolor='lightblue'><b>{fld}</b><br></td>" );
rsparms.Add( "fld_state", "<td><b>{fld}</b><br></td>" );
rsparms.Add( "colnames", "display" );
rsfast = new CCacheFast( rsparms );
rsfast.run();
rsfast.perf.show();
rsfast.perf.clear();
rsfast = null;
rsparms = null;
%>
Sterling has kindly re-written the library Rsfast
in server Jscript:
filename=/learn/rsfast_current/lib_rsfast_js.asp
<!--include file="lib_rsfast_perf.asp"-->
<%
// fix 1: removed some extra code that wasn't executing in this version
// fix 2: fixed .add "colnames", "display" so it works correctly
String.prototype.isNull = fnIsNull;
function fnIsNull() {
return (this.valueOf() == "" || this.valueOf() == "null" || this.valueOf() == "undefined");
}
// container for performance-related data
function CPerfData( owner ) {
this.parent = null; // Assigned to the CacheFast object.
this.timeStart = 0;
this.timeEnd = 0;
this.timeElapsed = 0;
this.ms = 0;
this.sec = 0;
this.min = 0;
this.day = 0;
this.params = owner.params;
this.debugOut = Cache_DebugOut;
this.item = Cache_GetItem;
this.timer = Perf_Timer;
this.show = Perf_Show;
this.clear = Perf_ShowClear;
this.showPretty = Perf_ShowPretty;
this.showAll = Perf_ShowAll;
this.keyGrab = Perf_KeyGrab;
this.keySet = Perf_KeySet;
this.keyIncrease = Perf_KeyIncrease;
this.keyAddUpdate = Perf_KeyAddUpdate;
this.makeNumber = Perf_MakeNumber;
}
// container for persistence across function calls (overcomes byref obstacle)
function CCacheFast( theparams ) {
// properties and fields
this.fields = new String();
this.data = new String();
this.asArray = new Array(); // The array representation; populated by this.toArray();
this.params = theparams;
this.perf = new CPerfData( this );
this.name = new String( "" );
this.empty = true;
this.expired = false;
// methods
this.timer = Perf_Timer; // Just here for convenience sake.
this.item = Cache_GetItem;
this.setItem = Cache_SetItem;
this.run = Cache_rsFast;
this.toArray = Cache_toArray;
this.build = Cache_Build;
this.grab = Cache_Grab;
this.fetch = Cache_Fetch;
this.show = Cache_Display;
this.isEmpty = Cache_IsEmpty;
this.isExpired = Cache_IsExpired;
this.isCached = Cache_IsCached;
this.microOptimize = Cache_MicroOptimize;
this.debugOut = Cache_DebugOut;
}
// And the driving object...
CacheFast = new CCacheFast();
// Quick method to retrieve items from the dictionary
function Cache_GetItem( theitem ) {
return this.params.item( theitem );
}
function Cache_SetItem( theitem,thevalue ) {
this.params.item( theitem ) = String(thevalue).valueOf();
}
// Handles debugging output -- performs debug on/off check here.
function Cache_DebugOut( thetext ) {
if (this.item( "debug" )) {
rp.Write( thetext +"<br>" );
rp.Flush();
}
}
// The routine to start it all off.
function Cache_rsFast() {
this.perf.timeStart = this.timer();
// Access database build OLEDB connection string START
sDB = String(this.item( "accdb" )).toLowerCase();
// If they supplied a path, and it is not fully qualified, map the path
if ((! sDB.isNull()) && (sDB.indexOf( ":" ) == -1))
sDB = new String( Server.MapPath( sDB.valueOf() ) );
this.setItem( "conn","PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" +sDB +";" );
// Caching and Data-Fetching START
this.name = new String( this.item( "cachename" ) );
with (this.perf) {
ms = new String( this.item( "cachems" ) );
sec = new String( this.item( "cachesec" ) );
min = new String( this.item( "cachemin" ) );
day = new String( this.item( "cacheday" ) );
}
this.setItem( "cachegrab","no" );
this.setItem( "cachebuild","no" );
if (! this.isCached())
this.fetch()
else if (Application( this.name +"_building" ))
this.fetch()
else {
if (this.isExpired() || this.isEmpty())
this.build();
this.grab();
this.data = this.data.split( "#r#\n" )
}
this.debugOut( "Calling show..." );
this.show();
with (this.perf) {
timeEnd = this.timer();
timeElapsed = timeEnd-timeStart;
}
this.setItem( "timetotalms",(this.perf.timeElapsed *1000) );
// Now calculate further stats
this.microOptimize();
this.perf.keyAddUpdate();
// reset cache -- should NEVER remember between calls
this.setItem( "cachename","" );
this.setItem( "cachemin","" );
}
function Cache_MicroOptimize() {
// Thanks to Mike "micro-optimization" Shaffer
this.debugOut( "Cache_MicroOptimize called" );
this.setItem( "timeopensec",(this.item( "timeopenms" ) / 1000) );
this.setItem( "timequerysec",(this.item( "timequeryms" ) / 1000) );
this.setItem( "timefetchsec",(this.item( "timefetchms" ) / 1000) );
this.setItem( "timedisplaysec",(this.item( "timedisplayms" ) / 1000) );
this.setItem( "timetotalsec",(this.item( "timetotalms" ) / 1000) );
this.setItem( "timeopenmin",(this.item( "timeopenms" ) / 60000) );
this.setItem( "timequerymin",(this.item( "timequeryms" ) / 60000) );
this.setItem( "timefetchmin",(this.item( "timefetchms" ) / 60000) );
this.setItem( "timedisplaymin",(this.item( "timedisplayms" ) / 60000) );
this.setItem( "timetotalmin",(this.item( "timetotalms" ) / 60000) );
}
function Cache_toArray() {
this.debugOut( "Cache_toArray() called" );
// Now transfer cache to Array
this.asArray = String(Application( this.name.valueOf() +"_cachedata" )).split( "#r#\n" );
}
function Cache_Build() {
this.debugOut( "Cache_Build called" );
Application( this.name.valueOf() +"_building" ) = true;
this.setItem( "cachefetch",true );
this.fetch();
this.setItem( "cachefetch",false );
Application( this.name +"_cachedata" ) = this.data.valueOf();
Application( this.name +"_cachefields" ) = this.fields.valueOf();
// Now Expire The Cache
// cachename_cacheExpires
theTime = new Date().getTime();
Application( this.name +"_cachecreated" ) = Number( theTime );
Application( this.name +"_cachemin" ) = Number( this.item( "cachemin" ) );
this.debugOut( "Cachecreated=" +Application( this.name +"_cachecreated" ) +"<br>" );
this.debugOut( "cachemin=" +Application( this.name +"_cachemin" ) );
this.setItem( "cachebuild","yes" );
Application( this.name +"_building" ) = false;
}
function Cache_Grab() {
this.debugOut( "Cache_Grab() called" );
this.data = Application( this.name +"_cachedata" );
this.fields = Application( this.name +"_cachefields" );
this.setItem( "cachegrab","yes" );
}
function Cache_IsEmpty() {
this.debugOut( "CacheEmpty called" );
this.empty = (String(Application( this.name +"_cachedata" )).isNull());
this.debugOut( "CacheEmpty = " +this.empty );
return this.empty;
}
function Cache_IsExpired() {
// If cache is out of date return TRUE
this.debugOut( "Cache_IsExpired called" );
cachemin = new Number( Application( this.name +"_cachemin" ) );
if (isNaN( cachemin ))
cachemin = 0;
cachecreated = new Number( Application( this.name +"_cachecreated" ) );
if (isNaN( cachecreated ))
cachecreated = 0;
whenexpires = cachemin +cachecreated;
rightnow = new Date().getTime();
this.debugOut( "cachemin: " +cachemin +"<br>cachecreated: " +cachecreated +"<br>rightnow: " +rightnow );
this.expired = (rightnow >= whenexpires);
this.debugOut( "expired=" +this.expired );
return this.expired;
}
function Cache_IsCached() {
// Returns True/False whether data is cache-affected
return (! this.name.isNull());
}
function Cache_Fetch() {
this.debugOut( "Cache_Fetch called" );
// Open and check for EOF
openStart = this.timer();
var connTemp = Server.CreateObject( "ADODB.Connection" );
connTemp.open( this.item( "conn" ) );
openEnd = this.timer();
openElapsed = openEnd - openStart;
this.setItem( "timeopenms",openElapsed );
this.debugOut( "Conn=" +this.item( "conn" ) );
this.debugOut( "Database opened in " +openElapsed +"ms" );
// If recordset is paged must be opened special
queryStart = this.timer();
if (Number(this.item( "pagesize" )) > 0) {
adUseClient = 3;
var rsTemp = Server.CreateObject( "ADODB.Recordset" );
rsTemp.cursorLocation = adUseClient;
rsTemp.cacheSize = this.item( "pagesize" );
rsTemp.open( this.item( "sql" ),this.item( "conn" ) );
rsTemp.absolutePage = this.item( "page" );
pageMax = Number(rsTemp.pagecount);
this.setItem( "pagemax",pagemax );
paged = true;
this.debugOut( "SQL = " +this.item( "sql" ) );
this.debugOut( "Recordset opened for paging!" );
} else
var rsTemp = connTemp.execute( this.item( "sql" ) );
queryEnd = this.timer();
queryElapsed = queryEnd - queryStart;
this.setItem( "timequeryms",openElapsed );
this.debugOut( "SQL = " +this.item( "sql" ) );
this.debugOut( "Query executed: " +queryElapsed +"ms" );
fetchStart = this.timer();
if (rsTemp.EOF) {
this.setItem( "errordesc","No records matched" );
this.setItem( "errornum",1 );
this.debugOut( "EOF encountered! " +queryElapsed +"ms" );
} else {
// Now Fill The Array
// Rockville#c#MD#c#20849#r#<vbcrlf>
// Dallas#c#TX#c#XXXXX#r#<vbcrlf>
sData = new String( rsTemp.getstring( 2,99999,"#c#","#r#\n","#n#" ) );
this.fields = new String( "" );
// Now Fill The FieldMaps
// City#c#State#c#Zip#r#<vbcrlf>
for (z=0; z < rsTemp.Fields.Count; z++)
this.fields += rsTemp( z ).name +"#c#";
if (this.item( "cachefetch"))
this.data = sData;
else
this.data = sData.split( "#r#\n" );
this.debugOut( "Data before split:<br>" +this.data +"<hr>" );
this.debugOut( "Fields before split:<br>" +this.fields +"<hr>" );
fetchElapsed = this.timer() -fetchStart;
this.setItem( "timefetchms",fetchElapsed );
}
this.debugOut( "Cleaning up db objects" );
rsTemp.close();
rsTemp = null;
connTemp.close();
connTemp = null;
this.debugOut( "Finished fetch" );
}
function Cache_Display() {
displayStart = this.timer();
this.debugOut( "Cache_Display called" );
sTemplate = new String( this.item( "template" ) ).toLowerCase();
sTemplateName = new String( this.item( "templatename" ) );
// Templates Are Applied As Needed START
// Probably need to replace dictionary items ONLY if they don't exist
bTemplate = false;
switch( sTemplate.valueOf() ) {
case "list":
case "listm":
sTemplateHeader = new String( "<select name='" +sTemplateName );
if (sTemplate.valueOf() == "listm")
sTemplateHeader += " multiple";
sTemplateHeader += "'>";
sRowHeader = "<option>";
sRowFooter = "</option>";
sColHeader = "";
sColFooter = "";
sTemplateFooter = "</select><br>";
sFieldNull = " ";
sFieldBlank = " ";
bTemplate = true;
break;
case "table":
case "tablepaged":
sTemplateHeader = new String( "<table border=1>" );
sRowHeader = "<tr>";
sRowFooter = "</tr>";
sColHeader = "<td>";
sColFooter = "</td>";
sTemplateFooter = "</table>";
sFieldNull = " ";
sFieldBlank = " ";
this.setItem( "colnames","display" );
bTemplate = true;
break;
default:
}
if (! bTemplate) {
sTemplateHeader = new String( this.item( "template_header" ) );
sTemplateFooter = new String( this.item( "template_footer" ) );
// Load dictionary items into simple variable to avoid
// doing so many times in loop
sRowHeader = this.item( "template_row_header" );
sRowFooter = this.item( "template_row_footer" );
sColHeader = this.item( "template_col_header" );
sColFooter = this.item( "template_col_footer" );
sFieldNull = this.item( "fieldnull" );
sFieldBlank = this.item( "fieldblank" );
}
reg = new RegExp( "{page}","g" );
regMax = new RegExp( "{pageMax}","g" );
// Page x of x displays may need to appear in header/footer
sTemplateHeader = sTemplateHeader.replace( reg,this.item( "page" ) );
sTemplateFooter = sTemplateFooter.replace( reg,this.item( "page" ) );
sTemplateHeader = sTemplateHeader.replace( regMax,this.item( "pagemax" ) );
sTemplateFooter = sTemplateFooter.replace( regMax,this.item( "pagemax" ) );
if (this.item( "debug" )) { // Additional processing here...
this.debugOut( "Data array=" );
this.debugOut( "Data length=" +this.data.length +"<p>" );
for (z=0; z < this.data.length-1; z++)
this.debugOut( "<b>Data( " +z +" )</b> = " +this.data[z] );
this.debugOut( "<hr>Fields: " +this.fields );
}
aFields = this.fields.split( "#c#" );
if (this.item( "debug" )) { // Additional processing here...
this.debugOut( "DataFields Data" );
this.debugOut( "DataFields length =" +aFields.length +"<p>" );
for (z=0; z < aFields.length-1; z++)
this.debugOut( "<b>DataFields( " +z +" )</b>=" +aFields[z] );
this.debugOut( "<hr><b>formatting info</b>" );
this.debugOut( "sTemplateHeader = " +Server.HTMLEncode( sTemplateHeader ) );
this.debugOut( "sTemplateFooter = " +Server.HTMLEncode( sTemplateFooter ) );
this.debugOut( "rowheader = " +Server.HTMLEncode( sRowHeader ) );
this.debugOut( "rowfooter = " +Server.HTMLEncode( sRowFooter ) );
this.debugOut( "colheader = " +Server.HTMLEncode( sColHeader ) );
this.debugOut( "colfooter = " +Server.HTMLEncode( sColFooter ) );
this.debugOut( "<hr>" );
for (item in this.params)
if (String(item).indexOf( "fld_" ) > -1)
this.debugOut( item +" = " +Server.HTMLEncode( this.item( item ) ) );
this.debugOut( "<hr>" );
}
// Page x of x displays may need to appear in header/footer
sTemplateHeader = sTemplateHeader.replace( reg,this.item( "page" ) );
sTemplateFooter = sTemplateFooter.replace( reg,this.item( "page" ) );
sTemplateHeader = sTemplateHeader.replace( regMax,this.item( "pagemax" ) );
sTemplateFooter = sTemplateFooter.replace( regMax,this.item( "pagemax" ) );
rp.Write( sTemplateHeader );
// Show the row header?
if (String(this.item( "colnames" )).valueOf() == "display") {
rp.Write( sRowHeader );
for (c=0; c < aFields.length-1; c++)
rp.Write( sColHeader +"<b>" +aFields[c] +"</b>" +sColFooter );
rp.Write( sRowFooter );
}
iCellCount = 0;
reg = new RegExp( "{fld}","g" );
// suck display out of cache
for (r=0; r < this.data.length-1; r++) {
rp.Write( "\n" +sRowHeader );
sRow = new String( this.data[r] ).split( "#c#" );
for (c=0; c < aFields.length-1; c++) {
sField = new String( aFields[c] ).toLowerCase();
sValue = new String( sRow[c] );
if (sValue.valueOf() == "#n#")
sValue = new String( sFieldNull );
if (sValue.isNull())
sValue = new String( sFieldBlank );
sFieldTemplate = new String( this.item( "fld_" +sField.valueOf() ) );
if (! sFieldTemplate.isNull())
rp.Write( sFieldTemplate.replace( reg,sValue ) )
else
rp.Write( "\n" +sColHeader +sValue +sColFooter +"\n" );
iCellCount++;
}
rp.Write( sRowFooter +"\n" );
}
rp.Write( sTemplateFooter );
displayElapsed = this.timer() -displayStart;
this.setItem( "timedisplayms",displayElapsed );
this.setItem( "cellcount",iCellCount );
}
function Perf_Show() {
lineBreak = "<br>\n";
rp.Write( "cachegrab=" +this.item( "cachegrab" ) +lineBreak );
rp.Write( "cachebuild=" +this.item( "cachebuild" ) +lineBreak );
rp.Write( "total ms=" +this.item( "timetotalms" ) +lineBreak );
rp.Write( "open ms=" +this.item( "timeopenms" ) +lineBreak );
rp.Write( "query ms=" +this.item( "timequeryms" ) +lineBreak );
rp.Write( "fetch ms=" +this.item( "timefetchms" ) +lineBreak );
rp.Write( "display ms=" +this.item( "timedisplayms" ) +lineBreak );
rp.Write( "cellcount=" +this.item( "cellcount" ) +lineBreak );
rp.Write( "<hr>" );
rp.Write( "total sec=" +this.item( "timetotalsec" ) +lineBreak );
rp.Write( "open sec=" +this.item( "timeopensec" ) +lineBreak );
rp.Write( "query sec=" +this.item( "timequerysec" ) +lineBreak );
rp.Write( "fetch sec=" +this.item( "timefetchsec" ) +lineBreak );
rp.Write( "display sec=" +this.item( "timedisplaysec" ) +lineBreak );
rp.Write( "<hr>" );
rp.Write( "total min=" +this.item( "timetotalmin" ) +lineBreak );
rp.Write( "open min=" +this.item( "timeopenmin" ) +lineBreak );
rp.Write( "query min=" +this.item( "timequerymin" ) +lineBreak );
rp.Write( "fetch min=" +this.item( "timefetchmin" ) +lineBreak );
rp.Write( "display min=" +this.item( "timedisplaymin" ) +lineBreak );
rp.Write( "<hr>" );
}
// Here is the the library file that does tracks all performance data:
function Perf_KeyAddUpdate() {
this.debugOut( "Perf_KeyAddUpdate called" );
// Check for FirstQuery ever
sKeyQuery = new String( this.item( "sql" ) +"\n" +this.item( "conn" ) );
if (String(Application( "rsfast_query_1" )).isNull()) {
Application( "rsfast_query_1" ) = sKeyQuery.valueOf();
Application( "rsfast_query_max" ) = 1;
iKeyCounterMax = 1;
}
// See if QueryPair already exists
iKeyCounterMax = Application( "rsfast_query_max" );
iKeyCurrent = -1;
for (k=0; k < iKeyCounterMax; k++)
if (String(Application( "rsfast_query_" +k )).valueOf() == sKeyQuery.valueOf())
iKeyCurrent = k;
// QueryPair doesn't exist
if (iKeyCurrent == -1) {
Application.lock();
Application( "rsfast_query_max" ) = Number(Application("rsfast_query_max")) +1;
iKeyCurrent = this.makeNumber( Application( "rsfast_query_max" ) );
Application( "rsfast_query_" +iKeyCurrent ) = sKeyQuery.valueOf();
Application.unlock();
}
this.debugOut( "keyquery=" +sKeyQuery +"<br>keycurrent=" +iKeyCurrent +"<br>keycountermax=" +iKeyCounterMax );
this.keyIncrease( iKeyCurrent,"_howmany",1 );
this.keyIncrease( iKeyCurrent,"_totalms",this.item( "timetotalms" ) );
this.keyIncrease( iKeyCurrent,"_openms",this.item( "timeopenms" ) );
this.keyIncrease( iKeyCurrent,"_queryms",this.item( "timequeryms" ) );
this.keyIncrease( iKeyCurrent,"_fetchms",this.item( "timefetchms" ) );
this.keyIncrease( iKeyCurrent,"_displayms",this.item( "timedisplayms" ) );
this.keyIncrease( iKeyCurrent,"_cellcount",this.item( "cellcount" ) );
if (String(this.item( "cachegrab" )).valueOf() == "yes")
this.keyIncrease( iKeyCurrent,"_cacheYES",1 )
else
this.keyIncrease( iKeyCurrent,"_cacheNO",1 );
}
function Perf_MakeNumber( num ) {
if (isNaN( num ))
num = 0;
return num;
}
function Perf_KeyIncrease( key,suffix,value ) {
// Increase This Key
sKeyLocal = "rsfast_query_" +key +suffix;
sKeyCount = "rsfast_query_" +key +"_howmany"
sKeyAvg = sKeyLocal +"_avg";
this.debugOut( "keylocal=" +sKeyLocal +"<br>keycount=" +sKeyCount +"<br>keyavg=" +sKeyAvg );
Application.lock();
Application( sKeyLocal.valueOf() ) = Number(this.makeNumber(Application( sKeyLocal.valueOf() )) +this.makeNumber(value));
Application.unlock();
// Average This Key
Application( sKeyAvg.valueOf() ) = Number(this.makeNumber(Application( sKeyLocal.valueOf() )) / this.makeNumber(Application( sKeyCount )));
}
function Perf_KeySet( key,suffix,value ) {
Application( "rsfast_" +key +suffix ) = value;
}
function Perf_KeyGrab( key,suffix ) {
sKey = "rsfast_query_" +key +suffix;
return Application( sKey.valueOf() );
}
function Perf_ShowAll() {
for (item in Application.contents)
if (String(item).indexOf( "rsfast" ) != -1)
rp.Write( "<b>" +item +"=</b>" +Application.contents( item ) +"<br>" );
}
function Perf_ShowPretty() {
sPerfPad = " ";
sPerfSep = "<br><hr><br>";
sPerfLB = "<br>\n";
iKeyCounterMax = Number(Application( "rsfast_query_max" ));
for (k=0; k < iKeyCounterMax; k++) {
rp.Write( "Query #" +k +"<br>" );
rp.Write( sPerfPad +"Query: <b>" );
rp.Write( this.keyGrab( k,"") +"</b>" +sPerfLB );
rp.Write( sPerfPad +"Query Count: <b>" );
rp.Write( this.keyGrab( k,"_howmany") +"</b>" +sPerfLB );
rp.Write( sPerfPad +"Query Total ms ave.: <b>" );
rp.Write( this.keyGrab( k,"_totalms_avg") +"</b>" +sPerfLB );
rp.Write( sPerfPad +"Cached YES: <b>" );
rp.Write( this.keyGrab( k,"_cacheYES") +"</b>" +sPerfLB );
rp.Write( sPerfPad +"Cached NO: <b>" );
rp.Write( this.keyGrab( k,"_cacheNO") +"</b>" +sPerfLB );
}
}
function Perf_ShowClear() {
for (item in Application.contents)
if (String(item).indexOf( "rsfast" ) != -1) {
rp.Write( "<b>Killing" +item +"<br>" );
Application.contents.remove( item );
} else
rp.Write( "<b>" +item +"</b><br>" );
}
function Perf_Timer() {
return (new Date().getTime()) / 1000;
}
%>
|