KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > metadata > JdbcLinkCollectionField


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.metadata;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.common.*;
16 import com.versant.core.metadata.*;
17 import com.versant.core.metadata.parser.JdoElement;
18 import com.versant.core.metadata.parser.JdoExtension;
19 import com.versant.core.metadata.parser.JdoExtensionKeys;
20 import com.versant.core.jdo.query.*;
21 import com.versant.core.server.StateContainer;
22 import com.versant.core.server.PersistGraph;
23 import com.versant.core.jdbc.*;
24 import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
25 import com.versant.core.jdbc.sql.JdbcNameGenerator;
26 import com.versant.core.jdbc.sql.SqlDriver;
27 import com.versant.core.jdbc.sql.exp.*;
28 import com.versant.core.util.CharBuf;
29
30 import java.io.PrintStream JavaDoc;
31 import java.sql.Connection JavaDoc;
32 import java.sql.PreparedStatement JavaDoc;
33 import java.sql.ResultSet JavaDoc;
34 import java.sql.SQLException JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.List JavaDoc;
38 import java.lang.reflect.Array JavaDoc;
39
40 /**
41  * A field that is a Collection, Map or array stored in a link table.
42  */

43 public class JdbcLinkCollectionField extends JdbcCollectionField {
44
45     /**
46      * If this field is in a many-to-many relationship then this is the
47      * other side.
48      */

49     public JdbcLinkCollectionField inverse;
50     /**
51      * Is this the read-only half of a many-to-many?
52      */

53     public boolean readOnly;
54     /**
55      * The link table.
56      */

57     public JdbcTable linkTable;
58     /**
59      * The column(s) holding the values. This array will have length 1
60      * unless the values are of a PC class with a composite primary key.
61      */

62     public JdbcColumn[] valueColumns;
63     /**
64      * Are the values OID's?
65      */

66     public boolean valuesAreOIDs;
67
68     private transient boolean createValueConstraint;
69     private transient String JavaDoc valueConstraintName;
70     private transient boolean doNotCreateTable;
71
72     // Cache for SQL to delete a value from the collection.
73
private transient String JavaDoc deleteRowSql;
74     // Cache for SQL to delete all values from the collection.
75
private transient String JavaDoc deleteAllRowsSql;
76     // Cache for SQL to insert a value into the collection.
77
private transient String JavaDoc insertRowSql;
78
79     // Size of the moving window used to calculate avgRowCount.
80
private static final int WINDOW_SIZE = 20;
81     // The initial array size is avgRowCount multiplied by this.
82
private static final float FUDGE_FACTOR = 1.5f;
83     // This is the minimum initial array size.
84
private static final int MIN_LEN = 4;
85
86     // Total number of fetches done so far.
87
protected transient int fetchCount;
88     // Average number of rows retrieved with each fetch.
89
protected transient float avgRowCount;
90     // Total number of times the values array for a fetch has had to be
91
// expanded (i.e. we guessed the size incorrectly. This is the number
92
// of extra objects and array copies we have had to do.
93
protected transient int expansionCount;
94
95     /**
96      * Add all tables that belong to this field to the set.
97      */

98     public void getTables(HashSet JavaDoc tables) {
99         if (!readOnly && !doNotCreateTable && linkTable != null) {
100             tables.add(linkTable);
101         }
102     }
103
104     public void dump(PrintStream JavaDoc out, String JavaDoc indent) {
105         super.dump(out, indent);
106         String JavaDoc is = indent + " ";
107         out.println(is + "inverse " + inverse);
108         out.println(is + "readOnly " + readOnly);
109         out.println(is + "valuesAreOIDs " + valuesAreOIDs);
110         out.println(is + "linkTable " + linkTable);
111         if (valueColumns == null) {
112             out.println(is + "valueColumns null");
113         } else {
114             for (int i = 0; i < valueColumns.length; i++) {
115                 out.println(is + "valueColumns[" + i + "] " + valueColumns[i]);
116             }
117         }
118     }
119
120     /**
121      * Complete the meta data for this collection. This must use info
122      * already supplied in the .jdo file and add anything else needed.
123      */

124     public void processMetaData(JdoElement context, JdbcMetaDataBuilder mdb,
125             boolean quiet) {
126         super.processMetaData(context, mdb, quiet);
127
128         JdoExtension[] extensions = getExtensions();
129
130         // see if this is the inverse side of a many-to-many
131
JdoExtension ext = JdoExtension.find(JdoExtensionKeys.INVERSE,
132                 extensions);
133         if (ext != null) {
134             processInverse(mdb, ext);
135             return;
136         }
137
138         valuesAreOIDs = fmd.elementTypeMetaData != null;
139         if (valuesAreOIDs) {
140             if (fmd.ordered || allowNulls()) {
141                 useJoin = JdbcField.USE_JOIN_OUTER;
142             } else {
143                 useJoin = JdbcField.USE_JOIN_INNER;
144             }
145         } else {
146             useJoin = JdbcField.USE_JOIN_NO;
147         }
148
149         ext = JdoExtension.find(JdoExtensionKeys.MANAGED, extensions);
150         if (ext != null) {
151             if (fmd.category == MDStatics.CATEGORY_ARRAY && ext.getBoolean()) {
152                 throw BindingSupportImpl.getInstance().invalidOperation(
153                         "The managed option is not supported for arrays: " + fmd.name);
154             }
155             fmd.managed = ext.getBoolean();
156         } else {
157             if (fmd.category != MDStatics.CATEGORY_ARRAY) {
158                 fmd.managed = mdb.getJdbcConfig().managedManyToMany;
159             } else {
160                 fmd.managed = false;
161             }
162         }
163
164         ClassMetaData cmd = fmd.classMetaData;
165         JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
166         ArrayList JavaDoc cols = new ArrayList JavaDoc();
167         JdoExtension link = JdoExtension.find(JdoExtensionKeys.JDBC_LINK_TABLE,
168                 extensions);
169         JdoExtension[] linkNested = link == null ? null : link.nested;
170
171         // create our pk columns
172
ext = JdoExtension.find(JdoExtensionKeys.JDBC_OWNER_REF, linkNested);
173         JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(cmd, mdb,
174                 fmd.classMetaData, context,
175                 JdbcMetaDataBuilder.OWNER_REF_FIELDNAME,
176                 ext == null ? null : ext.nested, quiet);
177         ourPkColumns = rdb.getCols();
178         cols.addAll(rdb.getColsList());
179         boolean pkConstraint = !rdb.isDoNotCreateConstraint();
180         String JavaDoc pkConstraintName = rdb.getConstraintName();
181
182         // create the sequence column if required
183
if (fmd.ordered) {
184             ext = JdoExtension.find(JdoExtensionKeys.JDBC_SEQUENCE, linkNested);
185             sequenceColumn = mdb.createColumn(ext == null ? null : ext.nested,
186                     JdbcMetaDataBuilder.SEQUENCE_FIELDNAME, Integer.TYPE);
187             cols.add(sequenceColumn);
188         }
189
190         completeKeyAndValueColumnMetaData(jdbcClass, cols, context, linkNested,
191                 mdb, quiet);
192
193         // create the link table
194
linkTable = new JdbcTable();
195         linkTable.comment = fmd.getTypeQName();
196         createLinkTablePK();
197         linkTable.sqlDriver = mdb.getSqlDriver();
198         linkTable.cols = new JdbcColumn[cols.size()];
199         cols.toArray(linkTable.cols);
200         linkTable.setTableOnCols();
201
202         // Create a constraint for the main table pk columns and for the value
203
// (and key for map) columns if the value (or key) is an OID
204
List JavaDoc constraints = createConstraints(pkConstraint, pkConstraintName);
205         linkTable.constraints = new JdbcConstraint[constraints.size()];
206         constraints.toArray(linkTable.constraints);
207
208         // see if the link table must be left out of the schema or not
209
ext = JdoExtension.find(JdoExtensionKeys.JDBC_DO_NOT_CREATE_TABLE,
210                 linkNested);
211         doNotCreateTable = ext != null && ext.getBoolean();
212         String JavaDoc linkTableNameForNamegen = null;
213
214         // Make sure the link table has a name. A dummy name is used for
215
// name generation purposes if the table is being left out of the
216
// schema and there is already a table with the same name so that
217
// any generated column names will match those of the table included
218
// in the schema (if any).
219
JdbcNameGenerator namegen = mdb.getNameGenerator();
220         ext = JdoExtension.find(JdoExtensionKeys.JDBC_TABLE_NAME, linkNested);
221         if (ext == null) {
222             nameLinkTable(namegen, jdbcClass);
223         } else {
224             try {
225                 namegen.addTableName(linkTable.name = ext.getString());
226             } catch (IllegalArgumentException JavaDoc e) {
227                 if (!doNotCreateTable) {
228                     throw BindingSupportImpl.getInstance().runtime(e.getMessage() + "\n" +
229                             context.getContext(), e);
230                 }
231                 // Duplicate names are ok if the table is not being created.
232
// Use a dummy name so generated column names are the same as
233
// the original. This dummy name is removed at the end of this
234
// method.
235
linkTableNameForNamegen = linkTable.name + System.currentTimeMillis();
236                 namegen.addTableName(linkTableNameForNamegen);
237             }
238         }
239         String JavaDoc linkTableName = linkTable.name;
240         if (linkTableNameForNamegen == null) linkTableNameForNamegen = linkTableName;
241
242         // name its pk constraint
243
if (!doNotCreateTable) {
244             if (linkTable.pkConstraintName == null) {
245                 linkTable.pkConstraintName =
246                         namegen.generatePkConstraintName(linkTableName);
247             } else {
248                 namegen.addPkConstraintName(linkTableName,
249                         linkTable.pkConstraintName);
250             }
251         }
252
253         // register the names of all columns that already have names
254
for (int i = 0; i < linkTable.cols.length; i++) {
255             try {
256                 linkTable.cols[i].addColumnNames(linkTableNameForNamegen,
257                         namegen);
258             } catch (IllegalArgumentException JavaDoc e) {
259                 if (!doNotCreateTable) {
260                     throw BindingSupportImpl.getInstance().runtime(e.getMessage() + "\n" +
261                             context.getContext(), e);
262                 }
263                 // duplicates are ok if table is not being created
264
}
265         }
266
267         // name our pk columns
268
String JavaDoc[] pkNames = jdbcClass.table.getPkNames();
269         String JavaDoc[] ourPkNames = JdbcColumn.getColumnNames(ourPkColumns);
270         namegen.generateLinkTableMainRefNames(linkTableNameForNamegen, pkNames,
271                 ourPkNames);
272         JdbcColumn.setColumnNames(ourPkColumns, ourPkNames);
273
274         // name the sequence column (if any)
275
if (sequenceColumn != null && sequenceColumn.name == null) {
276             sequenceColumn.name = namegen.generateLinkTableSequenceName(
277                     linkTableNameForNamegen);
278         }
279
280         // name the key and value columns
281
nameKeyAndValueColumns(namegen, linkTableNameForNamegen);
282
283         // name all the constraints
284
if (!doNotCreateTable) {
285             for (int i = 0; i < linkTable.constraints.length; i++) {
286                 JdbcConstraint c = linkTable.constraints[i];
287                 if (c.name != null) {
288                     namegen.addRefConstraintName(linkTableName, c.name);
289                 } else {
290                     String JavaDoc[] fkNames = JdbcColumn.getColumnNames(c.srcCols);
291                     String JavaDoc[] refPkNames = JdbcColumn.getColumnNames(c.dest.pk);
292                     c.name = namegen.generateRefConstraintName(linkTableName,
293                             c.dest.name, fkNames, refPkNames);
294                 }
295             }
296         }
297
298         // remove the table if it is not going to be in the schema so other
299
// fields can be mapped to the same name and get the same generated
300
// column names
301
if (doNotCreateTable) namegen.removeTableName(linkTableNameForNamegen);
302
303         // sycn with our inverse (if any)
304
syncWithInverse(mdb);
305     }
306
307     /**
308      * Create all the constraints for our link table.
309      */

310     protected List JavaDoc createConstraints(boolean pkConstraint,
311             String JavaDoc pkConstraintName) {
312         ArrayList JavaDoc constraints = new ArrayList JavaDoc();
313
314         if (pkConstraint) {
315             JdbcConstraint mainCon = new JdbcConstraint();
316             mainCon.src = linkTable;
317             mainCon.srcCols = ourPkColumns;
318             mainCon.dest = ((JdbcClass)fmd.classMetaData.storeClass).table;
319             mainCon.name = pkConstraintName;
320             constraints.add(mainCon);
321         }
322
323         if (createValueConstraint && valuesAreOIDs
324                 && fmd.elementTypeMetaData.storeClass != null) {
325             JdbcConstraint valueCon = new JdbcConstraint();
326             valueCon.src = linkTable;
327             valueCon.srcCols = valueColumns;
328             valueCon.dest = ((JdbcClass)fmd.elementTypeMetaData.storeClass).table;
329             valueCon.name = valueConstraintName;
330             constraints.add(valueCon);
331         }
332
333         return constraints;
334     }
335
336     /**
337      * Get our extensions or null if none.
338      */

339     private JdoExtension[] getExtensions() {
340         switch (fmd.category) {
341             case MDStatics.CATEGORY_ARRAY:
342                 if (fmd.jdoArray != null) return fmd.jdoArray.extensions;
343                 return null;
344             case MDStatics.CATEGORY_COLLECTION:
345                 if (fmd.jdoCollection != null) return fmd.jdoCollection.extensions;
346                 return null;
347             case MDStatics.CATEGORY_MAP:
348                 if (fmd.jdoMap != null) return fmd.jdoMap.extensions;
349                 return null;
350         }
351         throw BindingSupportImpl.getInstance().internal(
352                 "invalid category: " + fmd.category);
353     }
354
355     /**
356      * Set the PK of the link table.
357      */

358     protected void createLinkTablePK() {
359         if (sequenceColumn != null) {
360             linkTable.setPk(JdbcColumn.concat(ourPkColumns, sequenceColumn));
361         } else {
362             linkTable.setPk(JdbcColumn.concat(ourPkColumns, valueColumns));
363         }
364     }
365
366     /**
367      * Name the key and value columns.
368      */

369     protected void nameKeyAndValueColumns(JdbcNameGenerator namegen,
370             String JavaDoc linkTableNameForNamegen) {
371         // name the value column(s)
372
if (valuesAreOIDs) {
373             String JavaDoc[] valuePkNames = JdbcColumn.getColumnNames(
374                     ((JdbcClass)fmd.elementTypeMetaData.storeClass).table.pk);
375             String JavaDoc[] linkValueRefNames = JdbcColumn.getColumnNames(
376                     valueColumns);
377             namegen.generateLinkTableValueRefNames(linkTableNameForNamegen,
378                     valuePkNames, fmd.elementType.getName(), linkValueRefNames,
379                     false);
380             JdbcColumn.setColumnNames(valueColumns, linkValueRefNames);
381         } else {
382             JdbcColumn c = valueColumns[0];
383             if (c.name == null) {
384                 c.name = namegen.generateLinkTableValueName(
385                         linkTableNameForNamegen,
386                         fmd.elementType, false);
387             }
388         }
389     }
390
391     /**
392      * Complete the key and value column related meta data.
393      */

394     protected void completeKeyAndValueColumnMetaData(JdbcClass jdbcClass,
395             ArrayList JavaDoc cols,
396             JdoElement context, JdoExtension[] linkNested,
397             JdbcMetaDataBuilder mdb, boolean quiet) {
398
399         // create the value column(s)
400
JdoExtension ext = JdoExtension.find(JdoExtensionKeys.JDBC_VALUE,
401                 linkNested);
402         if (fmd.elementTypeMetaData != null) { // values are OIDs
403
JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(
404                     fmd.classMetaData, mdb,
405                     fmd.elementTypeMetaData, context,
406                     JdbcMetaDataBuilder.VALUE_FIELDNAME,
407                     ext == null ? null : ext.nested, quiet);
408             createValueConstraint = !rdb.isDoNotCreateConstraint();
409             valueConstraintName = rdb.getConstraintName();
410             valueColumns = rdb.getCols();
411             cols.addAll(rdb.getColsList());
412         } else {
413             if (fmd.elementType == Object JavaDoc.class) {
414                 if (fmd.classMetaData.jmd.testing) {
415                     fmd.setElementType(String JavaDoc.class); // fudge so things work
416
} else {
417                     throw BindingSupportImpl.getInstance().runtime("You must specify the element-type (or value-type for maps) " +
418                             "for collections (and maps)\n" +
419                             fmd + "\n" + context.getContext());
420                 }
421             }
422
423             JdbcColumn vc = null;
424             if (fmd.category == MDStatics.CATEGORY_COLLECTION) {
425                 vc = mdb.createColumn(ext == null ? null : ext.nested,
426                         JdbcMetaDataBuilder.VALUE_FIELDNAME, fmd.elementType);
427             } else if (fmd.category == MDStatics.CATEGORY_ARRAY) {
428                 vc = mdb.createColumn(ext == null ? null : ext.nested,
429                         JdbcMetaDataBuilder.VALUE_FIELDNAME, fmd.elementType);
430                 if (Debug.DEBUG) {
431                     if (fmd.componentType != fmd.elementType) {
432                         throw new RuntimeException JavaDoc();
433                     }
434                 }
435             } else if (fmd.category == MDStatics.CATEGORY_MAP) {
436                 vc = mdb.createColumn(ext == null ? null : ext.nested,
437                         JdbcMetaDataBuilder.VALUE_FIELDNAME, fmd.elementType);
438             } else {
439                 throw BindingSupportImpl.getInstance().internal("");
440             }
441
442             valueColumns = new JdbcColumn[]{vc};
443             cols.add(vc);
444         }
445
446         //update allowNulls
447
boolean nulls = allowNulls();
448         for (int i = 0; i < valueColumns.length; i++) {
449             JdbcColumn valueColumn = valueColumns[i];
450             valueColumn.setNulls(nulls);
451         }
452     }
453
454     /**
455      * Name our linkTable.
456      */

457     protected void nameLinkTable(JdbcNameGenerator namegen,
458             JdbcClass jdbcClass) {
459         JdbcClass valueTarget = null;
460         if (valuesAreOIDs) valueTarget = (JdbcClass)fmd.elementTypeMetaData.storeClass;
461         linkTable.name = namegen.generateLinkTableName(jdbcClass.table.name, fmd.name,
462                 valueTarget == null ? null : valueTarget.table.name);
463     }
464
465     /**
466      * Persist pass 2 field for a block of graph entries all with
467      * the same class. The same ps'es can be used for all entries in the block.
468      */

469     public void persistPass2Block(PersistGraph graph, int blockStart,
470             int blockEnd, CharBuf s, Connection JavaDoc con, boolean batchInserts,
471             boolean batchUpdates) throws SQLException JavaDoc {
472         if (Debug.DEBUG) {
473             if (readOnly) {
474                 for (int pos = blockStart; pos < blockEnd; pos++) {
475                     State ns = graph.getNewState(pos);
476                     if (ns.getInternalObjectField(stateFieldNo) != null) {
477                         throw BindingSupportImpl.getInstance().internal(
478                                 "readOnly field in ns");
479                     }
480                 }
481             }
482         }
483         if (readOnly) return;
484         if (fmd.ordered) {
485             persistPass2BlockOrdered(graph, blockStart, blockEnd, s, con,
486                     batchInserts, batchUpdates);
487         } else {
488             persistPass2BlockUnordered(graph, blockStart, blockEnd, s, con,
489                     batchInserts, batchUpdates);
490         }
491     }
492
493     private void persistPass2BlockOrdered(PersistGraph graph, int blockStart,
494             int blockEnd, CharBuf s, Connection JavaDoc con, boolean batchInserts,
495             boolean batchUpdates) throws SQLException JavaDoc {
496         PreparedStatement JavaDoc psdel = null;
497         PreparedStatement JavaDoc psdelAll = null;
498         PreparedStatement JavaDoc psins = null;
499         int delCount = 0;
500         try {
501             String JavaDoc psdelSql = null;
502             String JavaDoc psdelAllSql = null;
503             String JavaDoc psinsSql = null;
504             for (int pos = blockStart; pos < blockEnd; pos++) {
505                 State ns = graph.getNewState(pos);
506                 if (!ns.containsField(stateFieldNo)) continue;
507
508                 OID oid = graph.getOID(pos);
509                 if (fmd.category == MDStatics.CATEGORY_ARRAY) {
510                     if (!oid.isNew()) {
511                         //delete the current entries
512
if (psdelAll == null) {
513                             psdelAllSql = getDeleteAllLinkTableRowsSql(s);
514                             psdelAll = con.prepareStatement(psdelAllSql);
515                         }
516                         ((JdbcOID)oid).setParams(psdelAll, 1);
517                         if (batchUpdates) {
518                             psdelAll.addBatch();
519                         } else {
520                             try {
521                                 psdelAll.execute();
522                             } catch (Exception JavaDoc e) {
523                                 throw mapException(e,
524                                         "Delete all link table rows failed: " +
525                                         JdbcUtils.toString(e) + "\n" +
526                                         "Field: " + fmd.getTypeQName() + "\n" +
527                                         "Instance: " + oid.toSString() + "\n" +
528                                         JdbcUtils.getPreparedStatementInfo(
529                                                 psdelAllSql, psdelAll));
530                             }
531                         }
532                     }
533
534                     Object JavaDoc toInsert = ns.getInternalObjectField(stateFieldNo);
535                     if (toInsert != null) {
536                         if (psins == null) {
537                             psinsSql = getInsertLinkTableRowSql(s);
538                             psins = con.prepareStatement(psinsSql);
539                         }
540
541                         if (fmd.componentType.isPrimitive()) {
542                             //throw not supported
543
throw BindingSupportImpl.getInstance().unsupported();
544                         } else {
545                             insertOrderedLinkTableRows(oid, null,
546                                     toInsert, psins, batchInserts, psinsSql);
547                         }
548                     }
549
550                 } else {
551                     OrderedCollectionDiff diff =
552                             (OrderedCollectionDiff)ns.getInternalObjectField(
553                                     stateFieldNo);
554                     if (diff == null || diff.status == CollectionDiff.STATUS_NEW) {
555                         if (!oid.isNew()) {
556                             if (psdelAll == null) {
557                                 psdelAllSql = getDeleteAllLinkTableRowsSql(s);
558                                 psdelAll = con.prepareStatement(psdelAllSql);
559                             }
560                             ((JdbcOID)oid).setParams(psdelAll, 1);
561                             if (batchUpdates) {
562                                 psdelAll.addBatch();
563                             } else {
564                                 try {
565                                     psdelAll.execute();
566                                 } catch (Exception JavaDoc e) {
567                                     throw mapException(e,
568                                             "Delete all link table rows failed: " +
569                                             JdbcUtils.toString(e) + "\n" +
570                                             "Field: " + fmd.getTypeQName() + "\n" +
571                                             "Instance: " + oid.toSString() + "\n" +
572                                             JdbcUtils.getPreparedStatementInfo(
573                                                     psdelAllSql, psdelAll));
574                                 }
575                             }
576                         }
577                     } else {
578                         int[] deleted = diff.deletedIndexes;
579                         if (deleted != null && deleted.length > 0) {
580                             if (psdel == null) {
581                                 psdelSql = getDeleteLinkTableRowSql(s);
582                                 psdel = con.prepareStatement(psdelSql);
583                             }
584                             deletedOrderedLinkTableRows(oid, deleted, psdel,
585                                     batchUpdates, psdelSql);
586                             delCount += deleted.length;
587                         }
588                     }
589
590                     if (diff != null) {
591                         Object JavaDoc[] insertedValues = diff.insertedValues;
592                         if (insertedValues != null && insertedValues.length > 0) {
593                             if (psins == null) {
594                                 psinsSql = getInsertLinkTableRowSql(s);
595                                 psins = con.prepareStatement(psinsSql);
596                             }
597                             insertOrderedLinkTableRows(oid,
598                                     diff.insertedIndexes,
599                                     insertedValues, psins, batchInserts,
600                                     psinsSql);
601                         }
602                     }
603                 }
604             }
605             if (batchUpdates) {
606                 execLinkTableBatchDeletes(delCount, psdel, psdelSql,
607                         psdelAll, psdelAllSql);
608             }
609             if (batchInserts && psins != null) {
610                 execLinkTableBatchInserts(psins, psinsSql);
611             }
612         } finally {
613             cleanup(psdel);
614             cleanup(psdelAll);
615             cleanup(psins);
616         }
617     }
618
619     protected void execLinkTableBatchInserts(PreparedStatement JavaDoc psins,
620             String JavaDoc psinsSql) {
621         try {
622             psins.executeBatch();
623         } catch (SQLException JavaDoc e) {
624             throw mapException(e,
625                     "Link table batch insert failed: " +
626                     JdbcUtils.toString(e) + "\n" +
627                     "Field: " + fmd.getTypeQName() + "\n" +
628                     JdbcUtils.getPreparedStatementInfo(psinsSql, psins));
629         }
630     }
631
632     protected void execLinkTableBatchDeletes(int delCount, PreparedStatement JavaDoc psdel,
633             String JavaDoc psdelSql, PreparedStatement JavaDoc psdelAll, String JavaDoc psdelAllSql) {
634         if (delCount > 0) {
635             int[] a;
636             try {
637                 a = psdel.executeBatch();
638             } catch (Exception JavaDoc e) {
639                 throw mapException(e,
640                         "Link table batch delete failed: " +
641                         JdbcUtils.toString(e) + "\n" +
642                         "Field: " + fmd.getTypeQName() + "\n" +
643                         JdbcUtils.getPreparedStatementInfo(psdelSql, psdel));
644             }
645             for (int i = 0; i < delCount; i++) {
646                 int c = a[i];
647                 if (c <= 0) {
648                     String JavaDoc psi = JdbcUtils.getPreparedStatementInfo(psdelSql,
649                             psdel, i);
650                     if (c == 0) {
651                         throw BindingSupportImpl.getInstance().concurrentUpdate("Link table row not found on batch delete: " +
652                                 "Field: " + fmd.getTypeQName() + "\n" + psi, null);
653                     }
654                     throw BindingSupportImpl.getInstance().datastore("Unexpected update count for link table row batch delete: " +
655                             c + "\nField: " + fmd.getTypeQName() + "\n" + psi);
656                 }
657             }
658         }
659         if (psdelAll != null) {
660             try {
661                 psdelAll.executeBatch();
662             } catch (Exception JavaDoc e) {
663                 throw mapException(e,
664                         "Link table batch delete all failed: " +
665                         JdbcUtils.toString(e) + "\n" +
666                         "Field: " + fmd.getTypeQName() + "\n" +
667                         JdbcUtils.getPreparedStatementInfo(psdelAllSql,
668                                 psdelAll));
669             }
670         }
671     }
672
673     /**
674      * Delete rows from an ordered link table.
675      */

676     private void deletedOrderedLinkTableRows(OID oid, int[] deleted,
677             PreparedStatement JavaDoc psdel, boolean batch, String JavaDoc sql)
678             throws SQLException JavaDoc {
679         JdbcColumn sc = sequenceColumn;
680         for (int j = deleted.length - 1; j >= 0; j--) {
681             int pp = ((JdbcOID)oid).setParams(psdel, 1);
682             sc.set(psdel, pp, deleted[j]);
683             if (batch) {
684                 psdel.addBatch();
685             } else {
686                 int uc;
687                 try {
688                     uc = psdel.executeUpdate();
689                 } catch (Exception JavaDoc e) {
690                     throw mapException(e,
691                             "Delete link table row failed: " +
692                             JdbcUtils.toString(e) + "\n" +
693                             "Field: " + fmd.getTypeQName() + "\n" +
694                             "Sequence: " + deleted[j] + "\n" +
695                             "Instance: " + oid.toSString() + "\n" +
696                             JdbcUtils.getPreparedStatementInfo(sql, psdel));
697                 }
698                 if (uc == 0) {
699                     throw BindingSupportImpl.getInstance().concurrentUpdate("Link table row not found: " +
700                             "Field: " + fmd.getTypeQName() + "\n" +
701                             "Sequence: " + deleted[j] + "\n" +
702                             "Instance: " + oid.toSString() + "\n" +
703                             JdbcUtils.getPreparedStatementInfo(sql, psdel), oid);
704                 }
705             }
706         }
707     }
708
709     /**
710      * Insert values into an ordered link table.
711      */

712     private void insertOrderedLinkTableRows(OID oid, int[] insertedIndexes,
713             Object JavaDoc _insertedValues, PreparedStatement JavaDoc psins, boolean batch,
714             String JavaDoc sql) throws SQLException JavaDoc {
715
716         Object JavaDoc[] insertedValues = (Object JavaDoc[])_insertedValues;
717         int ilen = insertedValues.length;
718
719
720         if (valuesAreOIDs) {
721             for (int j = 0; j < ilen; j++) {
722                 int pp = ((JdbcOID)oid).setParams(psins, 1);
723                 sequenceColumn.set(psins, pp++,
724                         insertedIndexes == null ? j : insertedIndexes[j]);
725
726                 OID _oid = (OID)insertedValues[j];
727
728
729                 
730                 if (_oid != null) {
731                     ((JdbcOID)_oid).setParams(psins, pp);
732                 } else {
733                     JdbcGenericOID.setNullParams(psins, pp, fmd.elementTypeMetaData);
734                 }
735                 if (batch) {
736                     psins.addBatch();
737                 } else {
738                     try {
739                         psins.execute();
740                     } catch (Exception JavaDoc e) {
741                         throw mapException(e,
742                                 "Insert link table row failed: " +
743                                 JdbcUtils.toString(e) + "\n" +
744                                 "Field: " + fmd.getTypeQName() + "\n" +
745                                 "Instance: " + oid.toSString() + "\n" +
746                                 "Link table value[sequence " + insertedIndexes[j] + "]: " +
747                                 _oid.toSString() + "\n" +
748                                 JdbcUtils.getPreparedStatementInfo(sql, psins));
749                     }
750                 }
751             }
752         } else {
753             JdbcColumn vc = valueColumns[0];
754             for (int j = 0; j < ilen; j++) {
755                 int pp = ((JdbcOID)oid).setParams(psins, 1);
756                 sequenceColumn.set(psins, pp++,
757                         insertedIndexes == null ? j : insertedIndexes[j]);
758
759                 vc.set(psins, pp, insertedValues[j]);
760
761
762                 if (batch) {
763                     psins.addBatch();
764                 } else {
765                     try {
766                         psins.execute();
767                     } catch (Exception JavaDoc e) {
768                         throw mapException(e,
769                                 "Insert link table row failed: " +
770                                 JdbcUtils.toString(e) + "\n" +
771                                 "Field: " + fmd.getTypeQName() + "\n" +
772                                 "Instance: " + oid.toSString() + "\n" +
773                                 "Link table value[sequence " + insertedIndexes[j] + "]: " +
774
775                                 Utils.toString(insertedValues[j]) + "\n" +
776
777
778                                 JdbcUtils.getPreparedStatementInfo(sql, psins));
779                     }
780                 }
781             }
782         }
783     }
784
785     private void persistPass2BlockUnordered(PersistGraph graph, int blockStart,
786             int blockEnd, CharBuf s, Connection JavaDoc con, boolean batchInserts,
787             boolean batchUpdates) throws SQLException JavaDoc {
788         PreparedStatement JavaDoc psdel = null;
789         PreparedStatement JavaDoc psdelAll = null;
790         PreparedStatement JavaDoc psins = null;
791         int delCount = 0;
792         try {
793             String JavaDoc psdelSql = null;
794             String JavaDoc psdelAllSql = null;
795             String JavaDoc psinsSql = null;
796             for (int pos = blockStart; pos < blockEnd; pos++) {
797                 State ns = graph.getNewState(pos);
798                 if (!ns.containsField(stateFieldNo)) continue;
799
800                 UnorderedCollectionDiff diff =
801                         (UnorderedCollectionDiff)ns.getInternalObjectField(
802                                 stateFieldNo);
803
804                 OID oid = graph.getOID(pos);
805
806                 if (diff == null || diff.status == CollectionDiff.STATUS_NEW) {
807                     if (!oid.isNew()) {
808                         if (psdelAll == null) {
809                             psdelAllSql = getDeleteAllLinkTableRowsSql(s);
810                             psdelAll = con.prepareStatement(psdelAllSql);
811                         }
812                         ((JdbcOID)oid).setParams(psdelAll, 1);
813                         if (batchUpdates) {
814                             psdelAll.addBatch();
815                         } else {
816                             try {
817                                 psdelAll.execute();
818                             } catch (Exception JavaDoc e) {
819                                 throw mapException(e,
820                                         "Delete all link table rows failed: " +
821                                         JdbcUtils.toString(e) + "\n" +
822                                         "Field: " + fmd.getTypeQName() + "\n" +
823                                         "Instance: " + oid.toSString() + "\n" +
824                                         JdbcUtils.getPreparedStatementInfo(
825                                                 psdelAllSql, psdelAll));
826                             }
827                         }
828                     }
829                 } else {
830                     Object JavaDoc[] deleted = diff.deletedValues;
831                     if (deleted != null && deleted.length > 0) {
832                         if (psdel == null) {
833                             psdelSql = getDeleteLinkTableRowSql(s);
834                             psdel = con.prepareStatement(psdelSql);
835                         }
836                         deleteUnorderedLinkTableRows(oid, deleted, psdel,
837                                 batchUpdates, psdelSql);
838                         delCount += deleted.length;
839                     }
840                 }
841
842                 if (diff != null) {
843                     Object JavaDoc[] inserted = diff.insertedValues;
844                     if (inserted != null && inserted.length > 0) {
845                         if (psins == null) {
846                             psinsSql = getInsertLinkTableRowSql(s);
847                             psins = con.prepareStatement(psinsSql);
848                         }
849                         insertUnorderedLinkTableRows(oid, inserted,
850                                 psins, batchInserts, psinsSql);
851                     }
852                 }
853             }
854             if (batchUpdates) {
855                 execLinkTableBatchDeletes(delCount, psdel, psdelSql,
856                         psdelAll, psdelAllSql);
857             }
858             if (batchInserts && psins != null) {
859                 execLinkTableBatchInserts(psins, psinsSql);
860             }
861         } finally {
862             cleanup(psdel);
863             cleanup(psdelAll);
864             cleanup(psins);
865         }
866     }
867
868     /**
869      * Delete values from an unordered link table.
870      */

871     private void deleteUnorderedLinkTableRows(OID oid, Object JavaDoc[] deleted,
872             PreparedStatement JavaDoc psdel, boolean batch, String JavaDoc sql)
873             throws SQLException JavaDoc {
874         if (valuesAreOIDs) {
875             for (int j = deleted.length - 1; j >= 0; j--) {
876                 int pp = ((JdbcOID)oid).setParams(psdel, 1);
877                 ((JdbcOID)deleted[j]).setParams(psdel, pp);
878                 if (batch) {
879                     psdel.addBatch();
880                 } else {
881                     int uc;
882                     try {
883                         uc = psdel.executeUpdate();
884                     } catch (Exception JavaDoc e) {
885                         throw mapException(e,
886                                 "Delete link table row failed: " +
887                                 JdbcUtils.toString(e) + "\n" +
888                                 "Field: " + fmd.getTypeQName() + "\n" +
889                                 "Value: " + ((OID)deleted[j]).toSString() + "\n" +
890                                 "Instance: " + oid.toSString() + "\n" +
891                                 JdbcUtils.getPreparedStatementInfo(sql, psdel));
892                     }
893                     if (uc == 0) {
894                         throw BindingSupportImpl.getInstance().concurrentUpdate("Link table row not found: " +
895                                 "Field: " + fmd.getTypeQName() + "\n" +
896                                 "Value: " + ((OID)deleted[j]).toSString() + "\n" +
897                                 "Instance: " + oid.toSString() + "\n" +
898                                 JdbcUtils.getPreparedStatementInfo(sql, psdel), deleted[j]);
899                     }
900                 }
901             }
902         } else {
903             JdbcColumn vc = valueColumns[0];
904             for (int j = deleted.length - 1; j >= 0; j--) {
905                 int pp = ((JdbcOID)oid).setParams(psdel, 1);
906                 vc.set(psdel, pp, deleted[j]);
907                 if (batch) {
908                     psdel.addBatch();
909                 } else {
910                     int uc;
911                     try {
912                         uc = psdel.executeUpdate();
913                     } catch (Exception JavaDoc e) {
914                         throw mapException(e,
915                                 "Delete link table row failed: " +
916                                 JdbcUtils.toString(e) + "\n" +
917                                 "Field: " + fmd.getTypeQName() + "\n" +
918                                 "Value: " + Utils.toString(deleted[j]) + "\n" +
919                                 "Instance: " + oid.toSString() + "\n" +
920                                 JdbcUtils.getPreparedStatementInfo(sql, psdel));
921                     }
922                     if (uc == 0) {
923                         throw BindingSupportImpl.getInstance().concurrentUpdate("Link table row not found: " +
924                                 "Field: " + fmd.getTypeQName() + "\n" +
925                                 "Value: " + Utils.toString(deleted[j]) + "\n" +
926                                 "Instance: " + oid.toSString() + "\n" +
927                                 JdbcUtils.getPreparedStatementInfo(sql, psdel), oid);
928                     }
929                 }
930             }
931         }
932     }
933
934     /**
935      * Insert values into an unordered link table.
936      */

937     private void insertUnorderedLinkTableRows(OID oid, Object JavaDoc[] inserted,
938             PreparedStatement JavaDoc psins, boolean batch, String JavaDoc sql)
939             throws SQLException JavaDoc {
940         int ilen = inserted.length;
941         if (valuesAreOIDs) {
942             for (int j = 0; j < ilen; j++) {
943                 int pp = ((JdbcOID)oid).setParams(psins, 1);
944                 ((JdbcOID)inserted[j]).setParams(psins, pp);
945                 if (batch) {
946                     psins.addBatch();
947                 } else {
948                     try {
949                         psins.execute();
950                     } catch (Exception JavaDoc e) {
951                         throw mapException(e,
952                                 "Insert link table row failed: " +
953                                 JdbcUtils.toString(e) + "\n" +
954                                 "Field: " + fmd.getTypeQName() + "\n" +
955                                 "Instance: " + oid.toSString() + "\n" +
956                                 "Value: " + ((OID)inserted[j]).toSString() + "\n" +
957                                 JdbcUtils.getPreparedStatementInfo(sql, psins));
958                     }
959                 }
960             }
961         } else {
962             JdbcColumn vc = valueColumns[0];
963             for (int j = 0; j < ilen; j++) {
964                 int pp = ((JdbcOID)oid).setParams(psins, 1);
965                 vc.set(psins, pp, inserted[j]);
966                 if (batch) {
967                     psins.addBatch();
968                 } else {
969                     try {
970                         psins.execute();
971                     } catch (Exception JavaDoc e) {
972                         throw mapException(e,
973                                 "Insert link table row failed: " +
974                                 JdbcUtils.toString(e) + "\n" +
975                                 "Field: " + fmd.getTypeQName() + "\n" +
976                                 "Instance: " + oid.toSString() + "\n" +
977                                 "Value: " + Utils.toString(inserted[j]) + "\n" +
978                                 JdbcUtils.getPreparedStatementInfo(sql, psins));
979                     }
980                 }
981             }
982         }
983     }
984
985     /**
986      * Get SQL to delete a row from our link table.
987      */

988     protected String JavaDoc getDeleteLinkTableRowSql(CharBuf s) {
989         if (deleteRowSql == null) {
990             s.clear();
991             s.append("delete from ");
992             s.append(linkTable.name);
993             s.append(" where ");
994             linkTable.appendWherePK(s);
995             deleteRowSql = s.toString();
996         }
997         return deleteRowSql;
998     }
999
1000    /**
1001     * Get SQL to delete all rows from our link table.
1002     */

1003    protected String JavaDoc getDeleteAllLinkTableRowsSql(CharBuf s) {
1004        if (deleteAllRowsSql == null) {
1005            s.clear();
1006            s.append("DELETE FROM ");
1007            s.append(linkTable.name);
1008            s.append(" WHERE ");
1009            SqlDriver driver = linkTable.sqlDriver;
1010            int nc = ourPkColumns.length;
1011            JdbcColumn sc = ourPkColumns[0];
1012            s.append(sc.name);
1013            s.append(' ');
1014            s.append('=');
1015            s.append(' ');
1016            driver.appendWhereParam(s, sc);
1017            for (int i = 1; i < nc; i++) {
1018                s.append(" AND ");
1019                sc = ourPkColumns[i];
1020                s.append(sc.name);
1021                s.append(' ');
1022                s.append('=');
1023                s.append(' ');
1024                driver.appendWhereParam(s, sc);
1025            }
1026            deleteAllRowsSql = s.toString();
1027        }
1028        return deleteAllRowsSql;
1029    }
1030
1031    /**
1032     * Get SQL to delete all rows from our link table with a 'IN' List. This is only
1033     * supported if there is a single pk column.
1034     */

1035    protected void getDeleteAllLinkTableRowsSqlWithInList(CharBuf s) {
1036        s.clear();
1037        s.append("delete from ");
1038        s.append(linkTable.name);
1039        s.append(" where ");
1040        JdbcColumn sc = ourPkColumns[0];
1041        s.append(sc.name);
1042        s.append(" IN (");
1043    }
1044
1045    /**
1046     * Get SQL to insert a row into our link table.
1047     */

1048    public String JavaDoc getInsertLinkTableRowSql(CharBuf s) {
1049        if (insertRowSql == null) {
1050            s.clear();
1051            s.append("INSERT INTO ");
1052            s.append(linkTable.name);
1053            s.append('(');
1054            linkTable.appendInsertColumnList(s);
1055            s.append(") VALUES (");
1056            linkTable.appendInsertValueList(s);
1057            s.append(')');
1058            insertRowSql = s.toString();
1059        }
1060        return insertRowSql;
1061    }
1062
1063    /**
1064     * if null is allowed by default for this collection.
1065     */

1066    private boolean allowNulls() {
1067        return fmd.ordered || fmd.category == MDStatics.CATEGORY_MAP;
1068    }
1069
1070    /**
1071     * Get a SelectExp to select all the rows in this collection using the
1072     * supplied fetch group field to control joins and so on.
1073     */

1074    protected SelectExp getSelectExp(JdbcStorageManager sm, FetchGroupField field,
1075            FgDs[] fgDses) {
1076        SelectExp root = new SelectExp();
1077        root.table = linkTable;
1078        root.selectList = JdbcColumn.toSqlExp(valueColumns, root);
1079        root.whereExp = JdbcColumn.createEqualsParamExp(ourPkColumns, root);
1080
1081        if (valuesAreOIDs) {
1082            if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO || fmd.ordering != null) {
1083                SelectExp se = new SelectExp();
1084                JdbcClass valueJdbcClass = (JdbcClass)fmd.elementTypeMetaData.storeClass;
1085                se.table = valueJdbcClass.table;
1086                se.outer = field.jdbcUseJoin != JdbcField.USE_JOIN_INNER;
1087
1088                root.addJoin(valueColumns, se.table.pk, se);
1089                sm.addSelectFetchGroup(se, field.nextFetchGroup, true,
1090                        fgDses[0] = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true,
1091                                field.jdbcUseJoin != JdbcField.USE_JOIN_INNER),
1092                        root, valueColumns, this);
1093
1094                if (fmd.ordering != null) {
1095                    se.addOrderBy(fmd.ordering, false);
1096                    root.orderByList = se.orderByList;
1097                    se.orderByList = null;
1098                }
1099            }
1100        } else {
1101            if (fmd.ordering != null) {
1102                // the ordering can only be 'this ascending' or 'this descending'
1103
boolean desc = fmd.ordering[0].order == OrderNode.ORDER_DESCENDING;
1104                root.orderByList = new OrderExp(valueColumns[0].toSqlExp(root),
1105                        desc);
1106                // there will be only one entry in valueColumns as this is
1107
// not a collection of PC instances
1108
}
1109        }
1110        if (fmd.ordered) {
1111            if (fmd.ordering != null) {
1112                throw BindingSupportImpl.getInstance().internal(
1113                        "ordered == true && ordering != null, " + fmd.getTypeQName());
1114            }
1115            root.orderByList = linkTable.createOrderByPKList(root);
1116        }
1117        return root;
1118    }
1119
1120    public SelectExp getSelectExpFrom(JdbcStorageManager sm, SelectExp joinToExp, FetchGroupField field,
1121            FgDs owningFgDs) {
1122        SelectExp root = getSelectExpFromImp(joinToExp, field, sm,
1123                owningFgDs);
1124        root.selectList = JdbcColumn.toSqlExp(ourPkColumns, root,
1125                root.selectList);
1126        return root;
1127    }
1128
1129    public SelectExp getSelectExpFromImp(SelectExp joinToExp, FetchGroupField field,
1130            JdbcStorageManager sm, FgDs owningFgDs) {
1131        SelectExp root = new SelectExp();
1132        root.table = linkTable;
1133        root.selectList = JdbcColumn.toSqlExp(valueColumns, root);
1134
1135        if (valuesAreOIDs) {
1136            if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO || fmd.ordering != null) {
1137                SelectExp se = new SelectExp();
1138                JdbcClass valueJdbcClass = (JdbcClass)fmd.elementTypeMetaData.storeClass;
1139                se.table = valueJdbcClass.table;
1140                se.outer = true;
1141
1142                root.addJoin(valueColumns, se.table.pk, se);
1143                FgDs fgDs = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, se.outer);
1144                sm.addSelectFetchGroup(se, field.nextFetchGroup, true,
1145                        fgDs, root, valueColumns, this);
1146                owningFgDs.valueJs = fgDs.getJoinStruct();
1147
1148                if (fmd.ordering != null) {
1149                    se.addOrderBy(fmd.ordering, false);
1150                    root.orderByList = se.orderByList;
1151                    se.orderByList = null;
1152                }
1153            }
1154        } else {
1155            if (fmd.ordering != null) {
1156                // the ordering can only be 'this ascending' or 'this descending'
1157
boolean desc = fmd.ordering[0].order == OrderNode.ORDER_DESCENDING;
1158                root.orderByList = new OrderExp(valueColumns[0].toSqlExp(root),
1159                        desc);
1160                // there will be only one entry in valueColumns as this is
1161
// not a collection of PC instances
1162
}
1163        }
1164        if (fmd.ordered) {
1165            if (fmd.ordering != null) {
1166                throw BindingSupportImpl.getInstance().internal(
1167                        "ordered == true && ordering != null, " + fmd.getTypeQName());
1168            }
1169            root.orderByList = linkTable.createOrderByPKList(root);
1170        }
1171
1172        root.outer = true;
1173        joinToExp.addJoin(joinToExp.table.pk, ourPkColumns, root);
1174        joinToExp.appendOrderByExp(root.orderByList);
1175        root.orderByList = null;
1176        return root;
1177    }
1178
1179    /**
1180     * Get a SelectExp to select all the rows in this collection using the
1181     * supplied fetch group field to control joins and so on.
1182     */

