*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ---------------
* CommandSelect.java
* ---------------
* Author: Volker Berlin
*
*/
package smallsql.database;
import java.sql.*;
class CommandSelect extends Command{
private DataSources from; private Expression where;
RowSource join;
private Expressions groupBy;
private Expression having;
private Expressions orderBy;
private boolean isAggregateFunction;
private int maxRows;
private boolean isDistinct;
CommandSelect(Logger log){
super(log);
}
CommandSelect(Logger log, Expressions columnExpressions){
super(log, columnExpressions);
}
boolean compile(SSConnection con) throws Exception{
boolean needCompile = false;
if(from != null){
for(int i=0; i<from.size(); i++){
DataSource fromEntry = from.get(i);
needCompile |= fromEntry.init( con );
}
}
if(join == null){
join = new NoFromResult();
from = new DataSources();
needCompile = true;
}
if(!needCompile) return false;
for(int i=0; i<columnExpressions.size(); i++){
Expression col = columnExpressions.get(i);
if(col.getAlias() == null){
col.setAlias("col" + (i+1));
}
if(col.getType() != Expression.NAME){
compileLinkExpressionParams(col);
continue;
}
ExpressionName expr = (ExpressionName)col;
if("*".equals( expr.getName() )){
String tableAlias = expr.getTableAlias();
if(tableAlias != null){
int t=0;
for(; t<from.size(); t++){
DataSource fromEntry = from.get(t);
if(tableAlias.equalsIgnoreCase( fromEntry.getAlias() )){
TableView table = fromEntry.getTableView();
columnExpressions.remove(i);
i = compileAdd_All_Table_Columns( fromEntry, table, i ) - 1;
break;
}
}
if(t==from.size()) throw Utils.createSQLException( "The column prefix '" + tableAlias + "' does not match with a table name or alias name used in this query" );
}else{
columnExpressions.remove(i);
for(int t=0; t<from.size(); t++){
DataSource fromEntry = from.get(t);
TableView table = fromEntry.getTableView();
i = compileAdd_All_Table_Columns( fromEntry, table, i );
}
i--;
}
}else{
compileLinkExpressionName( expr );
}
}
if(where != null) compileLinkExpression( where );
if(having != null) compileLinkExpression( having );
if(orderBy != null) {
for(int i=0; i<orderBy.size(); i++){
compileLinkExpression( orderBy.get(i));
}
}
if(groupBy != null){
for(int i=0; i<groupBy.size(); i++){
compileLinkExpression( groupBy.get(i) );
}
}
if(join instanceof Join){
compileJoin( (Join)join );
}
if(where != null){
join = new Where( join, where );
}
if(isGroupResult()) {
GroupResult result = new GroupResult( this, join, groupBy, having, orderBy);
join = result;
}
if(isDistinct){
join = new Distinct( join, columnExpressions );
}
if(orderBy != null){
join = new SortedResult( join, orderBy );
}
return true;
}
final boolean isGroupResult(){
return groupBy != null || having != null || isAggregateFunction;
}
private void compileJoin( Join join ) throws Exception{
if(join.condition != null) compileLinkExpressionParams( join.condition );
if(join.left instanceof Join){
compileJoin( (Join)join.left );
}
if(join.right instanceof Join){
compileJoin( (Join)join.right );
}
}
private void compileLinkExpression( Expression expr) throws Exception{
if(expr.getType() == Expression.NAME)
compileLinkExpressionName( (ExpressionName)expr);
else compileLinkExpressionParams( expr );
}
private void compileLinkExpressionName( ExpressionName expr ) throws Exception{
String tableAlias = expr.getTableAlias();
if(tableAlias != null){
int t=0;
for(; t<from.size(); t++){
DataSource fromEntry = from.get(t);
if(tableAlias.equalsIgnoreCase( fromEntry.getAlias() )){
TableView table = fromEntry.getTableView();
int colIdx = table.findColumnIdx( expr.getName() );
if(colIdx>=0){
expr.setFrom( fromEntry, colIdx, table );
break;
}else
throw Utils.createSQLException("Invalid column name '" + expr.getName() + "'.");
}
}
if(t==from.size()) throw Utils.createSQLException( "The column prefix '" + tableAlias + "' does not match with a table name or alias name used in this query" );
}else{
int t=0;
for(; t<from.size(); t++){
DataSource fromEntry = from.get(t);
TableView table = fromEntry.getTableView();
int colIdx = table.findColumnIdx( expr.getName() );
if(colIdx>=0){
expr.setFrom( fromEntry, colIdx, table );
break;
}
}
if(t>=from.size()){
throw Utils.createSQLException("Invalid column name '" + expr.getName() + "'.");
}
}
compileLinkExpressionParams(expr);
}
private void compileLinkExpressionParams(Expression expr) throws Exception{
Expression[] params = expr.getParams();
isAggregateFunction = isAggregateFunction || expr.getType() >= Expression.GROUP_BEGIN;
if(params != null){
for(int k=0; k<params.length; k++){
Expression param = params[k];
int type = param.getType();
isAggregateFunction = isAggregateFunction || type >= Expression.GROUP_BEGIN;
if(type == Expression.NAME)
compileLinkExpressionName( (ExpressionName)param );
else compileLinkExpressionParams( param );
}
}
}
private final int compileAdd_All_Table_Columns( DataSource fromEntry, TableView table, int position){
for(int k=0; k<table.columns.size(); k++){
ExpressionName expr = new ExpressionName( table.columns.get(k).getName() );
expr.setFrom( fromEntry, k, table );
columnExpressions.add( position++, expr );
}
return position;
}
void executeImpl(SSConnection con, SSStatement st) throws Exception{
compile(con);
if((st.rsType == ResultSet.TYPE_SCROLL_INSENSITIVE || st.rsType == ResultSet.TYPE_SCROLL_SENSITIVE) &&
!join.isScrollable()){
join = new Scrollable(join);
}
join.execute();
rs = new SSResultSet( st, this );
}
void beforeFirst() throws Exception{
join.beforeFirst();
}
boolean isBeforeFirst() throws SQLException{
return join.isBeforeFirst();
}
boolean isFirst() throws SQLException{
return join.isFirst();
}
boolean first() throws Exception{
return join.first();
}
boolean previous() throws Exception{
return join.previous();
}
boolean next() throws Exception{
if(maxRows > 0 && join.getRow() > maxRows){
join.afterLast();
return false;
}
return join.next();
}
final boolean last() throws Exception{
if(maxRows > 0){
return join.absolute(maxRows);
}
return join.last();
}
final void afterLast() throws Exception{
join.afterLast();
}
boolean isLast() throws Exception{
return join.isLast();
}
boolean isAfterLast() throws SQLException{
return join.isAfterLast();
}
final boolean absolute(int row) throws Exception{
return join.absolute(row);
}
final boolean relative(int rows) throws Exception{
return join.relative(rows);
}
final int getRow() throws Exception{
int row = join.getRow();
if(maxRows > 0 && row > maxRows) return 0;
return row;
}
final void updateRow(SSConnection con, Expression[] newRowSources) throws SQLException{
int savepoint = con.getSavepoint();
try{
for(int t=0; t<from.size(); t++){
TableViewResult result = TableViewResult.getTableViewResult( from.get(t) );
TableView table = result.getTableView();
Columns tableColumns = table.columns;
int count = tableColumns.size();
Expression[] updateValues = new Expression[count];
boolean isUpdateNeeded = false;
for(int i=0; i<columnExpressions.size(); i++){
Expression src = newRowSources[i];
if(src != null && (!(src instanceof ExpressionValue) || !((ExpressionValue)src).isEmpty())){
Expression col = columnExpressions.get(i);
if(!col.isDefinitelyWritable())
throw Utils.createSQLException("Column " + i + " is read only.");
ExpressionName exp = (ExpressionName)col;
if(table == exp.getTable()){
updateValues[exp.getColumnIndex()] = src;
isUpdateNeeded = true;
continue;
}
}
}
if(isUpdateNeeded){
result.updateRow(updateValues);
}
}
}catch(Throwable e){
con.rollback(savepoint);
throw Utils.createSQLException(e);
}finally{
if(con.getAutoCommit()) con.commit();
}
}
final void insertRow(SSConnection con, Expression[] newRowSources) throws SQLException{
if(from.size() > 1)
throw Utils.createSQLException("InsertRow not supported on joins.");
if(from.size() == 0)
throw Utils.createSQLException("InsertRow need a FROM expression.");
int savepoint = con.getSavepoint();
try{
TableViewResult result = TableViewResult.getTableViewResult( from.get(0) );
TableView table = result.getTableView();
Columns tabColumns = table.columns;
int count = tabColumns.size();
Expression[] updateValues = new Expression[count];
if(newRowSources != null){
for(int i=0; i<columnExpressions.size(); i++){
Expression src = newRowSources[i];
if(src != null && (!(src instanceof ExpressionValue) || !((ExpressionValue)src).isEmpty())){
Expression rsColumn = columnExpressions.get(i); if(!rsColumn.isDefinitelyWritable())
throw Utils.createSQLException("Column " + i + " is read only.");
ExpressionName exp = (ExpressionName)rsColumn;
if(table == exp.getTable()){
updateValues[exp.getColumnIndex()] = src;
continue;
}
}
updateValues[i] = null;
}
}
result.insertRow(updateValues);
}catch(Throwable e){
con.rollback(savepoint);
throw Utils.createSQLException(e);
}finally{
if(con.getAutoCommit()) con.commit();
}
}
final void deleteRow(SSConnection con) throws SQLException{
int savepoint = con.getSavepoint();
try{
if(from.size() > 1)
throw Utils.createSQLException("DeleteRow not supported on joins.");
if(from.size() == 0)
throw Utils.createSQLException("DeleteRow need a FROM expression.");
TableViewResult.getTableViewResult( from.get(0) ).deleteRow();
}catch(Throwable e){
con.rollback(savepoint);
throw Utils.createSQLException(e);
}finally{
if(con.getAutoCommit()) con.commit();
}
}
public int findColumn(String columnName) throws SQLException {
Expressions columns = columnExpressions;
for(int i=0; i<columns.size(); i++){
if(columnName.equalsIgnoreCase(columns.get(i).getAlias()))
return i;
}
throw Utils.createSQLException("Column '"+columnName+"' not found." );
}
final void setDistinct(boolean distinct){
this.isDistinct = distinct;
}
final void setSource(RowSource join){
this.join = join;
}
final void setFrom( DataSources from ){
this.from = from;
}
final void setWhere( Expression where ){
this.where = where;
}
final void setGroup(Expressions group){
this.groupBy = group;
}
final void setHaving(Expression having){
this.having = having;
}
final void setOrder(Expressions order){
this.orderBy = order;
}
final void setMaxRows(int max){
maxRows = max;
}
}