///////////////////////////////////////////////////////////////////////////
//
// XBSDB - Cross-Browser JavaScript Database library
// Copyright (C) 2010 Alexey A.Znayev
//
// This file is part of XBSDB.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Alexey A.Znayev, znaeff@mail.ru
//
///////////////////////////////////////////////////////////////////////////
// This flie contains private class(es) and/or method(s) and may be changed any time
///////////////////////////////////////////////////////////////////////////
// table (name, structure, number of records, hash of columns)
// aStructure - array of arrays [ name, type ]
function XBSTable(aStructure){
// public fields
this.sResultCode = 'OK';
this._aStructure = aStructure;
this._oCols = {}; // hash 'name' => 'column'
this._aFieldNames = []; // solid array of names
this._oFieldTypes = {}; // hash 'name' => 'type'
for(var i = 0; i < this._aStructure.length; i++){ // solid
if(typeof this._aStructure[i][1] != 'string'){
this.sResultCode = 'TABLE_STRUCTURE_BAD';
break;
}
switch(this._aStructure[i][1]){
case 'string' :
case 'number' :
this._oCols[this._aStructure[i][0]] = []; // this way to store records provides the minimum of dereferences (3) to access a row value, see sFieldBefore & sFieldAfter
this._aFieldNames.push(this._aStructure[i][0]);
this._oFieldTypes[this._aStructure[i][0]] = this._aStructure[i][1];
break;
default :
this.sResultCode = 'TABLE_FIELD_TYPE_UN';
}//switch
if(this.sResultCode != 'OK'){
break;
}
}//for
if(this.sResultCode == 'OK'){
this._nRecords = 0;
this._oKeys = {}; // keys hash ['id'] => [XBSKey object]
this._aDeleted = []; // not solid not packed array 'recodr number' => 'true' for deleted records
}
}
//XBSTable.prototype.sFieldBefore = 'table._oCols.';
//XBSTable.prototype.sFieldBefore = 'table._aRecords[n][table._oCols["';
XBSTable.prototype.sFieldBefore = 'table._oCols.';
//XBSTable.prototype.sFieldAfter = '.GetValue(n)';
//XBSTable.prototype.sFieldAfter = '"]]';
XBSTable.prototype.sFieldAfter = '[n]';
XBSTable.prototype.oAllowedKeyOperations = {
// 'a' : ['+'],
// 'n' : ['+','-','*','/'],
// 's' : ['+']
}
XBSTable.prototype.Load = function(aRows){
this.sResultCode = 'OK';
var nFields = this._aFieldNames.length;
this._aDeleted = [];
this._nRecords = aRows.length;
for(var nRec = 0; nRec < this._nRecords; nRec++){
for(var nField = 0; nField < nFields; nField++){
this._oCols[this._aFieldNames[nField]][nRec] = aRows[nRec][nField];
}
}
return true;
}
XBSTable.prototype.Dump = function(){
this.sResultCode = 'OK';
var oDump = {}, nFields = this._aStructure.length, aRecord;
oDump['aStructure'] = [];
for(var nField = 0; nField < nFields; nField++){
oDump['aStructure'][nField] = [];
oDump['aStructure'][nField][0] = this._aStructure[nField][0];
oDump['aStructure'][nField][1] = this._aStructure[nField][1];
}
oDump['aData'] = [];
for(var nRec = 0; nRec < this._nRecords; nRec++){
if(!this._aDeleted[nRec]){
aRecord = [];
for(var nField = 0; nField < nFields; nField++){
aRecord[nField] = this._oCols[this._aStructure[nField][0]][nRec];
}
oDump['aData'].push(aRecord);
}
}
return oDump;
}
XBSTable.prototype.InsertOneByFields = function(aFields,aValues){
this.sResultCode = 'OK';
var nRecNum, i;
if(this._aDeleted.length > 0){
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then store number and exit, not solid, with check
if(this._aDeleted[i]){
nRecNum = i;
this._aDeleted.pop();
break;
}else{
this._aDeleted.pop();
}
}
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then exit, not solid, with check
if(this._aDeleted[i]){
break;
}else{
this._aDeleted.pop();
}
}
}else{
nRecNum = this._nRecords;
}
if(nRecNum == this._nRecords){
this._nRecords++;
}
for(i = 0; i < aFields.length; i++){ // solid
this._oCols[aFields[i]][nRecNum] = aValues[i];
}
for(i in this._oKeys){
if(!this._oKeys[i].Rebuild()){
this.sResultCode = this._oKeys[i].sResultCode;
return false;
}
}
return true;
}
XBSTable.prototype.InsertOneByOrder = function(aValues){
this.sResultCode = 'OK';
var nRecNum, i;
if(this._aDeleted.length > 0){
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then store number and exit, not solid, with check
if(this._aDeleted[i]){
nRecNum = i;
this._aDeleted.pop();
break;
}else{
this._aDeleted.pop();
}
}
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then exit, not solid, with check
if(this._aDeleted[i]){
break;
}else{
this._aDeleted.pop();
}
}
}else{
nRecNum = this._nRecords;
}
if(nRecNum == this._nRecords){
this._nRecords++;
}
for(i = 0; (i < aValues.length) && (i < this._aFieldNames.length) ; i++){ // solid
this._oCols[this._aFieldNames[i]][nRecNum] = aValues[i];
}
for(i in this._oKeys){
if(!this._oKeys[i].Rebuild()){
this.sResultCode = this._oKeys[i].sResultCode;
return false;
}
}
return true;
}
XBSTable.prototype.InsertManyByFields = function(aFields,aRows){
this.sResultCode = 'OK';
var nRecNum, nInserted = 0, aNewRecord, j, i;
if(aRows.length > 0){
for(j = 0; j < aRows.length; j++){ // solid
if(this._aDeleted.length > 0){
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then store number and exit, not solid, with check
if(this._aDeleted[i]){
nRecNum = i;
this._aDeleted.pop();
break;
}else{
this._aDeleted.pop();
}
}
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then exit, not solid, with check
if(this._aDeleted[i]){
break;
}else{
this._aDeleted.pop();
}
}
}else{
nRecNum = this._nRecords;
}
if(nRecNum == this._nRecords){
this._nRecords++;
}
for(i = 0; i < aFields.length; i++){ // solid
this._oCols[aFields[i]][nRecNum] = aRows[j][i];
}
nInserted++;
} //for j
for(i in this._oKeys){
if(!this._oKeys[i].Rebuild()){
this.sResultCode = this._oKeys[i].sResultCode;
return false;
}
}
}
return nInserted;
}
XBSTable.prototype.InsertManyByOrder = function(aRows){
this.sResultCode = 'OK';
var nRecNum, nInserted = 0, aNewRecord, j, i;
if(aRows.length > 0){
for(j = 0; j < aRows.length; j++){ // solid
if(this._aDeleted.length > 0){
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then store number and exit, not solid, with check
if(this._aDeleted[i]){
nRecNum = i;
this._aDeleted.pop();
break;
}else{
this._aDeleted.pop();
}
}
for(i = this._aDeleted.length - 1; i >= 0; i--){ // go back, seek true, delete elements from tail, when find true then exit, not solid, with check
if(this._aDeleted[i]){
break;
}else{
this._aDeleted.pop();
}
}
}else{
nRecNum = this._nRecords;
}
if(nRecNum == this._nRecords){
this._nRecords++;
}
for(i = 0; (i < aRows[j].length) && (i < this._aFieldNames.length); i++){ // subarray solid
this._oCols[this._aFieldNames[i]][nRecNum] = aRows[j][i];
}
nInserted++;
}
for(i in this._oKeys){
if(!this._oKeys[i].Rebuild()){
this.sResultCode = this._oKeys[i].sResultCode;
return false;
}
}
}
return nInserted;
}
XBSTable.prototype.UpdateByFields = function(aFields,aValues,aRecords){
this.sResultCode = 'OK';
if(aRecords.length <= 0){
return 0;
}
var nUpdated = 0, j, i;
for(j = 0; j < aRecords.length; j++){ // solid
if(this._IsRecordNumberOK(aRecords[j])){
for(i = 0; i < aFields.length; i++){ // solid
this._oCols[aFields[i]][aRecords[j]] = aValues[i];
}
nUpdated++;
}
}
if(nUpdated>0){
for(i in this._oKeys){
// check names in indexes!
for(j = 0; j < aFields.length; j++){// solid
if(this._oKeys[i].oFields[aFields[j]]){
this._oKeys[i].Rebuild();
break;
}
}
}
}
return nUpdated;
}
XBSTable.prototype.UpdateByOrder = function(aValues,aRecords){
this.sResultCode = 'OK';
if(aRecords.length <= 0){
return 0;
}
var nUpdated = 0, j, i;
for(j = 0; j < aRecords.length; j++){ // solid
if(this._IsRecordNumberOK(aRecords[j])){
for(i = 0; (i < aValues.length) && (i < this._aFieldNames.length); i++){ // solid
this._oCols[this._aFieldNames[i]][aRecords[j]] = aValues[i];
}
nUpdated++;
}
}
if(nUpdated>0){
for(i in this._oKeys){
// check names in indexes!
for(j = 0; (j < aValues.length) && (j < this._aFieldNames.length); j++){// solid
if(this._oKeys[i].oFields[this._aFieldNames[j]]){
this._oKeys[i].Rebuild();
break;
}
}
}
}
return nUpdated;
}
XBSTable.prototype.Delete = function(aRecords){
this.sResultCode = 'OK';
if(aRecords.length > 0){
var i, j;
for(i = 0; i < aRecords.length; i++){ // solid
for(j = 0; j < this._aFieldNames.length; j++){ // solid
delete this._oCols[this._aFieldNames[j]][aRecords[i]];
}
this._aDeleted[aRecords[i]] = true;
if(aRecords[i] == this._nRecords - 1){
this._nRecords--;
}
}
for(i in this._oKeys){
this._oKeys[i].Rebuild();
}
}
return aRecords.length;
}
XBSTable.prototype.AddKey = function(sExpression,aNumbersInit){
this.sResultCode = 'OK';
var oParser = new XBSEParser();
var vParserResult = oParser.Parse(sExpression,{'aFieldNames':this._aFieldNames, 'sFieldBefore':this.sFieldBefore, 'sFieldAfter':this.sFieldAfter, 'sExprType':'KEY'});
if(vParserResult && (vParserResult.sType == 'object_key_scalar')){
var oKey = new XBSKey(this,vParserResult.sExprFieldsX,vParserResult.oFields);
if(aNumbersInit && Object.prototype.toString.call(aNumbersInit) === '[object Array]'){
oKey.Load(aNumbersInit);
this._oKeys[vParserResult.sExprFields] = oKey;
return vParserResult.sExprFields;
}else{
if(oKey.Rebuild()){
this._oKeys[vParserResult.sExprFields] = oKey;
return vParserResult.sExprFields;
}else{
this.sResultCode = oKey.sResultCode;
return false;
}
}
}else{
this.sResultCode = oParser.sResultCode;
return false;
}
}
XBSTable.prototype.IsCol = function(sColName){
if(this._oCols[sColName]){
return true;
}else{
return false;
}
}
XBSTable.prototype.GetColTypeByName = function(sColName){
return this._oFieldTypes[sColName];
}
XBSTable.prototype.GetColTypeByNumber = function(nColNumber){
return this._oFieldTypes[this._aFieldNames[nColNumber]];
}
XBSTable.prototype.IsKey = function(sKeyID){
this.sResultCode = 'OK';
if(this._oKeys[sKeyID]){
return true;
}else{
return false;
}
}
XBSTable.prototype.GetKeyIDs = function(){
this.sResultCode = 'OK';
var aKeyIDs = [];
for(var i in this._oKeys){
aKeyIDs.push(i);
}
return aKeyIDs;
}
XBSTable.prototype.DropKey = function(sKeyID){
this.sResultCode = 'OK';
if(this._oKeys[sKeyID]){
delete this._oKeys[sKeyID];
}
return true;
}
XBSTable.prototype.GetKeyExpressionParsed = function(sExpr,aOpers){
this.sResultCode = 'OK';
if(!aOpers){ aOpers = ['+','-','*','/']} // think about possible using of this :-)
var oParser = new XBSEParser();
var vParserResult = oParser.Parse(sExpr,{'aFieldNames':this._aFieldNames, 'sFieldBefore':this.sFieldBefore, 'sFieldAfter':this.sFieldAfter, 'sExprType':'KEY'});
if(vParserResult){
return vParserResult;
}else{
this.sResultCode = oParser.sResultCode;
return false;
}
}
XBSTable.prototype.GetRecordsWhere = function(sWhere){
// base method, parse sWhere, process result and create record numbers array
this.sResultCode = 'OK';
var oParser = new XBSEParser();
var vParserResult = oParser.Parse(sWhere,{'aFieldNames':this._aFieldNames, 'sFieldBefore':this.sFieldBefore, 'sFieldAfter':this.sFieldAfter, 'sExprType':'WHERE'});
if(!vParserResult){
this.sResultCode = oParser.sResultCode;
return false;
}
var aFirstLevelRecords = this.GetRecordsAllUnPacked(); // temporary record numbers array, not packed
var aResultRecords = this._GetRecordsByParsedArray(vParserResult,aFirstLevelRecords,1);
if(aResultRecords){
return aResultRecords;
}else{
return false;
}
}
XBSTable.prototype.GetRows = function(aFields,aRecords,nOffset,nLimit){
this.sResultCode = 'OK';
var aRet = [], aRecord, nStart, nEnd, j, i;
if(nOffset < 0){
nStart = 0;
}else if(nOffset > aRecords.length - 1){
return aRet;
}else{
nStart = nOffset;
}
if(nLimit <= 0){
return aRet;
}else if(nLimit > aRecords.length - 1){
nEnd = aRecords.length;
}else{
nEnd = nOffset + nLimit;
if(nEnd > aRecords.length){
nEnd = aRecords.length;
}
}
for(i = nStart; i < nEnd; i++){ // solid
aRecord = [];
for(j = 0; j < aFields.length; j++){ // solid
aRecord.push(this._oCols[aFields[j]][aRecords[i]]);
}
aRet.push(aRecord);
}
return aRet;
}
XBSTable.prototype.GetRowsAll = function(aRecords,nOffset,nLimit){
this.sResultCode = 'OK';
var aRet = [], aRecord, nStart, nEnd, j, i;
if(nOffset < 0){
nStart = 0;
}else if(nOffset > aRecords.length - 1){
return aRet;
}else{
nStart = nOffset;
}
if(nLimit <= 0){
return aRet;
}else if(nLimit > aRecords.length - 1){
nEnd = aRecords.length;
}else{
nEnd = nOffset + nLimit;
if(nEnd > aRecords.length){
nEnd = aRecords.length;
}
}
for(i = nStart; i < nEnd; i++){ // solid
aRecord = [];
for(j = 0; j < this._aFieldNames.length; j++){ // solid
aRecord.push(this._oCols[this._aFieldNames[j]][aRecords[i]]);
}
aRet.push(aRecord);
}
return aRet;
}
XBSTable.prototype.GetRecordsAllPacked = function(){
var aRet = [], i;
for(i = 0; i < this._nRecords; i++){
if(!this._aDeleted[i]){
aRet.push(i);
}
}
return aRet;
}
XBSTable.prototype.GetRecordsAllUnPacked = function(){
return this._aRecordsUnPack(this.GetRecordsAllPacked());
}
XBSTable.prototype.SortRecordsOrderBy = function(aRecords,sOrderBy,bReverse){
this.sResultCode = 'OK';
var oParser = new XBSEParser();
var vParserResult = oParser.Parse(sOrderBy,{'aFieldNames':this._aFieldNames, 'sFieldBefore':this.sFieldBefore, 'sFieldAfter':this.sFieldAfter, 'sExprType':'KEY'});
if(vParserResult && (vParserResult.sType == 'object_key_scalar')){
var oKey = new XBSKey(this,vParserResult.sExprFieldsX,vParserResult.oFields);
if(oKey.Rebuild(aRecords)){
if(bReverse){
aRecords.reverse();
}
return true;
}else{
this.sResultCode = oKey.sResultCode;
return false;
}
}else{
this.sResultCode = oParser.sResultCode;
return false;
}
}
XBSTable.prototype.GetRecordsGroupBy = function(aRecords,sGroupBy){
this.sResultCode = 'OK';
var oParser = new XBSEParser();
var vParserResult = oParser.Parse(sGroupBy,{'aFieldNames':this._aFieldNames, 'sFieldBefore':this.sFieldBefore, 'sFieldAfter':this.sFieldAfter, 'sExprType':'KEY'});
if(vParserResult && (vParserResult.sType == 'object_key_scalar')){
var vCurrValue, vPrevValue, aResultRecords = [], i, fFuncGroup = new Function('table', 'n', 'return(' + vParserResult.sExprFieldsX + ');');
for(i = 0; i < aRecords.length; i++){ // solid
vCurrValue = fFuncGroup(this,aRecords[i]); // because packed
if(vCurrValue != vPrevValue){
aResultRecords.push(aRecords[i]);
vPrevValue = vCurrValue;
}
}
return aResultRecords;
}else{
this.sResultCode = oParser.sResultCode;
return false;
}
}
XBSTable.prototype.GetStructure = function(){
this.sResultCode = 'OK';
var aResult = [];
for(var i=0; i<this._aStructure.length; i++){
aResult.push([this._aStructure[i][0],this._aStructure[i][1]]);
}
return aResult;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// private methods
///////////////////////////////////////////////////////////////////////////////////////////////////////////
XBSTable.prototype._GetRecordsByParsedArray = function(oPA,aUpperRecords,nLevel){
//array_OR (OR array)
//array_AND (AND array)
//object_calc_scalar (scalar calculation object)
//object_calc_logical (logical calculation object) - not done yet
// aUpperRecords - not packed, for faster intersection of temporary results with it
var aResultRecords = [], aResultRecordsU = [], i, j, vConstant;
switch(oPA.sType){
case 'array_OR' : // (a)rray of (o)r elements
// processing of OR-elements, result - union
// aResultRecords - NOT PACKED here for faster unitization, will be packed after cycle
var sExprOR = '', aBufRecords = [];
for(i = 0; i < oPA.aOR.length; i++){ // solid
switch(oPA.aOR[i].sType){
case 'object_calc_scalar' :
try{
vConstant = eval(oPA.aOR[i].sConstant);
}catch(e){
this.sResultCode = "TABLE_BAD_CONSTANT : " + oPA.aOR[i].sConstant;
return false;
}
if(this.IsKey(oPA.aOR[i].sExprFields)){ // key exists
if(nLevel>1){
// not first level, upper level records exist, intersect result with it
aBufRecords = this._GetRecordsByKeyPacked(oPA.aOR[i].sExprFields,oPA.aOR[i].sOperation,vConstant,aUpperRecords);
}else{
// first level, no upper level records exist
aBufRecords = this._GetRecordsByKeyPacked(oPA.aOR[i].sExprFields,oPA.aOR[i].sOperation,vConstant);
}
if(!aBufRecords){
return false;
}
for(j = 0; j < aBufRecords.length; j++){ // solid
aResultRecords[aBufRecords[j]] = true;
}
}else{
if(sExprOR.length > 0){
sExprOR += '||';
}
sExprOR += '(' + '(' + oPA.aOR[i].sExprFieldsX + ') ' + oPA.aOR[i].sOperation + ' "' + vConstant.toString().replace('"','\"','g') + '"'+ ')';
}
break;
case 'object_calc_logical' : // (o)ject of (c)alculation of (l)ogical value
if(sExprOR.length > 0){
sExprOR += '||';
}
sExprOR += '(' + oPA.aOR[i].sExprFieldsX + ')';
break;
case 'array_OR' :
case 'array_AND' :
aBufRecords = this._GetRecordsByParsedArray(oPA.aOR[i],aUpperRecords,nLevel+1);
if(!aBufRecords){
return false;
}
for(j = 0; j < aBufRecords.length; j++){ // solid
aResultRecords[aBufRecords[j]] = true;
}
break;
}//switch
}//for
if(sExprOR.length > 0){
aBufRecords = this._GetRecordsByExpr(sExprOR,aUpperRecords);
if(!aBufRecords){
return false;
}
for(j = 0; j < aBufRecords.length; j++){ // solid
aResultRecords[aBufRecords[j]] = true;
}
}
aResultRecords = this._aRecordsPack(aResultRecords);
break;
case 'array_AND' : // (a)rray of (a)nd elements
// processing of AND-elements, result - intersection
// aResultRecords - PACKED here for faster intersection, it will be used next seek on next condition of same key
var sExprAND = '', aBufRecords = [], bFirstKeyCalc = true, bResultNotReady = true, sPrevKey = '', aBufRecords2;
for(i = 0; i < oPA.aAND.length; i++){ // solid
switch(oPA.aAND[i].sType){
case 'object_calc_scalar' :
try{
vConstant = eval(oPA.aAND[i].sConstant);
}catch(e){
this.sResultCode = "TABLE_BAD_CONSTANT : " + oPA.aAND[i].sConstant;
return false;
}
if(this.IsKey(oPA.aAND[i].sExprFields)){ // key exists
if(bFirstKeyCalc){
if(nLevel>1){
aResultRecords = this._GetRecordsByKeyPacked(oPA.aAND[i].sExprFields,oPA.aAND[i].sOperation,vConstant,aUpperRecords);
}else{
aResultRecords = this._GetRecordsByKeyPacked(oPA.aAND[i].sExprFields,oPA.aAND[i].sOperation,vConstant);
}
if(!aResultRecords){
return false;
}
bFirstKeyCalc = false;
}else{
if(sPrevKey==oPA.aAND[i].sExprFields){
aResultRecords = this._GetRecordsByKeyPacked(oPA.aAND[i].sExprFields,oPA.aAND[i].sOperation,vConstant,null,aResultRecords);
}else{
aResultRecords = this._GetRecordsByKeyPacked(oPA.aAND[i].sExprFields,oPA.aAND[i].sOperation,vConstant,this._aRecordsUnPack(aResultRecords));
}
}
if(!aResultRecords){
return false;
}
bResultNotReady = false;
sPrevKey = oPA.aAND[i].sExprFields;
}else{
if(sExprAND.length > 0){
sExprAND += '&&';
}
sExprAND += '(' + '(' + oPA.aAND[i].sExprFieldsX + ') ' + oPA.aAND[i].sOperation + ' "' + vConstant.toString().replace('"','\"') + '"'+ ')';
}
break;
case 'object_calc_logical' : // (o)ject of (c)alculation of (l)ogical value
if(sExprAND.length > 0){
sExprAND += '&&';
}
sExprAND += '(' + oPA.aAND[i].sExprFieldsX + ')';
break;
case 'array_OR' :
case 'array_AND' :
if(bFirstKeyCalc){
aResultRecords = this._GetRecordsByParsedArray(oPA.aAND[i],aUpperRecords,nLevel+1);
}else{
aResultRecords = this._GetRecordsByParsedArray(oPA.aAND[i],this._aRecordsUnPack(aResultRecords),nLevel+1);
}
if(!aResultRecords){
return false;
}
bResultNotReady = false;
break;
}//switch
if(!bFirstKeyCalc && aResultRecords.length == 0){
break;
}
}//for
if(sExprAND.length > 0){
if(bResultNotReady){
aResultRecords = this._GetRecordsByExpr(sExprAND,aUpperRecords);
if(!aResultRecords){
return false;
}
}else{
if(aResultRecords.length > 0){
aResultRecords = this._GetRecordsByExpr(sExprAND,this._aRecordsUnPack(aResultRecords));
if(!aResultRecords){
return false;
}
}
}
}
break;
case 'object_calc_scalar' :
// select records on object fields - expression with fields, comparsion, value
try{
vConstant = eval(oPA.sConstant);
}catch(e){
this.sResultCode = "TABLE_BAD_CONSTANT : " + oPA.sConstant;
return false;
}
if(this.IsKey(oPA.sExprFields)){ // key exists
if(nLevel>1){
aResultRecords = this._GetRecordsByKeyPacked(oPA.sExprFields,oPA.sOperation,vConstant,aUpperRecords);
}else{
aResultRecords = this._GetRecordsByKeyPacked(oPA.sExprFields,oPA.sOperation,vConstant);
}
if(!aResultRecords){
return false;
}
}else{
var sExpr = '(' + oPA.sExprFieldsX + ') ' + oPA.sOperation + ' "' + vConstant.toString().replace('"','\"') + '"';
aResultRecords = this._GetRecordsByExpr(sExpr,aUpperRecords);
if(!aResultRecords){
return false;
}
}
break;
case 'object_calc_logical' :
// select records on object fields - expression with fields only
var sExpr = '(' + oPA.sExprFieldsX + ')';
aResultRecords = this._GetRecordsByExpr(sExpr,aUpperRecords);
if(!aResultRecords){
return false;
}
break;
}
if(this.sResultCode == 'OK'){
return aResultRecords;
}else{
return false;
}
}
XBSTable.prototype._aRecordsPack = function(aRecordsU){
var aRecordsP = [], i;
for(i = 0; i < aRecordsU.length; i++){ // solid or not - no difference, pack true values only
if(aRecordsU[i] == true){
aRecordsP.push(i);
}
}
return aRecordsP;
}
XBSTable.prototype._aRecordsUnPack = function(aRecordsP){
var aRecordsU = [], i;
for(i = 0; i < aRecordsP.length; i++){ // solid
aRecordsU[aRecordsP[i]] = true;
}
return aRecordsU;
}
XBSTable.prototype._GetRecordsByKeyUnpacked = function(sKeyID,sKeyOper,vKeyValue,aUpperRecords,aExtRecords){
var aResultRecords;
var oKey = this._oKeys[sKeyID];
switch(sKeyOper){
case '>=' : aResultRecords = oKey.GetRecordsUnpackedGREQ(vKeyValue,aUpperRecords,aExtRecords); break; //! index and value types match check
case '<=' : aResultRecords = oKey.GetRecordsUnpackedLEEQ(vKeyValue,aUpperRecords,aExtRecords); break;
case '!=' : aResultRecords = oKey.GetRecordsUnpackedNE(vKeyValue,aUpperRecords,aExtRecords); break;
case '>' : aResultRecords = oKey.GetRecordsUnpackedGR(vKeyValue,aUpperRecords,aExtRecords); break;
case '<' : aResultRecords = oKey.GetRecordsUnpackedLE(vKeyValue,aUpperRecords,aExtRecords); break;
case '==' : aResultRecords = oKey.GetRecordsUnpackedEQ(vKeyValue,aUpperRecords,aExtRecords); break;
}
if(!aResultRecords){
this.sResultCode = oKey.sResultCode;
return false;
}
return aResultRecords;
}
XBSTable.prototype._GetRecordsByKeyPacked = function(sKeyID,sKeyOper,vKeyValue,aUpperRecords,aExtRecords){
var aResultRecords;
var oKey = this._oKeys[sKeyID];
switch(sKeyOper){
case '>=' : aResultRecords = oKey.GetRecordsPackedGREQ(vKeyValue,aUpperRecords,aExtRecords); break; //! index and value types match check
case '<=' : aResultRecords = oKey.GetRecordsPackedLEEQ(vKeyValue,aUpperRecords,aExtRecords); break;
case '!=' : aResultRecords = oKey.GetRecordsPackedNE(vKeyValue,aUpperRecords,aExtRecords); break;
case '>' : aResultRecords = oKey.GetRecordsPackedGR(vKeyValue,aUpperRecords,aExtRecords); break;
case '<' : aResultRecords = oKey.GetRecordsPackedLE(vKeyValue,aUpperRecords,aExtRecords); break;
case '==' : aResultRecords = oKey.GetRecordsPackedEQ(vKeyValue,aUpperRecords,aExtRecords); break;
}
if(!aResultRecords){
this.sResultCode = oKey.sResultCode;
return false;
}
return aResultRecords;
}
XBSTable.prototype._GetRecordsByExpr = function(sExpr,aUpperRecords){
var aResultRecords = [], i, fFunc = new Function('table', 'n', 'return(' + sExpr + ');');
try{
for(i = 0; i < aUpperRecords.length; i++){ // check solid or not
if(aUpperRecords[i] && fFunc(this,i)){ // aUpperRecords[i] check true or not BEFORE function call
aResultRecords.push(i);
}
}
}catch(e){
this.sResultCode = 'TABLE_NOT_GET_RECORDS_BY_EXPR_UN';
return false;
}
return aResultRecords;
}
XBSTable.prototype._IsRecordNumberOK = function(nNumber){
if(nNumber < 0 || nNumber >= this._nRecords){
return false;
}
for(var i = 0; i < this._aDeleted.length; i++){ // not solid, not packed
if(this._aDeleted[nNumber]){
return false;
}
}
return true;
}
|