package railo.runtime.tag;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import railo.commons.lang.ExceptionUtil;
import railo.commons.lang.StringUtil;
import railo.runtime.config.ConfigImpl;
import railo.runtime.db.DataSource;
import railo.runtime.db.DataSourceManager;
import railo.runtime.db.DatasourceConnection;
import railo.runtime.db.SQL;
import railo.runtime.db.SQLImpl;
import railo.runtime.db.SQLItem;
import railo.runtime.db.SQLItemImpl;
import railo.runtime.debug.DebuggerPro;
import railo.runtime.debug.DebuggerUtil;
import railo.runtime.exp.DatabaseException;
import railo.runtime.exp.PageException;
import railo.runtime.ext.tag.TagImpl;
import railo.runtime.op.Caster;
import railo.runtime.type.QueryImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.scope.Form;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.CollectionUtil;
import railo.runtime.type.util.ListUtil;
/**
* Updates existing records in data sources.
*
*
*
**/
public final class Update extends TagImpl {
/** If specified, password overrides the password value specified in the ODBC setup. */
private String password;
/** Name of the data source that contains a table. */
private String datasource;
/** If specified, username overrides the username value specified in the ODBC setup. */
private String username;
/** A comma-separated list of form fields to update. If this attribute is not specified, all fields
** in the form are included in the operation. */
private String formfields;
/** For data sources that support table ownership, for example, SQL Server, Oracle, and Sybase
** SQL Anywhere, use this field to specify the owner of the table. */
private String tableowner;
/** Name of the table you want to update. */
private String tablename;
/** For data sources that support table qualifiers, use this field to specify the qualifier for the
** table. The purpose of table qualifiers varies across drivers. For SQL Server and Oracle, the qualifier
** refers to the name of the database that contains the table. For the Intersolv dBase driver, the
** qualifier refers to the directory where the DBF files are located. */
private String tablequalifier;
@Override
public void release() {
super.release();
password=null;
username=null;
formfields=null;
tableowner=null;
tablequalifier=null;
datasource=null;
}
/** set the value password
* If specified, password overrides the password value specified in the ODBC setup.
* @param password value to set
**/
public void setPassword(String password) {
this.password=password;
}
/** set the value datasource
* Name of the data source that contains a table.
* @param datasource value to set
**/
public void setDatasource(String datasource) {
this.datasource=datasource;
}
/** set the value username
* If specified, username overrides the username value specified in the ODBC setup.
* @param username value to set
**/
public void setUsername(String username) {
this.username=username;
}
/** set the value formfields
* A comma-separated list of form fields to update. If this attribute is not specified, all fields
* in the form are included in the operation.
* @param formfields value to set
**/
public void setFormfields(String formfields) {
this.formfields=formfields;
}
/** set the value tableowner
* For data sources that support table ownership, for example, SQL Server, Oracle, and Sybase
* SQL Anywhere, use this field to specify the owner of the table.
* @param tableowner value to set
**/
public void setTableowner(String tableowner) {
this.tableowner=tableowner;
}
/** set the value tablename
* Name of the table you want to update.
* @param tablename value to set
**/
public void setTablename(String tablename) {
this.tablename=tablename;
}
/** set the value tablequalifier
* For data sources that support table qualifiers, use this field to specify the qualifier for the
* table. The purpose of table qualifiers varies across drivers. For SQL Server and Oracle, the qualifier
* refers to the name of the database that contains the table. For the Intersolv dBase driver, the
* qualifier refers to the directory where the DBF files are located.
* @param tablequalifier value to set
**/
public void setTablequalifier(String tablequalifier) {
this.tablequalifier=tablequalifier;
}
@Override
public int doStartTag() {
return SKIP_BODY;
}
@Override
public int doEndTag() throws PageException {
Object ds=Insert.getDatasource(pageContext,datasource);
DataSourceManager manager = pageContext.getDataSourceManager();
DatasourceConnection dc=ds instanceof DataSource?
manager.getConnection(pageContext,(DataSource)ds,username,password):
manager.getConnection(pageContext,Caster.toString(ds),username,password);
try {
Struct meta =null;
try {
meta=Insert.getMeta(dc,tablequalifier,tableowner,tablename);
}
catch(SQLException se){
meta=new StructImpl();
}
String[] pKeys=getPrimaryKeys(dc);
SQL sql=createSQL(dc,pKeys,meta);
if(sql!=null) {
railo.runtime.type.Query query = new QueryImpl(pageContext,dc,sql,-1,-1,-1,"query");
if(pageContext.getConfig().debug()) {
String dsn=ds instanceof DataSource?((DataSource)ds).getName():Caster.toString(ds);
boolean logdb=((ConfigImpl)pageContext.getConfig()).hasDebugOptions(ConfigImpl.DEBUG_DATABASE);
if(logdb){
boolean debugUsage=DebuggerUtil.debugQueryUsage(pageContext,query);
((DebuggerPro)pageContext.getDebugger()).addQuery(debugUsage?query:null,dsn,"",sql,query.getRecordcount(),pageContext.getCurrentPageSource(),query.getExecutionTime());
}
}
}
return EVAL_PAGE;
}
finally {
manager.releaseConnection(pageContext,dc);
}
}
private String[] getPrimaryKeys(DatasourceConnection dc) throws PageException {
railo.runtime.type.Query query = getPrimaryKeysAsQuery(dc);
int recCount=query.getRecordcount();
String[] pKeys=new String[recCount];
if(recCount==0) throw new DatabaseException("can't find primary keys of table ["+tablename+"]",null,null,dc);
for(int row=1;row<=recCount;row++) {
pKeys[row-1]=Caster.toString(query.getAt("column_name",row));
}
return pKeys;
}
private railo.runtime.type.Query getPrimaryKeysAsQuery(DatasourceConnection dc) throws PageException {
// Read Meta Data
DatabaseMetaData meta;
try {
meta = dc.getConnection().getMetaData();
}
catch (SQLException e) {
throw new DatabaseException(e,dc);
}
try {
return new QueryImpl(meta.getPrimaryKeys(tablequalifier, tableowner, tablename),-1,"query",pageContext.getTimeZone());
}
catch (SQLException e) {
try {
return new QueryImpl(meta.getBestRowIdentifier(tablequalifier, tableowner, tablename, 0, false),-1,"query",pageContext.getTimeZone());
}
catch (SQLException sqle) {
throw new DatabaseException("can't find primary keys of table ["+tablename+"] ("+ExceptionUtil.getMessage(sqle)+")",null,null,dc);
}
}
}
/**
* @param keys primary Keys
* @return return SQL String for update
* @throws PageException
*/
private SQL createSQL(DatasourceConnection dc,String[] keys, Struct meta) throws PageException {
String[] fields=null;
Form form = pageContext.formScope();
if(formfields!=null) fields=ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(formfields,','));
else fields=CollectionUtil.keysAsString(pageContext.formScope());
StringBuffer set=new StringBuffer();
StringBuffer where=new StringBuffer();
ArrayList setItems=new ArrayList();
ArrayList whereItems=new ArrayList();
String field;
for(int i=0;i<fields.length;i++) {
field = StringUtil.trim(fields[i],null);
if(StringUtil.startsWithIgnoreCase(field, "form."))
field=field.substring(5);
if(!field.equalsIgnoreCase("fieldnames")) {
if(ArrayUtil.indexOfIgnoreCase(keys,field)==-1) {
if(set.length()==0) set.append(" set ");
else set.append(",");
set.append(field);
set.append("=?");
ColumnInfo ci=(ColumnInfo) meta.get(field);
if(ci!=null)setItems.add(new SQLItemImpl(form.get(field,null),ci.getType()));
else setItems.add(new SQLItemImpl(form.get(field,null)));
}
else {
if(where.length()==0) where.append(" where ");
else where.append(" and ");
where.append(field);
where.append("=?");
whereItems.add(new SQLItemImpl(form.get(field,null)));
}
}
}
if((setItems.size()+whereItems.size())==0) return null;
if(whereItems.size()==0)throw new DatabaseException("can't find primary keys ["+ListUtil.arrayToList(keys,",")+"] of table ["+tablename+"] in form scope",null,null,dc);
StringBuffer sql=new StringBuffer();
sql.append("update ");
if(tablequalifier!=null && tablequalifier.length()>0) {
sql.append(tablequalifier);
sql.append('.');
}
if(tableowner!=null && tableowner.length()>0) {
sql.append(tableowner);
sql.append('.');
}
sql.append(tablename);
sql.append(set);
sql.append(where);
return new SQLImpl(sql.toString(),arrayMerge(setItems,whereItems));
}
private SQLItem[] arrayMerge(ArrayList setItems, ArrayList whereItems) {
SQLItem[] items=new SQLItem[setItems.size()+whereItems.size()];
int index=0;
// Item
int size=setItems.size();
for(int i=0;i<size;i++) {
items[index++]=(SQLItem) setItems.get(i);
}
// Where
size=whereItems.size();
for(int i=0;i<size;i++) {
items[index++]=(SQLItem) whereItems.get(i);
}
return items;
}
}