1183    public SelectExp getSelectFilterExp(JdbcStorageManager sm, FetchGroupField field,
1184            ColFieldHolder colFHolder) {
1185        SelectExp root = new SelectExp();
1186        root.table = linkTable;
1187        root.selectList = JdbcColumn.toSqlExp(valueColumns, root);
1188
1189        // prepend our pk columns to the select list
1190
SqlExp e = JdbcColumn.toSqlExp(ourPkColumns, root, root.selectList);
1191        root.selectList = e;
1192        root.appendOrderByForColumns(ourPkColumns);
1193
1194        if (valuesAreOIDs) {
1195            if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO || fmd.ordering != null) {
1196                SelectExp se = new SelectExp();
1197                JdbcClass valueJdbcClass = (JdbcClass)fmd.elementTypeMetaData.storeClass;
1198                se.table = valueJdbcClass.table;
1199                se.outer = field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER;
1200
1201                FgDs fgDs = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, se.outer);
1202                sm.addSelectFetchGroup(se, field.nextFetchGroup, true,
1203                        fgDs, root, valueColumns, this);
1204                colFHolder.valueJs = fgDs.getJoinStruct();
1205
1206                root.addJoin(valueColumns, se.table.pk, se);
1207                if (fmd.ordering != null) {
1208                    se.addOrderBy(fmd.ordering, false);
1209                    root.appendOrderByExp(se.orderByList);
1210                    se.orderByList = null;
1211                }
1212            }
1213        } else {
1214            if (fmd.ordering != null) {
1215                // the ordering can only be 'this ascending' or 'this descending'
1216
boolean desc = fmd.ordering[0].order == OrderNode.ORDER_DESCENDING;
1217                root.appendOrderByExp(new OrderExp(
1218                        valueColumns[0].toSqlExp(root),
1219                        desc));
1220                // there will be only one entry in valueColumns as this is
1221
// not a collection of PC instances
1222
}
1223        }
1224        if (fmd.ordered) {
1225            if (fmd.ordering != null) {
1226                throw BindingSupportImpl.getInstance().internal(
1227                        "ordered == true && ordering != null, " + fmd.getTypeQName());
1228            }
1229            root.appendOrderByForColumns(linkTable.pkSimpleCols);
1230        }
1231        return root;
1232    }
1233
1234    public SelectExp getSelectFilterJoinExp(boolean value, SelectExp lhSe,
1235            SelectExp rootSe, boolean addRootJoin) {
1236        SelectExp root = new SelectExp();
1237        root.table = linkTable;
1238        if (valuesAreOIDs) {
1239            SelectExp se = new SelectExp();
1240            se.table = ((JdbcClass)fmd.elementTypeMetaData.storeClass).table;
1241            root.addJoin(valueColumns, se.table.pk, se);
1242
1243            lhSe.addJoin(lhSe.table.pk, ourPkColumns, root);
1244
1245            return se;
1246        } else {
1247            throw BindingSupportImpl.getInstance().internal(
1248                    "This must only be called for pc collections");
1249        }
1250    }
1251
1252    /**
1253     * Fetch the values for this field using parallel query processing.
1254     */

