blob: 7cba532898a2d45103e29be90ea6db29de4ec12b [file] [log] [blame]
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gwtorm.jdbc;
import com.google.gwtorm.client.Key;
import com.google.gwtorm.schema.ColumnModel;
import com.google.gwtorm.schema.KeyModel;
import com.google.gwtorm.schema.QueryModel;
import com.google.gwtorm.schema.RelationModel;
import com.google.gwtorm.schema.Util;
import com.google.gwtorm.schema.sql.SqlDialect;
import com.google.gwtorm.server.Access;
import com.google.gwtorm.server.CodeGenSupport;
import com.google.gwtorm.server.GeneratedClassLoader;
import com.google.gwtorm.server.OrmException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/** Generates a concrete implementation of an {@link Access} extension. */
class AccessGen implements Opcodes {
private static final String REL_ALIAS = "T";
private static enum DmlType {
INSERT("bindOneInsert"),
UPDATE("bindOneUpdate"),
DELETE("bindOneDelete");
final String methodName;
DmlType(final String m) {
methodName = m;
}
}
private final GeneratedClassLoader classLoader;
private final RelationModel model;
private final SqlDialect dialect;
private ClassWriter cw;
private String superTypeName;
private String implClassName;
private String implTypeName;
private Type entityType;
AccessGen(final GeneratedClassLoader loader,
final RelationModel rm, final SqlDialect sd) {
classLoader = loader;
model = rm;
dialect = sd;
entityType =
Type.getObjectType(model.getEntityTypeClassName().replace('.', '/'));
}
public Class<?> create() throws OrmException {
init();
implementConstructor();
implementGetString("getRelationName", model.getRelationName());
implementGetString("getInsertOneSql", model.getInsertOneSql(dialect));
implementGetRelationID();
if (model.getPrimaryKey() != null) {
if (model.getDependentColumns().isEmpty()) {
implementMissingGetString("getUpdateOneSql", "update");
} else {
implementGetString("getUpdateOneSql", model.getUpdateOneSql(dialect));
}
implementGetString("getDeleteOneSql", model.getDeleteOneSql(dialect));
} else {
implementMissingGetString("getUpdateOneSql", "update");
implementMissingGetString("getDeleteOneSql", "delete");
}
implementPrimaryKey();
implementGetOne();
implementNewEntityInstance();
implementBindOne(DmlType.INSERT);
implementBindOne(DmlType.UPDATE);
implementBindOne(DmlType.DELETE);
implementBindOneFetch();
if (model.getPrimaryKey() != null) {
implementKeyQuery(model.getPrimaryKey());
if ((model.getPrimaryKey().getField().isNested() || !model
.getPrimaryKey().getField().getPrimitiveType().isPrimitive())
&& model.getPrimaryKey().getAllLeafColumns().size() == 1) {
overrideGetMany();
}
}
for (final QueryModel q : model.getQueries()) {
implementQuery(q);
}
implementQuery(new QueryModel(model, "iterateAllEntities", ""));
cw.visitEnd();
classLoader.defineClass(implClassName, cw.toByteArray());
return loadClass();
}
private Class<?> loadClass() throws OrmException {
try {
return Class.forName(implClassName, false, classLoader);
} catch (ClassNotFoundException err) {
throw new OrmException("Cannot load generated class", err);
}
}
private void init() {
superTypeName = Type.getInternalName(JdbcAccess.class);
implClassName =
model.getEntityTypeClassName() + "_Access_" + model.getMethodName()
+ "_" + Util.createRandomName();
implTypeName = implClassName.replace('.', '/');
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_3, ACC_PUBLIC | ACC_FINAL | ACC_SUPER, implTypeName, null,
superTypeName, new String[] {model.getAccessInterfaceName().replace(
'.', '/')});
}
private void implementConstructor() {
final String consName = "<init>";
final String consDesc =
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type
.getType(JdbcSchema.class)});
final MethodVisitor mv;
mv = cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, superTypeName, consName, consDesc);
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementGetString(final String methodName,
final String returnValue) {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, methodName, Type
.getMethodDescriptor(Type.getType(String.class), new Type[] {}),
null, null);
mv.visitCode();
mv.visitLdcInsn(returnValue);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementGetRelationID() {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "getRelationID", Type
.getMethodDescriptor(Type.INT_TYPE, new Type[] {}), null, null);
mv.visitCode();
new CodeGenSupport(mv).push(model.getRelationID());
mv.visitInsn(IRETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementMissingGetString(final String methodName,
final String why) {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, methodName, Type
.getMethodDescriptor(Type.getType(String.class), new Type[] {}),
null, null);
mv.visitCode();
throwUnsupported(mv, model.getMethodName() + " does not support " + why);
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void throwUnsupported(final MethodVisitor mv, final String message) {
final Type eType = Type.getType(UnsupportedOperationException.class);
mv.visitTypeInsn(NEW, eType.getInternalName());
mv.visitInsn(DUP);
mv.visitLdcInsn(message);
mv.visitMethodInsn(INVOKESPECIAL, eType.getInternalName(), "<init>", Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type
.getType(String.class)}));
mv.visitInsn(ATHROW);
}
private void implementPrimaryKey() {
final KeyModel pk = model.getPrimaryKey();
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "primaryKey", Type
.getMethodDescriptor(Type.getType(Key.class), new Type[] {Type
.getType(Object.class)}), null, null);
mv.visitCode();
if (pk != null && pk.getField().isNested()) {
final ColumnModel pkf = pk.getField();
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
mv.visitFieldInsn(GETFIELD, entityType.getInternalName(), pkf
.getFieldName(), CodeGenSupport.toType(pkf).getDescriptor());
} else {
mv.visitInsn(ACONST_NULL);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementGetOne() {
final KeyModel pk = model.getPrimaryKey();
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "get", Type.getMethodDescriptor(
Type.getType(Object.class), new Type[] {Type.getType(Key.class)}),
null, new String[] {Type.getType(OrmException.class)
.getInternalName()});
mv.visitCode();
if (pk != null && pk.getField().isNested()) {
final Type keyType = CodeGenSupport.toType(pk.getField());
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, keyType.getInternalName());
mv.visitMethodInsn(INVOKEVIRTUAL, implTypeName, pk.getName(), Type
.getMethodDescriptor(entityType, new Type[] {keyType}));
mv.visitInsn(ARETURN);
} else {
throwUnsupported(mv, model.getMethodName() + " does not support get(Key)");
}
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementNewEntityInstance() {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "newEntityInstance", Type
.getMethodDescriptor(Type.getType(Object.class), new Type[] {}),
null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, entityType.getInternalName());
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, entityType.getInternalName(), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementBindOne(final DmlType type) {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, type.methodName, Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
Type.getType(PreparedStatement.class),
Type.getType(Object.class)}), null, new String[] {Type.getType(
SQLException.class).getInternalName()});
mv.visitCode();
if (type != DmlType.INSERT && model.getPrimaryKey() == null) {
throwUnsupported(mv, model.getMethodName() + " has no primary key");
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
mv.visitVarInsn(ASTORE, 2);
final CodeGenSupport cgs = new CodeGenSupport(mv);
cgs.setEntityType(entityType);
for (final ColumnModel col : model.getRowVersionColumns()) {
cgs.setFieldReference(col);
cgs.fieldSetBegin();
cgs.pushFieldValue();
mv.visitInsn(ICONST_1);
mv.visitInsn(IADD);
cgs.fieldSetEnd();
}
cgs.resetColumnIndex(0);
if (type != DmlType.DELETE) {
final List<ColumnModel> cols = new ArrayList<ColumnModel>();
cols.addAll(model.getDependentFields());
cols.addAll(model.getRowVersionFields());
for (final ColumnModel field : cols) {
doBindOne(mv, cgs, field);
}
}
for (final ColumnModel col : model.getPrimaryKeyColumns()) {
cgs.setFieldReference(col);
dialect.getSqlTypeInfo(col).generatePreparedStatementSet(cgs);
}
if (type != DmlType.INSERT) {
for (final ColumnModel col : model.getRowVersionColumns()) {
cgs.setFieldReference(col);
cgs.pushSqlHandle();
cgs.pushColumnIndex();
cgs.pushFieldValue();
mv.visitInsn(ICONST_1);
mv.visitInsn(ISUB);
cgs.invokePreparedStatementSet("Int");
}
}
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void doBindOne(final MethodVisitor mv, final CodeGenSupport cgs,
final ColumnModel field) {
if (field.isNested() && field.isNotNull()) {
for (final ColumnModel c : field.getAllLeafColumns()) {
doBindOne(mv, cgs, c);
}
} else if (field.isNested()) {
final int colIdx = cgs.getColumnIndex();
final Label isnull = new Label();
final Label end = new Label();
cgs.setFieldReference(field);
cgs.pushFieldValue();
mv.visitJumpInsn(IFNULL, isnull);
cgs.resetColumnIndex(colIdx);
for (final ColumnModel c : field.getNestedColumns()) {
doBindOne(mv, cgs, c);
}
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(isnull);
cgs.resetColumnIndex(colIdx);
for (final ColumnModel c : field.getAllLeafColumns()) {
cgs.setFieldReference(c);
dialect.getSqlTypeInfo(c).generatePreparedStatementNull(cgs);
}
mv.visitLabel(end);
} else {
cgs.setFieldReference(field);
dialect.getSqlTypeInfo(field).generatePreparedStatementSet(cgs);
}
}
private void implementBindOneFetch() {
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "bindOneFetch", Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
Type.getType(ResultSet.class), Type.getType(Object.class)}),
null, new String[] {Type.getType(SQLException.class)
.getInternalName()});
mv.visitCode();
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
mv.visitVarInsn(ASTORE, 2);
final CodeGenSupport cgs = new CodeGenSupport(mv);
cgs.setEntityType(entityType);
if (model.getPrimaryKey() != null
&& model.getPrimaryKey().getField().isNested()) {
final ColumnModel pkf = model.getPrimaryKey().getField();
final Type vType = CodeGenSupport.toType(pkf);
final int oldIdx = cgs.getColumnIndex();
cgs.setFieldReference(pkf);
cgs.fieldSetBegin();
mv.visitTypeInsn(NEW, vType.getInternalName());
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, vType.getInternalName(), "<init>", Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
cgs.fieldSetEnd();
cgs.resetColumnIndex(oldIdx);
}
final List<ColumnModel> cols = new ArrayList<ColumnModel>();
cols.addAll(model.getDependentFields());
cols.addAll(model.getRowVersionFields());
cols.addAll(model.getPrimaryKeyColumns());
for (final ColumnModel field : cols) {
doFetchOne(mv, cgs, field, -1);
}
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void doFetchOne(final MethodVisitor mv, final CodeGenSupport cgs,
final ColumnModel field, final int reportLiveInto) {
if (field.isNested()) {
int oldIdx = cgs.getColumnIndex();
final Type vType = CodeGenSupport.toType(field);
final int livecnt;
if (field.isNotNull()) {
livecnt = -1;
} else {
livecnt = cgs.newLocal();
cgs.push(0);
mv.visitVarInsn(ISTORE, livecnt);
}
cgs.setFieldReference(field);
cgs.fieldSetBegin();
mv.visitTypeInsn(NEW, vType.getInternalName());
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, vType.getInternalName(), "<init>", Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
cgs.fieldSetEnd();
cgs.resetColumnIndex(oldIdx);
for (final ColumnModel c : field.getNestedColumns()) {
doFetchOne(mv, cgs, c, livecnt);
}
if (livecnt >= 0) {
oldIdx = cgs.getColumnIndex();
final Label islive = new Label();
mv.visitVarInsn(ILOAD, livecnt);
mv.visitJumpInsn(IFNE, islive);
cgs.setFieldReference(field);
cgs.fieldSetBegin();
mv.visitInsn(ACONST_NULL);
cgs.fieldSetEnd();
if (reportLiveInto >= 0) {
final Label end = new Label();
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(islive);
mv.visitIincInsn(reportLiveInto, 1);
mv.visitLabel(end);
} else {
mv.visitLabel(islive);
}
cgs.resetColumnIndex(oldIdx);
cgs.freeLocal(livecnt);
}
} else {
final int dupTo;
if (reportLiveInto >= 0
&& CodeGenSupport.toType(field).getSort() == Type.OBJECT) {
dupTo = cgs.newLocal();
} else {
dupTo = -1;
}
cgs.setFieldReference(field);
cgs.setDupOnFieldSetEnd(dupTo);
dialect.getSqlTypeInfo(field).generateResultSetGet(cgs);
if (reportLiveInto >= 0) {
final Label wasnull = new Label();
if (dupTo >= 0) {
mv.visitVarInsn(ALOAD, dupTo);
mv.visitJumpInsn(IFNULL, wasnull);
cgs.freeLocal(dupTo);
} else {
cgs.pushSqlHandle();
mv.visitMethodInsn(INVOKEINTERFACE, Type.getType(ResultSet.class)
.getInternalName(), "wasNull", Type.getMethodDescriptor(
Type.BOOLEAN_TYPE, new Type[] {}));
mv.visitJumpInsn(IFNE, wasnull);
}
mv.visitIincInsn(reportLiveInto, 1);
mv.visitLabel(wasnull);
}
}
}
private void implementKeyQuery(final KeyModel info) {
final Type keyType = CodeGenSupport.toType(info.getField());
final StringBuilder query = new StringBuilder();
query.append(model.getSelectSql(dialect, REL_ALIAS));
query.append(" WHERE ");
int nth = 1;
for (final Iterator<ColumnModel> i = info.getAllLeafColumns().iterator(); i
.hasNext();) {
final ColumnModel c = i.next();
query.append(REL_ALIAS);
query.append('.');
query.append(c.getColumnName());
query.append('=');
query.append(dialect.getParameterPlaceHolder(nth++));
if (i.hasNext()) {
query.append(" AND ");
}
}
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, info.getName(), Type
.getMethodDescriptor(entityType, new Type[] {keyType}), null,
new String[] {Type.getType(OrmException.class).getInternalName()});
mv.visitCode();
final int keyvar = 1, psvar = keyvar + keyType.getSize();
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(query.toString());
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "prepareStatement", Type
.getMethodDescriptor(Type.getType(PreparedStatement.class),
new Type[] {Type.getType(String.class)}));
mv.visitVarInsn(ASTORE, psvar);
final CodeGenSupport cgs = new CodeGenSupport(mv) {
@Override
public void pushSqlHandle() {
mv.visitVarInsn(ALOAD, psvar);
}
@Override
public void pushFieldValue() {
appendGetField(getFieldReference());
}
@Override
protected void appendGetField(final ColumnModel c) {
if (c.getParent() == null) {
loadVar(keyType, keyvar);
} else {
super.appendGetField(c);
}
}
};
for (final ColumnModel c : info.getAllLeafColumns()) {
cgs.setFieldReference(c);
dialect.getSqlTypeInfo(c).generatePreparedStatementSet(cgs);
}
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, psvar);
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "queryOne", Type
.getMethodDescriptor(Type.getType(Object.class), new Type[] {Type
.getType(PreparedStatement.class)}));
mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void overrideGetMany() {
final KeyModel pk = model.getPrimaryKey();
final StringBuilder query = new StringBuilder();
query.append(model.getSelectSql(dialect, REL_ALIAS));
query.append(" WHERE ");
final ColumnModel pkcol = pk.getAllLeafColumns().iterator().next();
query.append(REL_ALIAS);
query.append('.');
query.append(pkcol.getColumnName());
query.append(" IN");
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "getBySqlIn", Type
.getMethodDescriptor(Type
.getType(com.google.gwtorm.server.ResultSet.class),
new Type[] {Type.getType(Collection.class)}), null,
new String[] {Type.getType(OrmException.class).getInternalName()});
mv.visitCode();
final int keyset = 1;
final int psvar = 2;
final int itrvar = 3;
final int colvar = 4;
final int keyvar = 5;
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(query.toString());
mv.visitVarInsn(ALOAD, keyset);
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "prepareBySqlIn", Type
.getMethodDescriptor(Type.getType(PreparedStatement.class), new Type[] {
Type.getType(String.class), Type.getType(Collection.class)}));
mv.visitVarInsn(ASTORE, psvar);
mv.visitVarInsn(ALOAD, keyset);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Collection.class),
"iterator", Type.getMethodDescriptor(Type.getType(Iterator.class),
new Type[] {}));
mv.visitVarInsn(ASTORE, itrvar);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ISTORE, colvar);
final Label endbind = new Label();
final Label again = new Label();
mv.visitLabel(again);
mv.visitVarInsn(ALOAD, itrvar);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Iterator.class),
"hasNext", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] {}));
mv.visitJumpInsn(IFEQ, endbind);
mv.visitVarInsn(ALOAD, itrvar);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Iterator.class),
"next", Type.getMethodDescriptor(Type.getType(Object.class),
new Type[] {}));
mv.visitTypeInsn(CHECKCAST, CodeGenSupport.toType(pk.getField())
.getInternalName());
mv.visitVarInsn(ASTORE, keyvar);
final CodeGenSupport cgs = new CodeGenSupport(mv) {
@Override
public void pushSqlHandle() {
mv.visitVarInsn(ALOAD, psvar);
}
@Override
public void pushFieldValue() {
appendGetField(getFieldReference());
}
@Override
public void pushColumnIndex() {
mv.visitVarInsn(ILOAD, colvar);
}
@Override
protected void appendGetField(final ColumnModel c) {
if (c.getParent() == null) {
mv.visitVarInsn(ALOAD, keyvar);
} else {
super.appendGetField(c);
}
}
};
cgs.setFieldReference(pkcol);
dialect.getSqlTypeInfo(pkcol).generatePreparedStatementSet(cgs);
mv.visitIincInsn(colvar, 1);
mv.visitJumpInsn(GOTO, again);
mv.visitLabel(endbind);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, psvar);
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "queryList", Type
.getMethodDescriptor(Type.getType(com.google.gwtorm.server.ResultSet.class),
new Type[] {Type.getType(PreparedStatement.class)}));
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementQuery(final QueryModel info) {
final List<ColumnModel> pCols = info.getParameters();
final boolean hasLimitParam = info.hasLimitParameter();
final Type[] pTypes = new Type[pCols.size() + (hasLimitParam ? 1 : 0)];
final int[] pVars = new int[pTypes.length];
int nextVar = 1;
for (int i = 0; i < pCols.size(); i++) {
pTypes[i] = CodeGenSupport.toType(pCols.get(i));
pVars[i] = nextVar;
nextVar += pTypes[i].getSize();
}
if (hasLimitParam) {
pTypes[pTypes.length - 1] = Type.INT_TYPE;
pVars[pTypes.length - 1] = nextVar;
nextVar += Type.INT_TYPE.getSize();
}
final int psvar = nextVar++;
final MethodVisitor mv =
cw.visitMethod(ACC_PUBLIC | ACC_FINAL, info.getName(), Type
.getMethodDescriptor(Type
.getType(com.google.gwtorm.server.ResultSet.class), pTypes),
null, new String[] {Type.getType(OrmException.class)
.getInternalName()});
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(info.getSelectSql(dialect, REL_ALIAS));
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "prepareStatement", Type
.getMethodDescriptor(Type.getType(PreparedStatement.class),
new Type[] {Type.getType(String.class)}));
mv.visitVarInsn(ASTORE, psvar);
final int argIdx[] = new int[] {0};
final CodeGenSupport cgs = new CodeGenSupport(mv) {
@Override
public void pushSqlHandle() {
mv.visitVarInsn(ALOAD, psvar);
}
@Override
public void pushFieldValue() {
appendGetField(getFieldReference());
}
@Override
protected void appendGetField(final ColumnModel c) {
final int n = argIdx[0];
if (c == pCols.get(n)) {
loadVar(pTypes[n], pVars[n]);
} else {
super.appendGetField(c);
}
}
};
for (final ColumnModel c : pCols) {
if (c.isNested()) {
for (final ColumnModel n : c.getAllLeafColumns()) {
cgs.setFieldReference(n);
dialect.getSqlTypeInfo(n).generatePreparedStatementSet(cgs);
}
} else {
cgs.setFieldReference(c);
dialect.getSqlTypeInfo(c).generatePreparedStatementSet(cgs);
}
argIdx[0]++;
}
if (info.hasLimit()) {
if (hasLimitParam || !dialect.selectHasLimit()) {
mv.visitVarInsn(ALOAD, psvar);
if (hasLimitParam) {
CodeGenSupport cgs2 = new CodeGenSupport(mv) {
@Override
public void pushSqlHandle() {
mv.visitVarInsn(ALOAD, psvar);
}
@Override
public void pushFieldValue() {
final int n = argIdx[0];
loadVar(pTypes[n], pVars[n]);
}
};
cgs2.resetColumnIndex(cgs.getColumnIndex() + 1);
dialect.getSqlTypeInfo(Integer.TYPE).generatePreparedStatementSet(cgs2);
mv.visitVarInsn(ILOAD, pVars[pTypes.length - 1]);
} else {
cgs.push(info.getStaticLimit());
}
mv.visitMethodInsn(INVOKEINTERFACE, Type.getType(
PreparedStatement.class).getInternalName(), "setMaxRows", Type
.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE}));
}
}
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, psvar);
mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "queryList", Type
.getMethodDescriptor(Type.getType(com.google.gwtorm.server.ResultSet.class),
new Type[] {Type.getType(PreparedStatement.class)}));
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}