blob: 2f9d5c50139d3d2d1e8bfe9312ad847c3e0eef8b [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Mike Norman - from Proof-of-concept, become production code
package dbws.testing.shadowddlgeneration.oldjpub;
//javase imports
import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutput;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
//EclipseLink imports
import dbws.testing.shadowddlgeneration.oldjpub.MethodFilter;
import dbws.testing.shadowddlgeneration.oldjpub.Util;
import dbws.testing.shadowddlgeneration.oldjpub.OracleTypes;
import dbws.testing.shadowddlgeneration.oldjpub.SqlName;
import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_ARGUMENTS;
@SuppressWarnings({"unchecked","rawtypes"})
public class ViewCache implements Externalizable {
private static final long serialVersionUID = 5009092516275895424L;
static final String VIEW_CACHE_PREFIX = "viewcachefor";
public static final String PARAMETER_USER = "USER";
public static final String PARAMETER_ALL = "ALL";
protected Connection m_conn;
protected String m_user;
protected Map m_rowsCacheIndex;
protected ArrayList m_rowsCache;
protected int m_hits;
protected int m_visits;
protected boolean m_viewCacheDebug = false;
@SuppressWarnings("unused")
public Iterator<ViewRow> getRows(String view, String[] columns, String[] keys, Object[] values,
String[] orderby) throws java.sql.SQLException {
m_visits++;
String cKey = makeKey(view, columns, keys, values, orderby);
ArrayList rowsV = (ArrayList)m_rowsCacheIndex.get(cKey);
ArrayList derivedRowsV = rowsV;
// NARROW DOWN QUERIES
if (derivedRowsV == null) {
for (int i = 0; i < m_rowsCache.size(); i++) {
RowsCacheEntry incoming = new RowsCacheEntry(view, columns, keys, values, null);
RowsCacheEntry cached = (RowsCacheEntry)m_rowsCache.get(i);
RowsCacheEntry diff = cached.compare(incoming);
if (diff != null) {
if (m_viewCacheDebug) {
System.out.println("viewcache.hit.query.relaxed: " + i);
}
derivedRowsV = new ArrayList();
for (int j = 0; j < diff.getRows().size(); j++) {
ViewRow row = diff.getRows().get(j);
boolean match = true;
for (int k = 0; k < diff.getKeys().length; k++) {
if ((k + 1) < diff.getKeys().length
&& diff.getKeys()[k].equals(diff.getKeys()[k + 1])) {
if (!row.equals(diff.getKeys()[k], diff.getValues()[k])
&& !row.equals(diff.getKeys()[k], diff.getValues()[k])) {
match = false;
k++;
}
}
else if (!row.equals(diff.getKeys()[k], diff.getValues()[k])) {
match = false;
}
}
if (match) {
derivedRowsV.add(row);
}
}
break;
}
else if (m_viewCacheDebug) {
System.out.println("viewcache.no.match.query: " + i + ", diff=" + diff);
}
}
}
// ORDER BY SEQUENCE
if (rowsV == null && orderby.length == 1 && orderby[0].equalsIgnoreCase(Util.SEQUENCE)
&& ViewRowFactory.hasSequence(view)) {
if (derivedRowsV == null) {
String unorderedCKey = makeKey(view, columns, keys, values, new String[0]);
derivedRowsV = (ArrayList)m_rowsCacheIndex.get(unorderedCKey);
}
if (derivedRowsV != null) {
rowsV = (ArrayList)derivedRowsV.clone();
UserArguments.orderBySequence(rowsV);
if (m_viewCacheDebug) {
System.out.println("viewcache.hit.query.unordered.sequence");
}
}
}
else
// ORDER BY POSITION
if (rowsV == null && orderby.length == 1 && orderby[0].equalsIgnoreCase("POSITION")
&& ViewRowFactory.hasPosition(view)) {
if (derivedRowsV == null) {
String unorderedCKey = makeKey(view, columns, keys, values, new String[0]);
derivedRowsV = (ArrayList)m_rowsCacheIndex.get(unorderedCKey);
}
if (derivedRowsV != null) {
rowsV = (ArrayList)derivedRowsV.clone();
UserArguments.orderByPosition(rowsV);
if (m_viewCacheDebug) {
System.out.println("viewcache.hit.query.unordered.position");
}
}
}
if (rowsV == null) {
rowsV = derivedRowsV;
}
if (rowsV == null) {
String stmtText = makeQuery(view, columns, keys, values, orderby);
ResultSet rset = null;
PreparedStatement stmt = null;
if (m_conn == null) {
throw new SQLException("ERROR: null JDBC connection.");
}
try {
stmt = m_conn.prepareStatement(stmtText);
long millis = 0;
if (m_viewCacheDebug) {
millis = System.currentTimeMillis();
System.out.println("viewcache.execute.query: " + stmtText);
}
rset = stmt.executeQuery();
rowsV = new ArrayList<ViewRow>();
while (rset.next()) {
rowsV.add(ViewRowFactory.createViewRow(view, columns, rset));
if (m_viewCacheDebug) {
System.out.print(".");
}
}
if (m_viewCacheDebug) {
System.out.println("\nviewcache.execute.query.result.set.next.elapse: "
+ (System.currentTimeMillis() - millis));
}
}
finally {
if (rset != null) {
rset.close();
}
if (stmt != null) {
stmt.close();
}
}
m_rowsCacheIndex.put(cKey, rowsV);
m_rowsCache.add(new RowsCacheEntry(view, columns, keys, values, rowsV));
}
else {
m_hits++;
if (m_viewCacheDebug) {
String stmtText = makeQuery(view, columns, keys, values, orderby);
System.out.println("viewcache.hit.query: " + stmtText);
}
}
if (rowsV == null) {
// not found in view cache and no database connection
rowsV = new ArrayList();
}
if (m_viewCacheDebug) {
System.out.println("viewcache.execute.query.result.size: " + rowsV.size());
System.out.println("viewcache.hit.rate: " + cKey + ", " + m_hits + "/" + m_visits);
}
return rowsV.iterator();
}
public Object[] getOutParameters(String stmtText, Object[] inParams, int[] types)
throws SQLException {
m_visits++;
Object[] objTypes = toObject(types);
String cKey = makeKey(stmtText, new String[0], new String[0], inParams, new String[0]);
ArrayList outParamList = (ArrayList)m_rowsCacheIndex.get(cKey);
if (outParamList == null && m_conn != null) {
// initialize parameter list
outParamList = new ArrayList();
CallableStatement stmt = m_conn.prepareCall(stmtText);
int i = 1;
for (; i < inParams.length + 1; i++) {
if (inParams[i - 1] instanceof byte[]) {
stmt.setBytes(i, (byte[])inParams[i - 1]);
}
else {
throw new SQLException("input type not supported: "
+ inParams[i - 1].getClass().getName());
}
}
for (; i < (inParams.length + types.length + 1); i++) {
stmt.registerOutParameter(i, types[i - inParams.length - 1]);
}
stmt.executeUpdate();
for (i = inParams.length; i < inParams.length + types.length; i++) {
int index = i - inParams.length;
if (types[index] == OracleTypes.INTEGER) {
outParamList.add(stmt.getInt(i + 1));
}
else if (types[index] == OracleTypes.VARCHAR) {
outParamList.add(stmt.getString(i + 1));
}
}
stmt.close();
m_rowsCacheIndex.put(cKey, outParamList);
m_rowsCache.add(new RowsCacheEntry(stmtText, new String[0], new String[0], objTypes,
outParamList));
}
Object[] outParams = null;
if (outParamList != null) {
outParams = outParamList.toArray(new Object[0]);
}
return outParams;
}
private Object[] toObject(int[] types) {
Object[] obj = new Object[types.length];
for (int i = 0; i < types.length; i++) {
obj[i] = types[i];
}
return obj;
}
private String makeKey(String view, String[] columns, String[] keys, Object[] values,
String[] orderby) {
String key = view;
if (columns.length != 0) {
key = key + "[";
for (int i = 0; i < columns.length; i++) {
key += columns[i] + (i > 0 ? ", " : "");
}
key += "]";
}
key = key + "(";
for (int i = 0; i < keys.length; i++) {
key += keys[i] + ", ";
}
key += ": ";
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof byte[]) {
byte[] bytes = (byte[])values[i];
for (int j = 0; j < bytes.length; j++) {
key += bytes[j];
}
key += ", ";
}
else {
key += values[i] + ", ";
}
}
key += ")";
for (int i = 0; i < orderby.length; i++) {
key += orderby[i] + "(o)";
}
return key.toUpperCase(); // On Windows, all keys are capitalized during deserialization
}
/*
* makeQuery( "ALL_OBJECT", new String[]{"OWNER", "OBJEC_TYPE", "OBJECT_TYPE"}, new
* String[]{"SCOTT", "PROCEDURE", "FUN%CTION"}) => WEHERE OWNER='SCOTT' AND
* (OBJECT_TYPE='PROCEDURE OR OBJECT_TYPE like 'FUN%CTION')
*/
private String makeQuery(String view, String[] columns, String[] keys, Object[] values,
String[] orderby) {
String stmt = "SELECT " + ViewRowFactory.getProject(view, columns) + " FROM " + view;
if (keys.length > 0) {
stmt += " WHERE ";
}
boolean inOR = false;
for (int i = 0; i < keys.length; i++) {
// OR
if (!inOR && i < (keys.length - 1) && keys[i].equals(keys[i + 1])) {
stmt += "(";
inOR = true;
}
stmt += keys[i];
if (values[i] == null || "".equals(values[i])) {
stmt += " IS NULL";
}
else if ("NOT NULL".equals(values[i])) {
stmt += " IS NOT NULL";
}
else if (values[i] instanceof java.lang.String) {
stmt += "='" + values[i] + "'";
}
else if ((values[i] instanceof java.lang.String)
&& ((String)values[i]).indexOf("%") > -1) {
stmt += " like '" + values[i] + "'";
}
else {
stmt += "=" + values[i];
}
if (inOR && i < keys.length - 1 && keys[i].equals(keys[i + 1])) {
stmt += " OR ";
}
else if (inOR && i < keys.length - 1) {
stmt += ") AND ";
inOR = false;
}
else if (inOR) {
stmt += ")";
inOR = false;
}
else if (i < keys.length - 1) {
stmt += " AND ";
}
}
for (int i = 0; i < orderby.length; i++) {
if (i == 0) {
stmt += " ORDER BY ";
}
stmt += orderby[i];
if (i != (orderby.length - 1)) {
stmt += ", ";
}
}
return stmt;
}
public ViewCache() {
}
public ViewCache(Connection conn, String user) {
m_conn = conn;
m_user = user;
m_rowsCacheIndex = new HashMap();
m_rowsCache = new ArrayList();
m_hits = 0;
m_visits = 0;
}
/*
* initialize the cache using queries of the viewcache passed in
*/
public void init(ViewCache viewCache) {
int len = m_rowsCache.size();
for (int i = 0; i < len; i++) {
RowsCacheEntry entry = (RowsCacheEntry)m_rowsCache.get(i);
try {
getRows(entry);
}
catch (Exception e) {
System.err.println("WARNING: error in refreshing view cache for view "
+ entry.getView());
}
}
}
/*
* refresh the cache
*/
public void refresh() {
int len = m_rowsCache.size();
for (int i = 0; i < len; i++) {
RowsCacheEntry entry = (RowsCacheEntry)m_rowsCache.get(i);
try {
getRows(entry);
}
catch (Exception e) {
System.err.println("WARNING: error in refreshing view cache for view "
+ entry.getView());
}
}
}
private Iterator getRows(RowsCacheEntry entry) throws SQLException {
return getRows(entry.getView(), entry.getSelects(), entry.getKeys(), entry.getValues(),
new String[0]);
}
public void fetch(String packageName, MethodFilter sigf) throws SQLException {
if (m_viewCacheDebug) {
System.out.println("viewcache.fetch: " + packageName);
}
packageName = dbifyName(packageName);
if (PARAMETER_ALL.equalsIgnoreCase(packageName)
|| PARAMETER_USER.equalsIgnoreCase(packageName)) {
getRows(ALL_ARGUMENTS, new String[0], new String[]{}, new Object[]{}, new String[0]);
}
else {
String[] keys = null;
Object[] values = null;
if (sigf != null && sigf.isSingleMethod()) {
keys = new String[]{Util.PACKAGE_NAME, Util.OBJECT_NAME};
values = new Object[]{packageName, sigf.getSingleMethodName()};
}
else {
keys = new String[]{Util.PACKAGE_NAME};
values = new Object[]{packageName};
}
getRows(ALL_ARGUMENTS, new String[0], keys, values, new String[0]);
}
}
public int getHits() {
return m_hits;
}
public int getVisits() {
return m_visits;
}
public void reset(Connection conn) {
m_conn = conn;
}
public void close() {
try {
if (m_conn != null) {
m_conn.close();
}
}
catch (Exception e) {
// Closing resources. OK to ignore exception.
}
}
public String getUser() {
return m_user;
}
public String getFileName(String dir) {
return getFileName(dir, m_user);
}
public static String getFileName(String dir, String user) {
return (dir == null ? "" : dir + File.separator) + VIEW_CACHE_PREFIX + user.toLowerCase();
}
@Override
public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException {
if (m_viewCacheDebug) {
System.out.println("viewcache.read.external");
}
// summary
m_user = (String)in.readObject();
m_hits = (Integer) in.readObject();
m_visits = (Integer) in.readObject();
// m_rowsCache
int rowsCacheSize = (Integer) in.readObject();
m_rowsCache = new ArrayList(rowsCacheSize);
for (int i = 0; i < rowsCacheSize; i++) {
RowsCacheEntry rce = (RowsCacheEntry)in.readObject();
m_rowsCache.add(rce);
}
// m_rowsCacheIndex (String, ArrayList<ViewRow>)
int rowsCacheIndexSize = (Integer) in.readObject();
m_rowsCacheIndex = new HashMap(rowsCacheIndexSize);
for (int i = 0; i < rowsCacheIndexSize; i++) {
String key = (String)in.readObject();
int rowsSize = (Integer) in.readObject();
ArrayList rows = new ArrayList(rowsSize);
for (int j = 0; j < rowsSize; j++) {
ViewRow row = (ViewRow)in.readObject();
rows.add(row);
}
m_rowsCacheIndex.put(key, rows);
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
if (m_viewCacheDebug) {
System.out.println("viewcache.write.external");
}
// summary
out.writeObject(m_user);
out.writeObject(m_hits);
out.writeObject(m_visits);
// m_rowsCache
out.writeObject(m_rowsCache.size());
for (int i = 0; i < m_rowsCache.size(); i++) {
RowsCacheEntry rce = (RowsCacheEntry)m_rowsCache.get(i);
out.writeObject(rce);
}
// m_rowsCacheIndex (String, ArrayList<ViewRow>)
out.writeObject(m_rowsCacheIndex.size());
Iterator keys = m_rowsCacheIndex.keySet().iterator();
Iterator values = m_rowsCacheIndex.values().iterator();
while (keys.hasNext()) {
out.writeObject(keys.next());
ArrayList rows = (ArrayList)values.next();
out.writeObject(rows.size());
for (int i = 0; i < rows.size(); i++) {
out.writeObject(rows.get(i));
}
}
}
public String printSummary() {
// summary
String text = "";
text += "****" + getFileName(null) + "****" + "\n";
text += "schema: " + m_user + "\n";
text += "hits/visits: " + m_hits + "/" + m_visits + "\n";
for (int i = 0; i < m_rowsCache.size(); i++) {
RowsCacheEntry rce = (RowsCacheEntry)m_rowsCache.get(i);
text += rce.printSummary();
}
return text;
}
/*
* if the name is quoted, the quotes are removed.
*
* public static String dbifyName(String s, SqlReflector reflector) { if (s == null ||
* s.equals("") || reflector == null || reflector.getConnection() == null) { return s; } return
* dbifyName(s, reflector.getViewCache()); }
*/
public String dbifyName(String s) {
if (s == null) {
return "";
}
else if (s.equals("")) {
return "";
}
else if (SqlName.isQuoted(s)) {
return s.substring(1, s.length() - 1);
}
String upper_s = s.toUpperCase();
String dbName = "";
try {
Iterator rowIter = getRows(Util.DUAL, new String[]{"UPPER('" + s + "') AS UPPER_NAME"},
new String[0], new Object[0], new String[0]);
if (rowIter.hasNext()) {
SingleColumnViewRow row = (SingleColumnViewRow)rowIter.next();
dbName = row.getValue();
}
else {
// not found in viewCache and no database connection
dbName = upper_s;
}
}
catch (Exception ex) {
System.err.println(ex.getMessage());
dbName = upper_s;
}
return dbName;
}
}