1255    public int fetchWithFilter(JdbcStorageManager sm, StateContainer oidStates,
1256            FetchGroupField field, ResultSet JavaDoc rs, boolean forUpdate,
1257            OID oidToCheckOn,
1258            OID[] lastReadStateOID, ClassMetaData cmd,
1259            ColFieldHolder colFHolder) throws SQLException JavaDoc {
1260
1261        ClassMetaData valueCmd = fmd.elementTypeMetaData;
1262        boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
1263        FetchGroup nextFetchGroup = field.nextFetchGroup;
1264        int valuePkLen = 0;
1265        int keyPkLen = ((JdbcClass)cmd.storeClass).table.pkSimpleColumnCount;
1266        if (joined) valuePkLen = ((JdbcClass)valueCmd.storeClass).table.pkSimpleColumnCount;
1267        int stateOIDPKLen = ((JdbcClass)fmd.classMetaData.storeClass).table.pkSimpleColumnCount;
1268
1269        JdbcColumn vc = valueColumns[0];
1270
1271        //the oid just read from the rs row
1272
OID rootOid = cmd.createOID(false);
1273        //the oid read from the previous rs row
1274
OID prevRootOid = cmd.createOID(false);
1275        OID tmpOID = null;
1276
1277        OID stateOID = fmd.classMetaData.createOID(false);
1278        OID prevStateOID = fmd.classMetaData.createOID(false);
1279        OID tmpStateOID = null;
1280
1281        boolean currentRowValid = false;
1282        boolean prevRowValid = false;
1283
1284        Struct struct = new Struct();
1285        struct.init();
1286        int returnState = 0;
1287
1288        FgDs fgDs = null;
1289        if (valuesAreOIDs) {
1290            fgDs = ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getExistingFgDs(
1291                    true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER);
1292            if (colFHolder != null) {
1293                colFHolder.valueJs = fgDs.getJoinStruct();
1294            }
1295        }
1296
1297        if (lastReadStateOID[0] != null) {
1298            prevRootOid = lastReadStateOID[0];
1299            stateOID = lastReadStateOID[1];
1300            currentRowValid = oidStates.containsKey(stateOID);
1301
1302            if (currentRowValid) {
1303                returnState |= STATUS_VALID_ROWS;
1304                if (valuesAreOIDs) {
1305                    OID valueOid = valueCmd.createOID(false);
1306                    boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs,
1307                            1 + keyPkLen + stateOIDPKLen);
1308
1309                    if (!isNull) {
1310                        struct.add(valueOid);
1311                    } else {
1312                        struct.add(null);
1313                    }
1314
1315                    if (!isNull && joined && oidStates.isStateRequired(
1316                            valueOid,
1317                            nextFetchGroup)) {
1318                        returnState |= STATUS_DATA_ADDED;
1319                        State valueState = sm.createStateImp(rs, valueOid,
1320                                nextFetchGroup, forUpdate, 1 + valuePkLen + keyPkLen + valuePkLen,
1321                                null, true, oidStates, fgDs, false,
1322                                false, null);
1323                        oidStates.addState(valueOid, valueState);
1324                    }
1325
1326                } else {
1327                    struct.add(vc.get(rs, 1 + keyPkLen + stateOIDPKLen));
1328                }
1329            }
1330
1331            //preserve the prevRootOid
1332
tmpOID = rootOid;
1333            rootOid = prevRootOid;
1334            prevRootOid = tmpOID;
1335
1336            //preserve the prevStateOID
1337
tmpStateOID = stateOID;
1338            stateOID = prevStateOID;
1339            prevStateOID = tmpStateOID;
1340
1341            prevRowValid = currentRowValid;
1342        }
1343
1344        for (; rs.next();) {
1345            int index = 1;
1346            ((JdbcOID)rootOid).copyKeyFields(rs, index);
1347            index += keyPkLen;
1348
1349            ((JdbcOID)stateOID).copyKeyFields(rs, index);
1350            index += stateOIDPKLen;
1351
1352            currentRowValid = oidStates.containsKey(stateOID);
1353
1354            //detected a change in stateOid. Only update if the previous
1355
// row was for a valid oid.
1356
if (!stateOID.equals(prevStateOID) && prevRowValid) {
1357                if (updateStateFilter(struct, oidStates.get(prevStateOID))) {
1358                    returnState |= STATUS_DATA_ADDED;
1359                }
1360
1361                updateStatistics(struct.size);
1362                if (oidToCheckOn.equals(prevRootOid) && !oidToCheckOn.equals(
1363                        rootOid)) {
1364                    lastReadStateOID[0] = rootOid;
1365                    lastReadStateOID[1] = stateOID;
1366                    returnState |= STATUS_VALID_ROWS;
1367                    return returnState;
1368                }
1369
1370                struct.init();
1371            }
1372
1373            if (currentRowValid) {
1374                returnState |= STATUS_VALID_ROWS;
1375                if (valuesAreOIDs) {
1376                    OID valueOid = valueCmd.createOID(false);
1377                    boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs, index);
1378                    index += valuePkLen;
1379
1380                    if (!isNull) {
1381                        struct.add(valueOid);
1382                    } else {
1383                        struct.add(null);
1384                    }
1385
1386                    if (!isNull && joined && oidStates.isStateRequired(
1387                            valueOid,
1388                            nextFetchGroup)) {
1389                        State valueState = sm.createStateImp(rs, valueOid,
1390                                nextFetchGroup, forUpdate, index,
1391                                null, true, oidStates,
1392                                ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getFgDs(
1393                                        true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER),
1394                                false, false, null);
1395                        oidStates.addState(valueOid, valueState);
1396                    }
1397                } else {
1398                    struct.add(vc.get(rs, index));
1399                }
1400            }
1401
1402            //preserve the prevRootOid
1403
tmpOID = rootOid;
1404            rootOid = prevRootOid;
1405            prevRootOid = tmpOID;
1406
1407            //preserve the prevStateOID
1408
tmpStateOID = stateOID;
1409            stateOID = prevStateOID;
1410            prevStateOID = tmpStateOID;
1411
1412            prevRowValid = currentRowValid;
1413        }
1414
1415        rs.close();
1416        returnState |= STATUS_CLOSED;
1417        if ((returnState & STATUS_VALID_ROWS) == STATUS_VALID_ROWS) {
1418            if (updateStateFilter(struct, oidStates.get(prevStateOID))) {
1419                returnState |= STATUS_DATA_ADDED;
1420            }
1421        }
1422        return returnState;
1423    }
1424
1425    private boolean updateStateFilter(Struct struct, State state) {
1426        if (state == null) return false;
1427        if (struct.values == null) struct.values = EMPTY_OBJECT_ARRAY;
1428
1429        if (Debug.DEBUG) {
1430            if (!state.containsField(fmd.stateFieldNo)
1431                    || state.getInternalObjectField(fmd.stateFieldNo) == null) {
1432                throw BindingSupportImpl.getInstance().internal("");
1433            }
1434        }
1435
1436        if (state.getInternalObjectField(fmd.stateFieldNo) == PRE_GEN_EMPTY_OBJECT_ARRAY) {
1437            state.setInternalObjectField(fmd.stateFieldNo,
1438                    struct.asArray(getStructArrayType()));
1439            return true;
1440        }
1441        return false;
1442    }
1443
1444    private Class JavaDoc getStructArrayType() {
1445        if (valuesAreOIDs) {
1446            return OID.class;
1447        } else if (fmd.category == MDStatics.CATEGORY_ARRAY) {
1448            return fmd.componentType;
1449        } else {
1450            return Object JavaDoc.class;
1451        }
1452    }
1453
1454    private class Struct {
1455
1456        public int len;
1457
1458        public Object JavaDoc[] values;
1459
1460
1461        public int size;
1462
1463        public void init() {
1464            len = (int)(avgRowCount * FUDGE_FACTOR);
1465            if (len < MIN_LEN) len = 0;
1466            values = len == 0 ? null :
1467                    (Object JavaDoc[])
1468                    
1469                    Array.newInstance(getStructArrayType(), len);
1470            if (Debug.DEBUG) {
1471                if (((fetchCount + 1) % 10) == 0) {
1472                    System.out.println("JdbcLinkCollectionField.fetch" +
1473                            " avgRowCount = " + avgRowCount + " " +
1474                            " len = " + len + " " +
1475                            " expansionCount = " + expansionCount + " " +
1476                            " fetchCount = " + fetchCount);
1477                }
1478            }
1479            size = 0;
1480        }
1481
1482        public void add(Object JavaDoc value) {
1483            if (!fmd.ordered && value == null) {
1484                //ignore null for unordered cols.
1485
return;
1486            }
1487            //grow if nec.
1488
if (size == len) {
1489                if (len == 0) {
1490                    values =
1491                            (Object JavaDoc[])
1492                            
1493                            Array.newInstance(getStructArrayType(),
1494                                    len = MIN_LEN);
1495                } else {
1496                    len = len * 3 / 2 + 1;
1497
1498                    Object JavaDoc[] a = (Object JavaDoc[])Array.newInstance(
1499                            getStructArrayType(), len);
1500                    System.arraycopy(values, 0, a, 0, size);
1501
1502
1503                    values = a;
1504                    expansionCount++;
1505                }
1506            }
1507
1508
1509            values[size++] = value;
1510
1511
1512        }
1513
1514        /**
1515         * Return the values as an array of componentType.
1516         */

1517        public Object JavaDoc asArray(Class JavaDoc componentType) {
1518
1519            Object JavaDoc[] a = (Object JavaDoc[])Array.newInstance(componentType, size);
1520            System.arraycopy(values, 0, a, 0, size);
1521
1522
1523            return a;
1524        }
1525    }
1526
1527    public void fillStateWithEmpty(FetchGroupField field, State state) {
1528        if (!state.containsField(fmd.stateFieldNo)) {
1529            state.setInternalObjectField(fmd.stateFieldNo,
1530                    PRE_GEN_EMPTY_OBJECT_ARRAY);
1531        }
1532    }
1533
1534    /**
1535     * Fetch the values for this field.
1536     */

