001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2013  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.sql;
019
020import java.sql.SQLException;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.TreeSet;
028import java.util.concurrent.locks.Lock;
029
030import net.sf.hajdbc.Database;
031import net.sf.hajdbc.DatabaseCluster;
032import net.sf.hajdbc.DatabaseProperties;
033import net.sf.hajdbc.IdentityColumnSupport;
034import net.sf.hajdbc.Messages;
035import net.sf.hajdbc.SequenceSupport;
036import net.sf.hajdbc.TableProperties;
037import net.sf.hajdbc.dialect.Dialect;
038import net.sf.hajdbc.invocation.Invoker;
039import net.sf.hajdbc.lock.LockManager;
040
041/**
042 * 
043 * @author Paul Ferraro
044 */
045public abstract class AbstractSQLProxyFactory<Z, D extends Database<Z>, P, T> extends AbstractTransactionalProxyFactory<Z, D, P, T> implements SQLProxyFactory<Z, D, P, T>
046{
047        protected AbstractSQLProxyFactory(P parent, ProxyFactory<Z, D, P, SQLException> parentMap, Invoker<Z, D, P, T, SQLException> invoker, Map<D, T> map, TransactionContext<Z, D> context)
048        {
049                super(parent, parentMap, invoker, map, context);
050        }
051
052        @Override
053        public String evaluate(final String rawSQL)
054        {
055                String sql = rawSQL;
056                
057                long now = System.currentTimeMillis();
058                
059                DatabaseCluster<Z, D> cluster = this.getDatabaseCluster();
060                Dialect dialect = cluster.getDialect();
061                
062                if (cluster.isCurrentTimestampEvaluationEnabled())
063                {
064                        sql = dialect.evaluateCurrentTimestamp(sql, new java.sql.Timestamp(now));
065                }
066                
067                if (cluster.isCurrentDateEvaluationEnabled())
068                {
069                        sql = dialect.evaluateCurrentDate(sql, new java.sql.Date(now));
070                }
071                
072                if (cluster.isCurrentTimeEvaluationEnabled())
073                {
074                        sql = dialect.evaluateCurrentTime(sql, new java.sql.Time(now));
075                }
076                
077                if (cluster.isRandEvaluationEnabled())
078                {
079                        sql = dialect.evaluateRand(sql);
080                }
081                
082                return sql;
083        }
084        
085        @Override
086        public List<Lock> extractLocks(String sql) throws SQLException
087        {
088                return this.extractLocks(Collections.singleton(sql));
089        }
090        
091        protected List<Lock> extractLocks(Collection<String> statements) throws SQLException
092        {
093                Set<String> identifierSet = new TreeSet<String>();
094                DatabaseCluster<Z, D> cluster = this.getDatabaseCluster();
095                
096                for (String sql: statements)
097                {
098                        if (cluster.isSequenceDetectionEnabled())
099                        {
100                                SequenceSupport support = cluster.getDialect().getSequenceSupport();
101                                
102                                if (support != null)
103                                {
104                                        String sequence = support.parseSequence(sql);
105                                        
106                                        if (sequence != null)
107                                        {
108                                                identifierSet.add(sequence);
109                                        }
110                                }
111                        }
112                        
113                        if (cluster.isIdentityColumnDetectionEnabled())
114                        {
115                                IdentityColumnSupport support = cluster.getDialect().getIdentityColumnSupport();
116                                
117                                if (support != null)
118                                {
119                                        String table = support.parseInsertTable(sql);
120                                        
121                                        if (table != null)
122                                        {
123                                                TableProperties tableProperties = this.getDatabaseProperties().findTable(table);
124                                                
125                                                if (tableProperties == null)
126                                                {
127                                                        throw new SQLException(Messages.SCHEMA_LOOKUP_FAILED.getMessage(table, cluster, cluster.getDialect().getClass().getName() + ".getDefaultSchemas()"));
128                                                }
129                                                
130                                                if (!tableProperties.getIdentityColumns().isEmpty())
131                                                {
132                                                        identifierSet.add(tableProperties.getName().getDMLName());
133                                                }
134                                        }
135                                }
136                        }
137                }
138                
139                List<Lock> lockList = new ArrayList<Lock>(identifierSet.size());
140                
141                if (!identifierSet.isEmpty())
142                {
143                        LockManager lockManager = cluster.getLockManager();
144                        
145                        for (String identifier: identifierSet)
146                        {
147                                lockList.add(lockManager.writeLock(identifier));
148                        }
149                }
150                
151                return lockList;
152        }
153
154        private DatabaseProperties getDatabaseProperties() throws SQLException
155        {
156                DatabaseCluster<Z, D> cluster = this.getDatabaseCluster();
157                D database = cluster.getBalancer().primary();
158                return cluster.getDatabaseMetaDataCache().getDatabaseProperties(database, this.getConnection(database));
159        }
160        
161        @Override
162        public boolean isSelectForUpdate(String sql) throws SQLException
163        {
164                return this.getDatabaseProperties().supportsSelectForUpdate() ? this.getDatabaseCluster().getDialect().isSelectForUpdate(sql) : false;
165        }
166
167        @Override
168        public boolean locatorsUpdateCopy() throws SQLException
169        {
170                return this.getDatabaseProperties().locatorsUpdateCopy();
171        }
172}