0
1
Fork 0
hedera-web-mindshore/package/usr/share/hedera-web/js/db/model.js

884 lines
17 KiB
JavaScript
Raw Normal View History

Db.Model = new Class ().extend
({
Status:
{
CLEAN : 1
,LOADING : 2
,READY : 3
,ERROR : 4
}
});
Db.Model.implement
({
Extends: Vn.Object
,Tag: 'db-model'
,Child: 'query'
,Properties:
{
/**
* The connection used to execute the statement.
**/
conn:
{
type: Db.Conn
,set: function (x)
{
this._conn = x;
this.refresh ();
}
,get: function ()
{
return this._conn;
}
},
/**
* The result index.
**/
resultIndex:
{
type: Number
,set: function (x)
{
this._resultIndex = x;
}
,get: function ()
{
return this._resultIndex;
}
},
/**
* The batch used to execute the statement.
**/
batch:
{
type: Sql.Batch
,set: function (x)
{
this.link ({_batch: x}, {'changed': this.refresh});
this.refresh ();
}
,get: function ()
{
return this._batch;
}
},
/**
* The model select statement.
**/
stmt:
{
type: Sql.Stmt
,set: function (x)
{
this._stmt = x;
this.refresh ();
}
,get: function ()
{
return this._stmt;
}
},
/**
* The model query.
**/
query:
{
type: String
,set: function (x)
{
this._stmt = new Sql.String ({query: x});
}
,get: function ()
{
return this._stmt.render (null);
}
},
/**
* The main table.
**/
mainTable:
{
type: String
,set: function (x)
{
this._mainTable = null;
this.requestedMainTable = x;
this.refreshMainTable ();
}
,get: function ()
{
return this._mainTable;
}
},
/**
* Determines if the model is updatable.
**/
updatable:
{
type: Boolean
,set: function (x)
{
this._updatable = false;
this.requestedUpdatable = x;
this.refreshUpdatable ();
}
,get: function ()
{
return this._updatable;
}
},
/**
* The number of rows in the model.
**/
numRows:
{
type: Number
,get: function ()
{
if (this.data)
return this.data.length;
return 0;
}
},
/**
* The current status of the model.
**/
status:
{
type: Number
,get: function ()
{
return this._status;
}
},
/**
* Checks if the model data is ready.
**/
ready:
{
type: Boolean
,get: function ()
{
return this._status == Db.Model.Status.READY;
}
}
}
,_resultIndex: 0
,_batch: null
,_stmt: null
,_status: Db.Model.Status.CLEAN
,requestedMainTable: null
,requestedUpdatable: true
,data: null
,columns: null
,columnMap: null
,insertedRow: -1
,defaults: []
,columnDefaults: []
,sortColumn: -1
2015-02-08 15:38:38 +00:00
,requestedIndexes: {}
,indexes: []
,initialize: function (props)
{
this.parent (props);
2015-02-08 15:38:38 +00:00
this.cleanData ();
this.setStatus (Db.Model.Status.CLEAN);
}
,loadXml: function (builder, node)
{
this.parent (builder, node);
var query = node.firstChild.nodeValue;
if (query)
this.query = query;
}
,refresh: function ()
{
if (this._stmt && this._batch)
this._stmt.findHolders (this._batch);
if (this._conn && this._stmt
&& (!this._batch || this._batch.isReady ()))
{
this.setStatus (Db.Model.Status.LOADING);
this._conn.execStmt (this._stmt, this.selectDone.bind (this), this._batch);
}
else
2015-02-08 15:38:38 +00:00
{
this.cleanData ();
this.setStatus (Db.Model.Status.CLEAN);
}
}
2015-02-08 15:38:38 +00:00
,cleanData: function (error)
{
this.data = null;
this.columns = null;
this.columnMap = null;
2015-02-08 15:38:38 +00:00
this.indexes = [];
}
,refreshUpdatable: function ()
{
var oldValue = this._updatable;
this._updatable = this._mainTable != null && this.requestedUpdatable;
if (oldValue != this._updatable)
this.signalEmit ('updatable-changed');
}
,refreshMainTable: function ()
{
var newMainTable = null;
var newMainSchema = null;
var x = this.columns;
if (x)
for (var i = 0; i < x.length; i++)
if (x[i].flags & Db.Conn.Flag.PRI_KEY)
if (!this.requestedMainTable
|| x[i].table === this.requestedMainTable)
{
newMainTable = x[i].table;
break;
}
this._mainTable = newMainTable;
this.refreshUpdatable ();
}
/**
* Sets the default value for inserted fields.
*
* @param {String} field The destination field name
* @param {String} table The destination table name
* @param {Object} value The default value
**/
,setDefaultFromValue: function (field, table, value)
{
this.defaults.push
({
field: field
,table: table
,value: value
});
}
/**
* Sets the default value for inserted fields from another column in the
* model.
*
* @param {String} field The destination field name
* @param {String} table The destination table name
* @param {String} srcColumn The source column
**/
,setDefaultFromColumn: function (field, table, srcColumn)
{
this.columnDefaults.push
({
field: field
,table: table
,srcColumn: srcColumn
});
}
/**
* Checks if the column exists.
*
* @param {integer} column The column index
* @return {Boolean} %true if column exists, %false otherwise
**/
,checkColExists: function (column)
{
return this.columns && column >= 0 && column < this.columns.length;
}
/**
* Checks if the row exists.
*
* @param {integer} row The row index
* @return {Boolean} %true if row exists, %false otherwise
**/
,checkRowExists: function (row)
{
return this.data && row >= 0 && row < this.data.length;
}
,checkRowUpdatable: function (row)
{
return this.checkRowExists (row);
}
/**
* Get the index of the column from its name.
*
* @param {string} columnName The column name
* @return {number} The column index or -1 if column not exists
**/
,getColumnIndex: function (columnName)
{
var index;
if (this.columnMap
&& (index = this.columnMap[columnName]) !== undefined)
return index;
return -1;
}
/**
* Gets a value from the model.
*
* @param {number} row The row index
* @param {string} columnName The column name
* @return {mixed} The value
**/
,get: function (row, columnName)
{
var index = this.getColumnIndex (columnName);
if (index != -1)
return this.getByIndex (row, index);
return undefined;
}
/**
* Updates a value on the model.
*
* @param {number} row The row index
* @param {string} columnName The column name
* @param {mixed} value The new value
**/
,set: function (row, columnName, value)
{
var index = this.getColumnIndex (columnName);
if (index != -1)
this.setByIndex (row, index, value);
}
/**
* Gets a value from the model using the column index.
*
* @param {number} row The row index
* @param {number} column The column index
* @return {mixed} The value
**/
,getByIndex: function (row, column)
{
if (this.checkRowExists (row) && this.checkColExists (column))
return this.data[row][column];
return undefined;
}
/**
* Updates a value on the model using the column index.
*
* @param {number} row The row index
* @param {number} col The column index
* @param {mixed} value The new value
**/
,setByIndex: function (row, col, value)
{
if (!(this.checkRowUpdatable (row) && this.checkColExists (col)))
return;
if (row == this.insertedRow)
{
this.performUpdate (row, [col], [value]);
return;
}
var column = this.columns[col];
var where = this.getWhere (column.table, row, false);
if (where)
{
var multiStmt = new Sql.MultiStmt ();
var table = new Sql.Table
({
name: column.orgtable
,schema: column.db
});
var update = new Sql.Update ({where: where});
update.addTarget (table);
update.addSet (column.orgname, value);
multiStmt.addStmt (update);
var select = new Sql.Select ({where: where});
select.addTarget (table);
select.addField (column.orgname);
multiStmt.addStmt (select);
var updateData = {
row: row
,columns: [col]
};
this._conn.execStmt (multiStmt,
this.updateDone.bind (this, updateData));
}
else
this.sendInsert (column.table, row, col, value);
}
/**
* Deletes a row from the model.
*
* @param {number} row The row index
**/
,deleteRow: function (row)
{
if (!this.checkRowUpdatable (row))
return;
if (row != this.insertedRow)
{
var where = this.getWhere (this._mainTable, row, false);
if (where)
{
var deleteQuery = new Sql.Delete ({where: where});
var table = this.getTarget (this._mainTable);
deleteQuery.addTarget (table);
this._conn.execStmt (deleteQuery,
this.deleteDone.bind (this, row));
}
}
else
{
this.performDelete (row);
this.insertedRow = -1;
}
}
/**
* Inserts a new row on the model.
*
* @return The index of the inserted row
**/
,insertRow: function ()
{
if (!this._updatable || this.insertedRow != -1)
return -1;
var x = this.columns;
var newRow = new Array (x.length);
for (var i = 0; i < x.length; i++)
if (x[i].table === this._mainTable)
newRow[i] = x[i].def;
else
newRow[i] = null;
this.insertedRow = this.data.push (newRow) - 1;
this.signalEmit ('row-inserted', this.insertedRow);
return this.insertedRow;
}
,performOperations: function ()
{
if (this.insertedRow == -1)
return;
this.sendInsert (this._mainTable, this.insertedRow, -1, null);
}
/*
* Function used to sort the model.
*/
,sortFunction: function (column, a, b)
{
if (a[column] < b[column])
return -1;
else if (a[column] > b[column])
return 1;
return 0;
}
/**
* Orders the model by the specified column.
*
* @param {integer} column the column index
**/
,sort: function (column)
{
if (!this.checkColExists (column))
return;
this.setStatus (Db.Model.Status.LOADING);
if (column != this.sortColumn)
{
this.data.sort (this.sortFunction.bind (this, column));
this.sortColumn = column;
}
else
this.data.reverse ();
this.setStatus (Db.Model.Status.READY);
}
2015-02-08 15:38:38 +00:00
/**
* Builds an internal hash index for the specified column, this speeds
* significantly searches on that column.
* Not implemented yet.
*
* @param {String} column The column name
**/
,indexColumn: function (column)
{
this.requestedIndexes[column] = true;
if (this._status == Db.Model.Status.READY)
this.buildIndex (column);
}
,buildIndex: function (column)
{
var columnIndex = this.getColumnIndex (column);
if (columnIndex != -1)
{
var index = {};
var data = this.data;
switch (this.columns[columnIndex].type)
{
case Db.Conn.Type.TIMESTAMP:
case Db.Conn.Type.DATE_TIME:
case Db.Conn.Type.DATE:
for (var i = 0; i < data.length; i++)
index[data[i][columnIndex].toString ()] = i;
break;
default:
for (var i = 0; i < data.length; i++)
index[data[i][columnIndex]] = i;
}
this.indexes[columnIndex] = index;
}
}
/**
* Searchs a value on the model and returns the row index of the first
* ocurrence.
*
* @param {String} column The column name
* @param {Object} value The value to search
* @return {integer} The column index
**/
,search: function (column, value)
{
var index = this.getColumnIndex (column);
return this.searchByIndex (index, value);
}
/**
* Searchs a value on the model and returns the row index of the first
* ocurrence.
*
* @param {integer} col The column index
* @param {Object} value The value to search
* @return {integer} The column index
**/
,searchByIndex: function (col, value)
{
if (!this.checkColExists (col))
return -1;
2015-02-08 15:38:38 +00:00
if (value)
switch (this.columns[col].type)
{
case Db.Conn.Type.BOOLEAN:
value = !!value;
break;
case Db.Conn.Type.INTEGER:
value = parseInt (value);
break;
case Db.Conn.Type.DOUBLE:
value = parseFloat (value);
break;
default:
value = value.toString ();
}
var index = this.indexes[col];
if (index)
{
2015-02-17 11:48:53 +00:00
if (index[value] !== undefined)
2015-02-08 15:38:38 +00:00
return index[value];
return -1;
}
var data = this.data;
switch (this.columns[col].type)
{
case Db.Conn.Type.TIMESTAMP:
case Db.Conn.Type.DATE_TIME:
case Db.Conn.Type.DATE:
{
for (var i = 0; i < data.length; i++)
if (value === data[i][col].toString ());
return i;
break;
}
default:
for (var i = 0; i < data.length; i++)
if (value === data[i][col])
return i;
}
return -1;
}
// private:
,setStatus: function (status)
{
this._status = status;
this.signalEmit ('status-changed', status);
}
,getTarget: function (table)
{
var x = this.columns;
for (var i = 0; i < x.length; i++)
if (x[i].table == table)
return new Sql.Table
({
name: x[i].orgtable
,schema: x[i].db
});
return null;
}
,getWhere: function (table, rowIndex, forInsert)
{
var keyFound = false;
var row = this.data[rowIndex];
var andOp = new Sql.Operation ({type: Sql.Operation.Type.AND});
var x = this.columns;
for (var i = 0; i < x.length; i++)
if (x[i].flags & Db.Conn.Flag.PRI_KEY
&& x[i].table === table)
{
var equalOp = new Sql.Operation ({type: Sql.Operation.Type.EQUAL});
equalOp.exprs.add (new Sql.Field ({name: x[i].orgname}));
andOp.exprs.add (equalOp);
if (row[i])
equalOp.exprs.add (new Sql.Value ({value: row[i]}));
else if (x[i].flags & Db.Conn.Flag.AI && forInsert)
equalOp.exprs.add (new Sql.Func ({name: 'LAST_INSERT_ID'}));
else
break;
keyFound = true;
}
return (keyFound) ? andOp : null;
}
,sendInsert: function (table, rowIndex, singleColumn, newValue)
{
var where = this.getWhere (table, rowIndex, true);
if (!where)
return;
var multiStmt = new Sql.MultiStmt ();
var target = this.getTarget (table);
var insert = new Sql.Insert ();
insert.addTarget (target);
multiStmt.addStmt (insert);
var x = this.defaults;
for (var i = 0; i < x.length; i++)
if (x[i].table === table)
insert.addSet (x[i].field, x[i].value);
var x = this.columnDefaults;
for (var i = 0; i < x.length; i++)
if (x[i].table === table)
insert.addSet (x[i].field, this.get (rowIndex, x[i].srcColumn));
var select = new Sql.Select ({where: where});
select.addTarget (target);
multiStmt.addStmt (select);
var columns = [];
var row = this.data[rowIndex];
var x = this.columns;
for (var i = 0; i < x.length; i++)
if (x[i].table === table)
{
var setValue = singleColumn != i ? row[i] : newValue;
if (setValue)
insert.addSet (x[i].orgname, setValue);
select.addField (x[i].orgname);
columns.push (i);
}
var updateData = {
row: rowIndex
,columns: columns
};
this._conn.execStmt (multiStmt,
this.updateDone.bind (this, updateData));
}
,selectDone: function (resultSet)
{
var result;
var dataResult;
2015-02-08 15:38:38 +00:00
this.cleanData ();
for (var i = 0; result = resultSet.fetchResult (); i++)
if (i == this._resultIndex)
dataResult = result;
if (dataResult && typeof dataResult === 'object')
{
this.sortColumn = -1;
this.data = dataResult.data;
this.columns = dataResult.columns;
this.columnMap = dataResult.columnMap;
this.repairColumns (this.columns);
this.refreshMainTable ();
2015-02-08 15:38:38 +00:00
for (column in this.requestedIndexes)
this.buildIndex (column);
this.setStatus (Db.Model.Status.READY);
}
else
2015-02-08 15:38:38 +00:00
this.setStatus (Db.Model.Status.ERROR);
}
,updateDone: function (updateData, resultSet)
{
var newValues;
if (resultSet.fetchResult () && (newValues = resultSet.fetchRow ()))
this.performUpdate (updateData.row, updateData.columns, newValues);
}
,performUpdate: function (rowIndex, columns, newValues)
{
this.signalEmit ('row-updated-before', rowIndex);
var row = this.data[rowIndex];
for (var i = 0; i < columns.length; i++)
row[columns[i]] = newValues[i];
this.signalEmit ('row-updated', rowIndex, columns);
}
,deleteDone: function (rowIndex, resultSet)
{
if (resultSet.fetchResult())
this.performDelete (rowIndex);
}
,performDelete: function (rowIndex)
{
var row = this.data[rowIndex];
if (!this.requestedMainTable)
{
this.signalEmit ('row-deleted-before', rowIndex);
this.data.splice (rowIndex, 1);
this.signalEmit ('row-deleted', rowIndex);
}
else
{
this.signalEmit ('row-updated-before', rowIndex);
var columns = [];
for (var i = 0; i < this.columns.length; i++)
if (this.columns[i].table == this._mainTable)
{
row[i] = null;
columns.push (i);
}
this.signalEmit ('row-updated', rowIndex, columns);
}
}
// Delete when MySQL FLAG and view orgname "bugs" are repaired:
,tableInfo: {}
,fieldFlags: {}
,setTableInfo: function (table, orgtable, db)
{
this.tableInfo[table] =
({
orgtable: orgtable
,db: db
});
}
,setFieldFlags: function (field, flags)
{
this.fieldFlags[field] = flags;
}
,repairColumns: function (columns)
{
for (var i = 0; i < columns.length; i++)
{
var newFlags = this.fieldFlags[columns[i].name];
if (newFlags)
columns[i].flags |= newFlags;
var tableInfo = this.tableInfo[columns[i].table];
if (tableInfo)
{
columns[i].orgtable = tableInfo.orgtable;
columns[i].db = tableInfo.db;
}
}
}
});