1537    public int fetch(JdbcStorageManager sm, OID oid, State state,
1538            FetchGroupField field, boolean forUpdate,
1539            StateContainer container, boolean fetchPass2Fields,
1540            ColFieldHolder colFHolder)
1541            throws SQLException JavaDoc {
1542        String JavaDoc sql = forUpdate ? field.jdbcSelectSqlForUpdate : field.jdbcSelectSql;
1543
1544        FgDs[] fgDses = new FgDs[1];
1545
1546        if (sql == null) {
1547            SelectExp se = getSelectExp(sm, field, fgDses);
1548
1549            CharBuf s = sm.generateSql(se);
1550            sql = s.toString();
1551            if (forUpdate) {
1552                field.jdbcSelectSqlForUpdate = sql;
1553            } else {
1554                field.jdbcSelectSql = sql;
1555            }
1556        }
1557
1558        if (valuesAreOIDs) {
1559            fgDses[0] = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getExistingFgDs(
1560                    true, field.jdbcUseJoin != JdbcField.USE_JOIN_INNER);
1561            if (colFHolder != null) {
1562                colFHolder.valueJs = fgDses[0].getJoinStruct();
1563            }
1564        }
1565
1566        final Struct struct = new Struct();
1567
1568        PreparedStatement JavaDoc ps = null;
1569        ResultSet JavaDoc rs = null;
1570        try {
1571            ps = sm.con().prepareStatement(sql);
1572            ((JdbcOID)oid).setParams(ps, 1);
1573            try {
1574                rs = ps.executeQuery();
1575            } catch (Exception JavaDoc e) {
1576                throw mapException(e,
1577                        "Fetch link table rows failed: " +
1578                        JdbcUtils.toString(e) + "\n" +
1579                        "Field: " + fmd.getTypeQName() + "\n" +
1580                        "Instance: " + oid.toSString() + "\n" +
1581                        JdbcUtils.getPreparedStatementInfo(sql, ps));
1582            }
1583
1584            if (valuesAreOIDs) {
1585                if (colFHolder != null) colFHolder.valueJs = fgDses[0].getJoinStruct();
1586                final boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO
1587                        || fmd.ordering != null;
1588                final int startIndex = 1 +
1589                        ((JdbcClass)fmd.elementTypeMetaData.storeClass).table.pkSimpleColumnCount;
1590
1591                for (; rs.next();) {
1592                    OID valueOid = fmd.elementTypeMetaData.createOID(false);
1593                    if (((JdbcOID)valueOid).copyKeyFields(rs, 1)) {
1594                        struct.add(valueOid);
1595                        if (joined && container.isStateRequired(valueOid,
1596                                field.nextFetchGroup)) {
1597                            container.addState(valueOid,
1598                                    sm.createStateImp(rs,
1599                                            valueOid, field.nextFetchGroup,
1600                                            forUpdate,
1601                                            startIndex, null, true,
1602                                            container,
1603                                            fgDses[0], fetchPass2Fields, false,
1604                                            null));
1605                        }
1606                    } else {
1607                        struct.add(null);
1608                    }
1609                }
1610            } else {
1611                for (; rs.next();) {
1612                    struct.add(
1613                            valueColumns[0].get(rs, 1 ));
1614                }
1615            }
1616
1617            // this can come out when the bugs are removed from the rest
1618
// of the code
1619
Object JavaDoc data;
1620            if (struct.values == null) {
1621
1622                data = EMPTY_OBJECT_ARRAY;
1623            } else {
1624                data = struct.asArray(getStructArrayType());
1625            }
1626            state.setInternalObjectField(fmd.stateFieldNo, data);
1627            updateStatistics(struct.size);
1628            return struct.size;
1629        } finally {
1630            cleanup(rs);
1631            cleanup(ps);
1632        }
1633    }
1634
1635    /**
1636     * Fetch the values for this field.
1637     */

1638    public int fetchFrom(ResultSet JavaDoc rs, OID oid, State state,
1639            FetchGroupField field, boolean forUpdate,
1640            StateContainer container,
1641            boolean fetchPass2Fields, int colIndex,
1642            FetchInfo fetchInfo, JdbcStorageManager sm) throws SQLException JavaDoc {
1643        final Struct struct = new Struct();
1644        if (valuesAreOIDs) {
1645            ClassMetaData valueCmd = fmd.elementTypeMetaData;
1646            boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
1647            FetchGroup nextFetchGroup = field.nextFetchGroup;
1648            int valuePkLen = 0;
1649            if (joined) valuePkLen = ((JdbcClass)valueCmd.storeClass).table.pkSimpleColumnCount;
1650            boolean first = true;
1651            for (; ;) {
1652                boolean mustBreak = false;
1653                if (first) {
1654                    first = false;
1655                    mustBreak = updateForFirstRow(fetchInfo, mustBreak, rs,
1656                            colIndex, oid);
1657                } else {
1658                    if (!rs.next()) {
1659                        fetchInfo.onNextRow = false;
1660                        fetchInfo.finished = true;
1661                        mustBreak = true;
1662                    } else {
1663                        fetchInfo.onNextRow = true;
1664                        mustBreak = checkKeyOid(rs, colIndex, fetchInfo, mustBreak,
1665                                oid);
1666                    }
1667                }
1668                if (mustBreak) break;
1669
1670                OID valueOid = valueCmd.createOID(false);
1671                if (((JdbcOID)valueOid).copyKeyFields(rs, colIndex + ourPkColumns.length)) {
1672                    struct.add(valueOid);
1673                    if (joined && container.isStateRequired(valueOid,
1674                            nextFetchGroup)) {
1675                        State valueState = sm.createStateImp(rs,
1676                                valueOid, nextFetchGroup, forUpdate,
1677                                colIndex + valuePkLen + ourPkColumns.length, null,
1678                                true, container,
1679                                ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getFgDs(true, true),
1680                                fetchPass2Fields, false, null);
1681                        container.addState(valueOid, valueState);
1682                    }
1683                } else {
1684                    struct.add(null);
1685                }
1686            }
1687        } else {
1688            boolean mustBreak = false;
1689            boolean first = true;
1690            for (; ;) {
1691                if (first) {
1692                    first = false;
1693                    mustBreak = updateForFirstRow(fetchInfo, mustBreak, rs,
1694                            colIndex, oid);
1695                } else {
1696                    if (!rs.next()) break;
1697                    fetchInfo.onNextRow = true;
1698                    mustBreak = checkKeyOid(rs, colIndex, fetchInfo, mustBreak,
1699                            oid);
1700                }
1701                if (mustBreak) break;
1702                struct.add(
1703                    valueColumns[0].get(rs, colIndex + ourPkColumns.length ));
1704            }
1705        }
1706        // this can come out when the bugs are removed from the rest
1707
// of the code
1708
Object JavaDoc data;
1709        if (struct.values == null) {
1710
1711            data = EMPTY_OBJECT_ARRAY;
1712        } else {
1713            data = struct.asArray(getStructArrayType());
1714        }
1715        state.setInternalObjectField(fmd.stateFieldNo, data);
1716        updateStatistics(struct.size);
1717        return struct.size;
1718    }
1719
1720    private void updateStatistics(int size) {
1721        // Update statistics. This is not thread safe but it is not a
1722
// problem if the avgRowCount is a bit out sometimes.
1723
int fc = ++fetchCount;
1724        if (fc > WINDOW_SIZE) {
1725            fc = WINDOW_SIZE;
1726        } else if (fc == 1) {
1727            avgRowCount = size;
1728        } else if (fc < 0) {
1729            fc = fetchCount = WINDOW_SIZE;
1730        } else {
1731            avgRowCount = (avgRowCount * (fc - 1) + size) / fc;
1732        }
1733    }
1734
1735    /**
1736     * Delete a pass 2 field for a block of graph entries all with
1737     * the same class. The same ps'es can be used for all entries in the block.
1738     */

1739    public void deletePass2Block(DeletePacket graph, int blockStart,
1740            int blockEnd, CharBuf s, Connection JavaDoc con, boolean batch)
1741            throws SQLException JavaDoc {
1742        if (readOnly) return;
1743        PreparedStatement JavaDoc ps = null;
1744        try {
1745            final int count = blockEnd - blockStart;
1746            boolean useInList = (ourPkColumns.length == 1) && (count > 1);
1747            if (!batch && !useInList) {
1748                //delete one-by-one
1749
ps = deleteOneByOne(s, con, ps, blockStart, blockEnd, graph);
1750            } else {
1751                if (useInList) {
1752                    ps = deleteWithInList(s, count, con, ps, blockStart, blockEnd, graph);
1753                } else if (batch) {
1754                    ps = deleteWithBatch(s, con, ps, blockStart, blockEnd, graph);
1755                }
1756            }
1757        } finally {
1758            cleanup(ps);
1759        }
1760    }
1761
1762    private PreparedStatement JavaDoc deleteWithBatch(CharBuf s, Connection JavaDoc con,
1763            PreparedStatement JavaDoc ps, int blockStart, int blockEnd,
1764            DeletePacket graph) throws SQLException JavaDoc {
1765        String JavaDoc sql = getDeleteAllLinkTableRowsSql(s);
1766        ps = con.prepareStatement(sql);
1767        for (int pos = blockStart; pos < blockEnd; pos++) {
1768            ((JdbcOID)graph.oids[pos]).setParams(ps, 1);
1769            ps.addBatch();
1770        }
1771        try {
1772            ps.executeBatch();
1773        } catch (Exception JavaDoc e) {
1774            throw mapException(e,
1775                    "Batch delete link table rows failed: " +
1776                    JdbcUtils.toString(e) + "\n" +
1777                    "Field: " + fmd.getTypeQName() + "\n" +
1778                    JdbcUtils.getPreparedStatementInfo(sql, ps));
1779        }
1780        return ps;
1781    }
1782
1783    private PreparedStatement JavaDoc deleteWithInList(CharBuf s, int count,
1784            Connection JavaDoc con, PreparedStatement JavaDoc ps, int blockStart, int blockEnd,
1785            DeletePacket graph) throws SQLException JavaDoc {
1786        SqlDriver driver = getSqlDriver();
1787
1788        if (count > driver.getMaxInOperands()) {
1789            //must break it up
1790
final int maxInOps = driver.getMaxInOperands();
1791            int amountOfFullRuns = count/driver.getMaxInOperands();
1792
1793            final int amountLeft = count % driver.getMaxInOperands();;
1794            String JavaDoc sql = null;
1795            int pos = blockStart;
1796            final String JavaDoc param = driver.getSqlParamString(ourPkColumns[0].jdbcType);
1797            getDeleteAllLinkTableRowsSqlWithInList(s);
1798            if (amountLeft > 0) {
1799                for (int i = 0; i < amountLeft; i++) {
1800                    if (i != 0) s.append(",");
1801                    s.append(param);
1802                }
1803                s.append(")");
1804
1805                sql = s.toString();
1806                ps = con.prepareStatement(sql);
1807                for (int curIndex = 0; curIndex < amountLeft; curIndex++, pos++) {
1808                    ((JdbcOID)graph.oids[pos]).setParams(ps, curIndex + 1);
1809                }
1810                try {
1811                    ps.execute();
1812                } catch (Exception JavaDoc e) {
1813                    throw mapException(e,
1814                            "Batch delete link table rows failed: " +
1815                            JdbcUtils.toString(e) + "\n" +
1816                            "Field: " + fmd.getTypeQName() + "\n" +
1817                            JdbcUtils.getPreparedStatementInfo(sql, ps));
1818                }
1819                s.set(s.size() - 1, ',');
1820            }
1821
1822            for (int i = 0; i < maxInOps - amountLeft; i++) {
1823                if (i != 0) s.append(",");
1824                s.append(param);
1825            }
1826            s.append(")");
1827
1828            sql = s.toString();
1829            ps = con.prepareStatement(sql);
1830            for (int i = 0; i < amountOfFullRuns; i ++) {
1831                for (int curIndex = 0; curIndex < maxInOps; curIndex++, pos++) {
1832                    ((JdbcOID)graph.oids[pos]).setParams(ps, curIndex + 1);
1833                }
1834                try {
1835                    ps.execute();
1836                } catch (Exception JavaDoc e) {
1837                    throw mapException(e,
1838                            "Batch delete link table rows failed: " +
1839                            JdbcUtils.toString(e) + "\n" +
1840                            "Field: " + fmd.getTypeQName() + "\n" +
1841                            JdbcUtils.getPreparedStatementInfo(sql, ps));
1842                }
1843            }
1844        } else {
1845            getDeleteAllLinkTableRowsSqlWithInList(s);
1846            for (int i = 0; i < count; i++) {
1847                if (i != 0) s.append(",");
1848                driver.appendWhereParam(s, ourPkColumns[0]);
1849            }
1850            s.append(")");
1851            String JavaDoc sql = s.toString();
1852            ps = con.prepareStatement(sql);
1853
1854            int index = 1;
1855            for (int pos = blockStart; pos < blockEnd; pos++) {
1856                ((JdbcOID)graph.oids[pos]).setParams(ps, index++);
1857            }
1858            try {
1859                ps.execute();
1860            } catch (Exception JavaDoc e) {
1861                throw mapException(e,
1862                        "Batch delete link table rows failed: " +
1863                        JdbcUtils.toString(e) + "\n" +
1864                        "Field: " + fmd.getTypeQName() + "\n" +
1865                        JdbcUtils.getPreparedStatementInfo(sql, ps));
1866            }
1867        }
1868        return ps;
1869    }
1870
1871    private PreparedStatement JavaDoc deleteOneByOne(CharBuf s, Connection JavaDoc con,
1872            PreparedStatement JavaDoc ps, int blockStart, int blockEnd,
1873            DeletePacket graph) throws SQLException JavaDoc {
1874        String JavaDoc sql = getDeleteAllLinkTableRowsSql(s);
1875        ps = con.prepareStatement(sql);
1876        for (int pos = blockStart; pos < blockEnd; pos++) {
1877            ((JdbcOID)graph.oids[pos]).setParams(ps, 1);
1878            try {
1879                ps.execute();
1880            } catch (Exception JavaDoc e) {
1881                throw mapException(e,
1882                        "Delete link table rows failed: " +
1883                        JdbcUtils.toString(e) + "\n" +
1884                        "Field: " + fmd.getTypeQName() + "\n" +
1885                        "Instance: " + graph.oids[pos].toSString() + "\n" +
1886                        JdbcUtils.getPreparedStatementInfo(sql, ps));
1887            }
1888        }
1889        return ps;
1890    }
1891
1892    /**
1893     * Convert this field into an isEmpty expression.
1894     */

1895    public SqlExp toIsEmptySqlExp(JdbcJDOQLCompiler comp, SelectExp root) {
1896        SelectExp se = new SelectExp();
1897        se.table = linkTable;
1898        se.jdbcField = this;
1899        se.subSelectJoinExp = root.createJoinExp(root.table.pk, ourPkColumns,
1900                se);
1901        return new UnaryOpExp(new ExistsExp(se, true), UnaryOpExp.OP_NOT);
1902    }
1903
1904    /**
1905     * Convert this field into a contains expression.
1906     */

1907    public SqlExp toContainsSqlExp(JdbcJDOQLCompiler comp, SelectExp root,
1908            Node args) {
1909        return toContainsSqlExp(valueColumns, fmd.elementTypeMetaData, comp,
1910                root, args);
1911    }
1912
1913    protected SqlExp toContainsSqlExp(JdbcColumn[] cols, ClassMetaData colsCmd,
1914            JdbcJDOQLCompiler comp, SelectExp root, Node args) {
1915        SelectExp se = new SelectExp();
1916        se.table = linkTable;
1917        se.jdbcField = this;
1918        se.subSelectJoinExp = root.createJoinExp(root.table.pk, ourPkColumns,
1919                se);
1920
1921        if (args instanceof VarNodeIF) {
1922            VarNode v = ((VarNodeIF)args).getVarNode();
1923            if (v.getCmd() == null) {
1924                SelectExp storeExtent = new SelectExp();
1925                storeExtent.table = linkTable;
1926                storeExtent.var = v;
1927                v.setStoreExtent(storeExtent);
1928                v.setFieldExtent(se);
1929                v.setFmd(fmd);
1930                se.var = v;
1931            } else {
1932                SelectExp vse = (SelectExp)v.getStoreExtent();
1933                se.addJoin(cols, vse.table.pk, vse);
1934                se.var = v;
1935            }
1936        } else {
1937            SqlExp left = JdbcColumn.toSqlExp(cols, se);
1938            if (colsCmd != null) {
1939                for (SqlExp e = left; e != null; e = e.next) {
1940                    ((ColumnExp)e).cmd = colsCmd;
1941                }
1942            }
1943            SqlExp right = comp.getVisitor().toSqlExp(args, root, left, 0, null);
1944            se.whereExp = SqlExp.createBinaryOpExp(left, BinaryOpExp.EQUAL,
1945                    right);
1946        }
1947        return new ExistsExp(se, true, (args instanceof VarNodeIF ? (VarNodeIF)args : null));
1948    }
1949
1950    /**
1951     * Process meta data for an inverse many-to-many collection.
1952     */

1953    private void processInverse(JdbcMetaDataBuilder mdb, JdoExtension e) {
1954        ClassMetaData ecmd = fmd.elementTypeMetaData;
1955        if (ecmd.storeClass == null) {
1956            throw BindingSupportImpl.getInstance().runtime("The inverse extension may only be used for " +
1957                    "collections of PC instances stored by JDBC\n" +
1958                    e.getContext());
1959        }
1960        String JavaDoc fname = e.getString();
1961        FieldMetaData f = ecmd.getFieldMetaData(fname);
1962        if (f.storeField == null) {
1963            throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' is not persistent\n" +
1964                    e.getContext());
1965        }
1966        if (!(f.storeField instanceof JdbcLinkCollectionField)) {
1967            throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' is not a collection or array mapped " +
1968                    "using a link table\n" + e.getContext());
1969        }
1970        inverse = (JdbcLinkCollectionField)f.storeField;
1971        if (f.elementTypeMetaData != fmd.classMetaData) {
1972            throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' contains " +
1973                    f.elementTypeMetaData + " and not our class\n" +
1974                    e.getContext());
1975        }
1976        if (inverse == this) {
1977            throw BindingSupportImpl.getInstance().runtime("Field '" + fname +
1978                    "' may not be in a many-to-many with itself\n" +
1979                    e.getContext());
1980        }
1981        inverse.inverse = this;
1982        readOnly = true;
1983        valuesAreOIDs = true;
1984        fmd.ordered = false;
1985        fmd.isManyToMany = true;
1986        fmd.isReadOnly = true;
1987        fmd.inverseFieldMetaData = inverse.fmd;
1988        inverse.fmd.isManyToMany = true;
1989        inverse.fmd.inverseFieldMetaData = fmd;
1990        syncWithInverse(mdb);
1991    }
1992
1993    /**
1994     * Sync our mapping info with our inverse. This will get called twice:
1995     * once for each side of the many-to-many.
1996     * @param mdb
1997     */

1998    private void syncWithInverse(JdbcMetaDataBuilder mdb) {
1999        if (inverse == null) return; // not inverse or inverse side not done
2000
if (readOnly) {
2001            linkTable = inverse.linkTable;
2002            if (linkTable != null) { // main side has been done
2003
valueColumns = inverse.ourPkColumns;
2004                ourPkColumns = inverse.valueColumns;
2005                sequenceColumn = inverse.sequenceColumn;
2006                createInverseIndex(mdb);
2007            }
2008            fmd.managed = inverse.fmd.managed;
2009        } else {
2010            inverse.syncWithInverse(mdb);
2011        }
2012    }
2013
2014    /**
2015     * For inverse collections create an index on our primary key fields
2016     * in the link table of the main collection unless this has been disabled.
2017     * @param mdb
2018     */

2019    private void createInverseIndex(JdbcMetaDataBuilder mdb) {
2020        JdoExtension ext = JdoExtension.find(JdoExtensionKeys.INVERSE,
2021                getExtensions());
2022        JdoExtension[] nested = ext.nested;
2023
2024        JdbcIndex idx = null;
2025        boolean doNotCreateIndex = false;
2026
2027        int n = nested == null ? 0 : nested.length;
2028        for (int i = 0; i < n; i++) {
2029            JdoExtension e = nested[i];
2030            switch (e.key) {
2031                case JdoExtensionKeys.JDBC_INDEX:
2032                    if (idx != null) {
2033                        throw BindingSupportImpl.getInstance().runtime("Only one jdbc-index extension is allowed here\n" +
2034                                e.getContext());
2035                    }
2036                    if (e.isNoValue()) {
2037                        doNotCreateIndex = true;
2038                        break;
2039                    }
2040                    idx = new JdbcIndex();
2041                    idx.name = e.value;
2042                    break;
2043                default:
2044                    if (e.isJdbc()) {
2045                        MetaDataBuilder.throwUnexpectedExtension(e);
2046                    }
2047            }
2048        }
2049
2050        if (doNotCreateIndex) return;
2051        if (idx == null) idx = new JdbcIndex();
2052        idx.setCols(ourPkColumns);
2053
2054        // register the name of the index if one was specified otherwise one
2055
// will be generated later along with user specified indexes
2056
if (idx.name != null) {
2057            try {
2058                mdb.getNameGenerator().addIndexName(linkTable.name,
2059                        idx.name);
2060            } catch (IllegalArgumentException JavaDoc x) {
2061                throw BindingSupportImpl.getInstance().runtime(x.getMessage(),
2062                        x);
2063            }
2064        }
2065
2066        if (linkTable.indexes != null) {
2067            throw BindingSupportImpl.getInstance().internal("Link table already has index: "
2068                    + linkTable + ", " + fmd.getTypeQName());
2069        }
2070        linkTable.indexes = new JdbcIndex[]{idx};
2071    }
2072
2073    /**
2074     * Make sure all the indexes on our link tables (if any) have names,
2075     */

2076    public void nameLinkTableIndexes(JdbcNameGenerator namegen) {
2077        if (readOnly) return;
2078        int n = linkTable == null ? 0 : linkTable.indexes == null ? 0 : linkTable.indexes.length;
2079        for (int i = 0; i < n; i++) {
2080            JdbcIndex idx = linkTable.indexes[i];
2081            if (idx.name == null) {
2082                JdbcMetaDataBuilder.generateNameForIndex(namegen,
2083                        linkTable.name, idx);
2084            }
2085        }
2086    }
2087
2088    /**
2089     * Return SQL that will fetch all the rows in the link table.
2090     * This is used when bulk copying one database to another. The OID of
2091     * the owning table must be first followed by the other columns in the
2092     * order expected by readRow.
2093     *
2094     * @see #readRow
2095     */

2096    public String JavaDoc getFetchAllRowsSql(JdbcStorageManager sm) throws SQLException JavaDoc {
2097        SelectExp root = new SelectExp();
2098        root.table = linkTable;
2099        SqlExp e = root.selectList = JdbcColumn.toSqlExp(ourPkColumns, root);
2100        for (; e.next != null; e = e.next) ;
2101        if (fmd.ordered) e = e.next = sequenceColumn.toSqlExp(root);
2102        e = e.next = JdbcColumn.toSqlExp(valueColumns, root);
2103        addFetchAllRowsKey(e, root);
2104        return sm.generateSql(root).toString();
2105    }
2106
2107    /**
2108     * Hook for JdbcMapField to add its key columns to the row. The returned
2109     * SqlExp must be the last one in the list.
2110     */

2111    protected void addFetchAllRowsKey(SqlExp e, SelectExp se) {
2112    }
2113
2114    /**
2115     * Fetch a row of values for this field. This is used when bulk copying
2116     * one database to another to read all the rows in a given link table.
2117     * Return the index of the last column read + 1.
2118     */

2119    public int readRow(ResultSet JavaDoc rs, LinkRow row) throws SQLException JavaDoc {
2120        row.owner = (JdbcGenericOID)fmd.classMetaData.createOID(false);
2121        row.owner.copyKeyFields(rs, 1);
2122        int pos = ourPkColumns.length + 1;
2123        if (fmd.ordered) {
2124            row.seq = ((Integer JavaDoc)sequenceColumn.get(rs, pos++)).intValue();
2125        }
2126        if (valuesAreOIDs) {
2127            OID valueOid = fmd.elementTypeMetaData.createOID(false);
2128            ((JdbcOID)valueOid).copyKeyFields(rs, pos);
2129            row.value = valueOid;
2130            pos += valueColumns.length;
2131        } else {
2132            row.value = valueColumns[0].get(rs, pos++);
2133        }
2134        return pos;
2135    }
2136
2137    /**
2138     * Set a row of values for this field on a PreparedStatement.
2139     * This is used when bulk copying one database to another.
2140     */

2141    public void writeRow(PreparedStatement JavaDoc ps, LinkRow row)
2142            throws SQLException JavaDoc {
2143        row.owner.setCmd(fmd.classMetaData);
2144        int pos = row.owner.setParams(ps, 1);
2145        if (fmd.ordered) sequenceColumn.set(ps, pos++, row.seq);
2146        if (valuesAreOIDs) {
2147            JdbcGenericOID v = (JdbcGenericOID)row.value;
2148            v.setCmd(fmd.classMetaData);
2149            v.setParams(ps, pos);
2150        } else {
2151            valueColumns[0].set(ps, pos, row.value);
2152        }
2153    }
2154
2155    /**
2156     * A row from our link table. This is used by the bulk database copying
2157     * operations.
2158     */

2159    public static class LinkRow {
2160
2161        public JdbcGenericOID owner;
2162        public int seq;
2163        public Object JavaDoc key;
2164        public Object JavaDoc value;
2165    }
2166
2167}
2168
Popular Tags