KickJava   Java API By Example, From Geeks To Geeks.

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


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.jdbc.sql.JdbcNameGenerator;
15 import com.versant.core.jdbc.sql.exp.*;
16 import com.versant.core.jdbc.*;
17 import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
18 import com.versant.core.server.*;
19 import com.versant.core.metadata.parser.JdoElement;
20 import com.versant.core.metadata.parser.JdoExtension;
21 import com.versant.core.metadata.parser.JdoExtensionKeys;
22 import com.versant.core.metadata.*;
23 import com.versant.core.common.OID;
24 import com.versant.core.common.State;
25 import com.versant.core.common.Utils;
26 import com.versant.core.util.CharBuf;
27 import com.versant.core.common.*;
28 import com.versant.core.jdo.query.Node;
29 import com.versant.core.jdo.query.OrderNode;
30 import com.versant.core.common.Debug;
31
32 import java.io.PrintStream JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.sql.*;
37
38 import com.versant.core.common.BindingSupportImpl;
39
40 /**
41  * This is a Map field stored using a link table.
42  */

43 public class JdbcMapField extends JdbcLinkCollectionField {
44
45     private static final Object JavaDoc[] EMPTY_OBJECT_ARRAY = new Object JavaDoc[0];
46     private static final OID[] EMPTY_OID_ARRAY = new OID[0];
47
48     /**
49      * The column(s) holding the keys. This array will have length 1 unless
50      * the keys are of a PC class with a composite primary key.
51      */

52     public JdbcColumn keyColumns[];
53     /**
54      * Should the keys be considered a dependent? If they are
55      * they will be deleted if removed from the map or if our
56      * instance is deleted. This only makes sense if the keys are instances
57      * of a PC class.
58      */

59     public boolean keysDependent;
60     /**
61      * Are the keys OID's?
62      */

63     public boolean keysAreOIDs;
64     /**
65      * Should a join be done to pick up the fields for keys when they are
66      * read? This only makes sense if the key is a PC class.
67      */

68     public int useKeyJoin;
69
70     private transient boolean createKeyConstraint;
71     private transient String JavaDoc keyConstraintName;
72
73     public void dump(PrintStream JavaDoc out, String JavaDoc indent) {
74         super.dump(out, indent);
75         String JavaDoc is = indent + " ";
76         if (keyColumns == null) {
77             out.println(is + "keyColumns null");
78         } else {
79             for (int i = 0; i < keyColumns.length; i++) {
80                 out.println(is + "keyColumns[" + i + "] " + keyColumns[i]);
81             }
82         }
83         out.println(is + "keysDependent " + keysDependent);
84         out.println(is + "keysAreOIDs " + keysAreOIDs);
85         out.println(is + "useKeyJoin " + toUseJoinString(useKeyJoin));
86     }
87
88     /**
89      * Get the useKeyJoin value for this field. This is only valid for maps.
90      */

91     public int getUseKeyJoin() {
92         return useKeyJoin;
93     }
94
95     /**
96      * Complete the meta data for this collection. This must use info
97      * already supplied in the .jdo file and add anything else needed.
98      */

99     public void processMetaData(JdoElement context, JdbcMetaDataBuilder mdb,
100             boolean quiet) {
101         keysAreOIDs = fmd.keyTypeMetaData != null;
102         if (keysAreOIDs) {
103             useKeyJoin = JdbcField.USE_JOIN_INNER;
104         } else {
105             useKeyJoin = JdbcField.USE_JOIN_NO;
106         }
107         super.processMetaData(context, mdb, quiet);
108     }
109
110     /**
111      * Set the PK of the link table.
112      */

113     protected void createLinkTablePK() {
114         linkTable.setPk(JdbcColumn.concat(ourPkColumns, keyColumns));
115     }
116
117     /**
118      * Complete the key and value column related meta data.
119      */

120     protected void completeKeyAndValueColumnMetaData(JdbcClass jdbcClass,
121             ArrayList JavaDoc cols,
122             JdoElement context, JdoExtension[] linkNested,
123             JdbcMetaDataBuilder mdb, boolean quiet) {
124
125         // create the key column(s)
126
JdoExtension ext = JdoExtension.find(JdoExtensionKeys.JDBC_KEY,
127                 linkNested);
128         if (fmd.keyTypeMetaData != null) { // values are OIDs
129
JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(
130                     fmd.classMetaData,
131                     mdb, fmd.keyTypeMetaData, context,
132                     JdbcMetaDataBuilder.KEY_FIELDNAME,
133                     ext == null ? null : ext.nested, quiet);
134             keyColumns = rdb.getCols();
135             cols.addAll(rdb.getColsList());
136             createKeyConstraint = !rdb.isDoNotCreateConstraint();
137             keyConstraintName = rdb.getConstraintName();
138         } else {
139             if (fmd.keyType == Object JavaDoc.class) {
140                 throw BindingSupportImpl.getInstance().runtime("You must specify the key-type for maps\n" +
141                         fmd + "\n" + context.getContext());
142             }
143             JdbcColumn kc = mdb.createColumn(ext == null ? null : ext.nested,
144                     JdbcMetaDataBuilder.KEY_FIELDNAME, fmd.keyType);
145             keyColumns = new JdbcColumn[]{kc};
146             cols.add(kc);
147         }
148
149         int n = keyColumns.length;
150         for (int i = 0; i < n; i++) keyColumns[i].setNulls(false);
151
152         super.completeKeyAndValueColumnMetaData(jdbcClass, cols, context,
153                 linkNested, mdb, quiet);
154     }
155
156     /**
157      * Name the key and value columns.
158      */

159     protected void nameKeyAndValueColumns(JdbcNameGenerator namegen,
160             String JavaDoc linkTableNameForNamegen) {
161         // name the keycolumn(s)
162
if (keysAreOIDs) {
163             String JavaDoc[] keyPkNames = JdbcColumn.getColumnNames(
164                     ((JdbcClass)fmd.keyTypeMetaData.storeClass).table.pk);
165             String JavaDoc[] linkKeyRefNames = JdbcColumn.getColumnNames(keyColumns);
166             namegen.generateLinkTableValueRefNames(linkTable.name,
167                     keyPkNames, fmd.keyType.getName(), linkKeyRefNames, true);
168             JdbcColumn.setColumnNames(keyColumns, linkKeyRefNames);
169         } else {
170             JdbcColumn c = keyColumns[0];
171             if (c.name == null) {
172                 c.name = namegen.generateLinkTableValueName(linkTable.name,
173                         fmd.keyType, true);
174             }
175         }
176
177         super.nameKeyAndValueColumns(namegen, linkTableNameForNamegen);
178     }
179
180     /**
181      * Name our linkTable.
182      */

183     protected void nameLinkTable(JdbcNameGenerator namegen,
184             JdbcClass jdbcClass) {
185         linkTable.name = namegen.generateLinkTableName(jdbcClass.table.name,
186                 fmd.name, null);
187     }
188
189     /**
190      * Create all the constraints for our link table.
191      */

192     protected List JavaDoc createConstraints(boolean pkConstraint,
193             String JavaDoc pkConstraintName) {
194         List JavaDoc constraints = super.createConstraints(pkConstraint,
195                 pkConstraintName);
196
197         if (createKeyConstraint && keysAreOIDs
198                 && fmd.keyTypeMetaData.storeClass != null) {
199             JdbcConstraint keyCon = new JdbcConstraint();
200             keyCon.src = linkTable;
201             keyCon.srcCols = keyColumns;
202             keyCon.dest = ((JdbcClass)fmd.keyTypeMetaData.storeClass).table;
203             keyCon.name = keyConstraintName;
204             constraints.add(keyCon);
205         }
206
207         return constraints;
208     }
209
210     /**
211      * Persist pass 2 field for a block of graph entries all with
212      * the same class. The same ps'es can be used for all entries in the block.
213      */

214     public void persistPass2Block(PersistGraph graph, int blockStart,
215             int blockEnd, CharBuf s, Connection con, boolean batchInserts,
216             boolean batchUpdates) throws SQLException {
217         PreparedStatement psdel = null;
218         PreparedStatement psdelAll = null;
219         PreparedStatement psins = null;
220         int delCount = 0;
221         try {
222             String JavaDoc psdelSql = null;
223             String JavaDoc psdelAllSql = null;
224             String JavaDoc psinsSql = null;
225             for (int pos = blockStart; pos < blockEnd; pos++) {
226                 State ns = graph.getNewState(pos);
227                 if (!ns.containsField(stateFieldNo)) continue;
228
229                 MapDiff diff = (MapDiff)ns.getInternalObjectField(stateFieldNo);
230
231                 OID oid = graph.getOID(pos);
232
233                 if (diff == null || diff.status == CollectionDiff.STATUS_NEW) {
234                     if (!oid.isNew()) {
235                         if (psdelAll == null) {
236                             psdelAllSql = getDeleteAllLinkTableRowsSql(s);
237                             psdelAll = con.prepareStatement(psdelAllSql);
238                         }
239                         ((JdbcOID)oid).setParams(psdelAll, 1);
240                         if (batchUpdates) {
241                             psdelAll.addBatch();
242                         } else {
243                             try {
244                                 psdelAll.execute();
245                             } catch (Exception JavaDoc e) {
246                                 throw mapException(e,
247                                         "Delete all link table rows failed: " +
248                                         JdbcUtils.toString(e) + "\n" +
249                                         "Field: " + fmd.getQName() + "\n" +
250                                         "Instance: " + oid.toSString() + "\n" +
251                                         JdbcUtils.getPreparedStatementInfo(
252                                                 psdelAllSql, psdelAll));
253                             }
254                         }
255                     }
256                 } else {
257                     Object JavaDoc[] deleted = diff.deletedKeys;
258                     if (deleted != null && deleted.length > 0) {
259                         if (psdel == null) {
260                             psdelSql = getDeleteLinkTableRowSql(s);
261                             psdel = con.prepareStatement(psdelSql);
262                         }
263                         deleteMapLinkTableRows(oid, deleted, psdel,
264                                 batchUpdates, psdelSql);
265                         delCount += deleted.length;
266                     }
267                 }
268
269                 if (diff != null) {
270                     Object JavaDoc[] inserted = diff.insertedKeys;
271                     if (inserted != null && inserted.length > 0) {
272                         if (psins == null) {
273                             psinsSql = getInsertLinkTableRowSql(s);
274                             psins = con.prepareStatement(psinsSql);
275                         }
276                         insertMapLinkTableRows(oid, inserted, diff.insertedValues,
277                                 psins, batchInserts, psinsSql);
278                     }
279                 }
280             }
281
282             if (batchUpdates) {
283                 execLinkTableBatchDeletes(delCount, psdel, psdelSql,
284                         psdelAll, psdelAllSql);
285             }
286             if (batchInserts && psins != null) {
287                 execLinkTableBatchInserts(psins, psinsSql);
288             }
289         } finally {
290             cleanup(psdel);
291             cleanup(psdelAll);
292             cleanup(psins);
293         }
294     }
295
296     /**
297      * Delete keys from an map link table.
298      */

299     private void deleteMapLinkTableRows(OID oid, Object JavaDoc[] deleted,
300             PreparedStatement psdel, boolean batch, String JavaDoc sql)
301             throws SQLException {
302         if (keysAreOIDs) {
303             for (int j = deleted.length - 1; j >= 0; j--) {
304                 int pp = ((JdbcOID)oid).setParams(psdel, 1);
305                 ((JdbcOID)deleted[j]).setParams(psdel, pp);
306                 if (batch) {
307                     psdel.addBatch();
308                 } else {
309                     int uc;
310                     try {
311                         uc = psdel.executeUpdate();
312                     } catch (Exception JavaDoc e) {
313                         throw mapException(e,
314                                 "Delete map link table row failed: " +
315                                 JdbcUtils.toString(e) + "\n" +
316                                 "Field: " + fmd.getTypeQName() + "\n" +
317                                 "Key: " + ((OID)deleted[j]).toSString() + "\n" +
318                                 "Instance: " + oid.toSString() + "\n" +
319                                 JdbcUtils.getPreparedStatementInfo(sql, psdel));
320                     }
321                     if (uc == 0) {
322                         throw BindingSupportImpl.getInstance().concurrentUpdate("Map link table row not found: " +
323                                 "Field: " + fmd.getTypeQName() + "\n" +
324                                 "Key: " + ((OID)deleted[j]).toSString() + "\n" +
325                                 "Instance: " + oid.toSString() + "\n" +
326                                 JdbcUtils.getPreparedStatementInfo(sql, psdel), deleted[j]);
327                     }
328                 }
329             }
330         } else {
331             JdbcColumn kc = keyColumns[0];
332             for (int j = deleted.length - 1; j >= 0; j--) {
333                 int pp = ((JdbcOID)oid).setParams(psdel, 1);
334                 kc.set(psdel, pp, deleted[j]);
335                 if (batch) {
336                     psdel.addBatch();
337                 } else {
338                     int uc;
339                     try {
340                         uc = psdel.executeUpdate();
341                     } catch (Exception JavaDoc e) {
342                         throw mapException(e,
343                                 "Delete map link table row failed: " +
344                                 JdbcUtils.toString(e) + "\n" +
345                                 "Field: " + fmd.getTypeQName() + "\n" +
346                                 "Key: " + Utils.toString(deleted[j]) + "\n" +
347                                 "Instance: " + oid.toSString() + "\n" +
348                                 JdbcUtils.getPreparedStatementInfo(sql, psdel));
349                     }
350                     if (uc == 0) {
351                         throw BindingSupportImpl.getInstance().concurrentUpdate("Map link table row not found: " +
352                                 "Field: " + fmd.getTypeQName() + "\n" +
353                                 "Key: " + Utils.toString(deleted[j]) + "\n" +
354                                 "Instance: " + oid.toSString() + "\n" +
355                                 JdbcUtils.getPreparedStatementInfo(sql, psdel), oid);
356                     }
357                 }
358             }
359         }
360     }
361
362     /**
363      * Insert rows into an map link table.
364      */

365     private void insertMapLinkTableRows(OID oid, Object JavaDoc[] insertedKeys,
366             Object JavaDoc[] insertedValues, PreparedStatement psins,
367             boolean batch, String JavaDoc sql)
368             throws SQLException {
369
370         JdbcColumn kc = keyColumns[0];
371         JdbcColumn vc = valueColumns[0];
372
373         // do the inserts
374
int ilen = insertedKeys.length;
375         for (int j = 0; j < ilen; j++) {
376             int pp = ((JdbcOID)oid).setParams(psins, 1);
377
378             // set key
379
if (keysAreOIDs) {
380                 pp = ((JdbcOID)insertedKeys[j]).setParams(psins, pp);
381             } else {
382                 kc.set(psins, pp++, insertedKeys[j]);
383             }
384
385             // set value
386
if (valuesAreOIDs) {
387                 if (insertedValues[j] == null) {
388                     JdbcGenericOID.setNullParams(psins, pp, fmd.elementTypeMetaData);
389                 } else {
390                     ((JdbcOID)insertedValues[j]).setParams(psins, pp);
391                 }
392             } else {
393                 vc.set(psins, pp, insertedValues[j]);
394             }
395
396             if (batch) {
397                 psins.addBatch();
398             } else {
399                 try {
400                     psins.execute();
401                 } catch (Exception JavaDoc e) {
402                     String JavaDoc keyStr = keysAreOIDs
403                             ? ((OID)insertedKeys[j]).toSString()
404                             : Utils.toString(insertedKeys[j]);
405                     String JavaDoc valueStr = valuesAreOIDs
406                             ? ((OID)insertedValues[j]).toSString()
407                             : Utils.toString(insertedValues[j]);
408                     throw mapException(e,
409                             "Insert link table row failed: " +
410                             JdbcUtils.toString(e) + "\n" +
411                             "Field: " + fmd.getQName() + "\n" +
412                             "Instance: " + oid.toSString() + "\n" +
413                             "Key: " + keyStr + "\n" +
414                             "Value: " + valueStr + "\n" +
415                             JdbcUtils.getPreparedStatementInfo(sql, psins));
416                 }
417             }
418         }
419     }
420
421     /**
422      * Get a SelectExp to select all the rows in this map using the
423      * supplied fetch group field to control joins and so on.
424      */

425     public SelectExp getSelectExp(JdbcStorageManager dataStore, FetchGroupField field,
426             FgDs[] fgDses) {
427         SelectExp root = super.getSelectExp(dataStore, field, fgDses);
428
429         // prepend our key columns to the select list
430
SqlExp e = JdbcColumn.toSqlExp(keyColumns, root, root.selectList);
431         root.selectList = e;
432
433         // add a join for the keys if required
434
if (keysAreOIDs) {
435             if (field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO) {
436                 SelectExp se = new SelectExp();
437                 JdbcClass keyJdbcClass = (JdbcClass)fmd.keyTypeMetaData.storeClass;
438                 se.table = keyJdbcClass.table;
439                 se.outer = field.jdbcUseKeyJoin == JdbcField.USE_JOIN_OUTER;
440                 dataStore.addSelectFetchGroup(se, field.nextKeyFetchGroup, true,
441                         fgDses[1] = ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getFgDs(
442                                 true, se.outer),
443                         false);
444                 root.addJoin(keyColumns, se.table.pk, se);
445             }
446         }
447         return root;
448     }
449
450     public SelectExp getSelectFilterJoinExp(boolean value, SelectExp lhSe,
451             SelectExp rootSe, boolean addRootJoin) {
452         SelectExp root = new SelectExp();
453         root.table = linkTable;
454
455         SelectExp se = new SelectExp();
456         if (value) {
457             se.table = ((JdbcClass)fmd.elementTypeMetaData.storeClass).table;
458             root.addJoin(valueColumns, se.table.pk, se);
459         } else {
460             se.table = ((JdbcClass)fmd.keyTypeMetaData.storeClass).table;
461             root.addJoin(keyColumns, se.table.pk, se);
462         }
463
464         lhSe.addJoin(lhSe.table.pk, ourPkColumns, root);
465         return se;
466     }
467
468     public SelectExp getSelectFilterExp(JdbcStorageManager sm, FetchGroupField field,
469             ColFieldHolder colFHolder) {
470         SelectExp root = new SelectExp();
471         root.table = linkTable;
472
473         // add value columns to the select list
474
SqlExp e = JdbcColumn.toSqlExp(valueColumns, root);
475         root.selectList = e;
476
477         // prepend our key columns to the select list
478
e = JdbcColumn.toSqlExp(keyColumns, root, root.selectList);
479         root.selectList = e;
480
481         // prepend our pk columns to the select list
482
e = JdbcColumn.toSqlExp(ourPkColumns, root, root.selectList);
483         root.selectList = e;
484         //add the order by to the owner
485
root.appendOrderByForColumns(ourPkColumns);
486
487         if (valuesAreOIDs) {
488             if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO || fmd.ordering != null) {
489                 SelectExp se = new SelectExp();
490                 JdbcClass valueJdbcClass = (JdbcClass)fmd.elementTypeMetaData.storeClass;
491                 se.table = valueJdbcClass.table;
492                 se.outer = field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER;
493                 FgDs fgDs = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, se.outer);
494                 sm.addSelectFetchGroup(se, field.nextFetchGroup, true,
495                         fgDs, false);
496                 colFHolder.valueJs = fgDs.getJoinStruct();
497
498                 root.addJoin(valueColumns, se.table.pk, se);
499
500                 if (fmd.ordering != null) {
501                     se.addOrderBy(fmd.ordering, false);
502                     root.appendOrderByExp(se.orderByList);
503                     se.orderByList = null;
504                 }
505             }
506         } else {
507             if (fmd.ordering != null) {
508                 // the ordering can only be 'this ascending' or 'this descending'
509
boolean desc = fmd.ordering[0].order == OrderNode.ORDER_DESCENDING;
510                 root.appendOrderByExp(new OrderExp(
511                         valueColumns[0].toSqlExp(root),
512                         desc));
513                 // there will be only one entry in valueColumns as this is
514
// not a collection of PC instances
515
}
516         }
517
518         if (fmd.ordered) {
519             if (fmd.ordering != null) {
520                 throw BindingSupportImpl.getInstance().internal(
521                         "ordered == true && ordering != null, " + fmd.getTypeQName());
522             }
523             root.appendOrderByForColumns(linkTable.pkSimpleCols);
524         }
525
526         // add a join for the keys if required
527
if (keysAreOIDs) {
528             if (field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO) {
529                 SelectExp se = new SelectExp();
530                 JdbcClass keyJdbcClass = (JdbcClass)fmd.keyTypeMetaData.storeClass;
531                 se.table = keyJdbcClass.table;
532                 se.outer = field.jdbcUseKeyJoin == JdbcField.USE_JOIN_OUTER;
533
534                 FgDs fgDs = ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getFgDs(true, se.outer);
535                 sm.addSelectFetchGroup(se, field.nextKeyFetchGroup, true,
536                         fgDs, false);
537                 colFHolder.keyJs = fgDs.getJoinStruct();
538                 root.addJoin(keyColumns, se.table.pk, se);
539             }
540         }
541         return root;
542     }
543
544     public int fetchFrom(ResultSet rs, OID oid,
545             State state, FetchGroupField field, boolean forUpdate,
546             StateContainer container,
547             boolean fetchPass2Fields, int colIndex,
548             FetchInfo fetchInfo, JdbcStorageManager sm) throws SQLException {
549         ArrayList JavaDoc keys = new ArrayList JavaDoc();
550         ArrayList JavaDoc values = new ArrayList JavaDoc();
551
552         // get info to extract the keys
553
ClassMetaData keyCmd = fmd.keyTypeMetaData;
554         boolean keyJoined = field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO;
555         FetchGroup keyNFG = field.nextKeyFetchGroup;
556         JdbcColumn kc = keyColumns[0];
557         int keyScc = keyColumns.length;
558         OID keyOid = null;
559
560         // get info to extract the values
561
ClassMetaData valueCmd = fmd.elementTypeMetaData;
562         boolean valueJoined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
563         FetchGroup nfg = field.nextFetchGroup;
564         JdbcColumn vc = valueColumns[0];
565         int valueScc = valueColumns.length;
566
567         MutableInt nextIndex = null;
568         if (valuesAreOIDs) nextIndex = new MutableInt();
569         boolean first = true;
570         for (; ;) {
571             if (first) {
572                 first = false;
573             } else {
574                 if (!rs.next()) break;
575             }
576
577             OID owningOid = fmd.classMetaData.createOID(false);
578             if (!((JdbcOID)owningOid).copyKeyFields(rs, colIndex)) {
579                 //no elements found
580
break;
581             }
582             int index = colIndex + ourPkColumns.length;
583
584             if (keysAreOIDs) {
585                 keyOid = keyCmd.createOID(false);
586                 boolean isNull = !((JdbcOID)keyOid).copyKeyFields(rs, colIndex);
587                 if (!isNull) keys.add(keyOid);
588             } else {
589                 keys.add(kc.get(rs, colIndex ));
590             }
591             index += keyScc;
592
593             if (valuesAreOIDs) {
594                 OID valueOid = valueCmd.createOID(false);
595                 boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs, index);
596                 index += valueScc;
597                 if (!isNull) values.add(valueOid);
598
599                 if (!isNull && valueJoined) {
600                     if (container.isStateRequired(valueOid, nfg)) {
601                         State valueState = sm.createStateImp(rs, valueOid,
602                                 field.nextFetchGroup, forUpdate, index, nextIndex,
603                                 true, container,
604                                 ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, true),
605                                 fetchPass2Fields, false, null);
606                         index = nextIndex.value;
607                         container.addState(valueOid, valueState);
608                     } else {
609                         index = sm.skipState(index,
610                                 ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, true));
611                     }
612                 }
613             } else {
614                 values.add(vc.get(rs, index ));
615                 index += valueScc;
616             }
617
618             if (keysAreOIDs && keyJoined
619                     && container.isStateRequired(keyOid, keyNFG)) {
620                 State keyState = sm.createStateImp(rs, keyOid,
621                         field.nextKeyFetchGroup, forUpdate, index, null,
622                         true, container,
623                         ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, true),
624                         fetchPass2Fields, false, null);
625                 container.addState(keyOid, keyState);
626             }
627         }
628
629         MapEntries me = new MapEntries();
630         if (keysAreOIDs) {
631             me.keys = new OID[keys.size()];
632             keys.toArray(me.keys);
633         } else {
634             me.keys = keys.toArray();
635         }
636         if (valuesAreOIDs) {
637             me.values = new OID[values.size()];
638             values.toArray(me.values);
639         } else {
640             me.values = values.toArray();
641         }
642         state.setInternalObjectField(fmd.stateFieldNo, me);
643         return keys.size();
644     }
645
646     public SelectExp getSelectExpFrom(JdbcStorageManager sm,
647             SelectExp joinToExp, FetchGroupField field,
648             FgDs owningFgDs) {
649         SelectExp root = super.getSelectExpFromImp(joinToExp, field, sm, owningFgDs);
650         root.selectList = JdbcColumn.toSqlExp(keyColumns, root, root.selectList);
651         root.selectList = JdbcColumn.toSqlExp(ourPkColumns, root, root.selectList);
652         
653         // add a join for the keys if required
654
if (keysAreOIDs) {
655             if (field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO) {
656                 SelectExp se = new SelectExp();
657                 JdbcClass keyJdbcClass = (JdbcClass)fmd.keyTypeMetaData.storeClass;
658                 se.table = keyJdbcClass.table;
659                 se.outer = true;
660                 FgDs fgDs = ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getFgDs(true, se.outer);
661                 sm.addSelectFetchGroup(se, field.nextKeyFetchGroup, true,
662                         fgDs, false);
663                 owningFgDs.keyJs = fgDs.getJoinStruct();
664                 root.addJoin(keyColumns, se.table.pk, se);
665             }
666         }
667         return root;
668     }
669
670     /**
671      * Fetch the values for this field.
672      */

673     public int fetch(JdbcStorageManager sm, OID oid, State state,
674             FetchGroupField field, boolean forUpdate,
675             StateContainer container, boolean fetchPass2Fields,
676             ColFieldHolder colFHolder)
677             throws SQLException {
678         JoinStructure valueJs = null;
679         JoinStructure keyJs = null;
680
681         FgDs[] fgDses = new FgDs[2];
682
683         String JavaDoc sql = forUpdate ? field.jdbcSelectSqlForUpdate : field.jdbcSelectSql;
684         if (sql == null) {
685             SelectExp se = getSelectExp(sm, field, fgDses);
686             CharBuf s = sm.generateSql(se);
687             sql = s.toString();
688             if (forUpdate) {
689                 field.jdbcSelectSqlForUpdate = sql;
690             } else {
691                 field.jdbcSelectSql = sql;
692             }
693         }
694
695         if (valuesAreOIDs) {
696             fgDses[0] = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getExistingFgDs(
697                     true, field.jdbcUseJoin != JdbcField.USE_JOIN_INNER);
698             if (colFHolder != null) {
699                 if (fgDses[0] != null) {
700                     colFHolder.valueJs = fgDses[0].getJoinStruct();
701                 }
702             }
703         }
704         if (keysAreOIDs) {
705             fgDses[1] = ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getExistingFgDs(
706                     true, field.jdbcUseKeyJoin == JdbcField.USE_JOIN_OUTER);
707             if (colFHolder != null) {
708                 if (fgDses[1] != null) {
709                     colFHolder.keyJs = fgDses[1].getJoinStruct();
710                 }
711             }
712         }
713
714         PreparedStatement ps = null;
715         ResultSet rs = null;
716         try {
717             ps = sm.con().prepareStatement(sql);
718
719             ArrayList JavaDoc keys = new ArrayList JavaDoc();
720             ArrayList JavaDoc values = new ArrayList JavaDoc();
721
722             // get info to extract the keys
723
final boolean keyJoined = field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO;
724             int keyScc = keyColumns.length;
725             OID keyOid = null;
726
727             // get info to extract the values
728
final boolean valueJoined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO
729                     || fmd.ordering != null;
730             final int valueScc = valueColumns.length;
731
732             // process the rows
733
((JdbcOID)oid).setParams(ps, 1);
734             try {
735                 rs = ps.executeQuery();
736             } catch (Exception JavaDoc e) {
737                 throw mapException(e,
738                         "Fetch link table rows failed: " +
739                         JdbcUtils.toString(e) + "\n" +
740                         "Field: " + fmd.getTypeQName() + "\n" +
741                         "Instance: " + oid.toSString() + "\n" +
742                         JdbcUtils.getPreparedStatementInfo(sql, ps));
743             }
744
745             MutableInt nextIndex = null;
746             if (valuesAreOIDs) nextIndex = new MutableInt();
747             for (; rs.next();) {
748
749                 if (keysAreOIDs) {
750                     keyOid = fmd.keyTypeMetaData.createOID(false);
751                     ((JdbcOID)keyOid).copyKeyFields(rs, 1);
752                     keys.add(keyOid);
753                 } else {
754                     keys.add(keyColumns[0].get(rs, 1 ));
755                 }
756                 int index = 1 + keyScc;
757
758                 if (valuesAreOIDs) {
759                     OID valueOid = fmd.elementTypeMetaData.createOID(false);
760                     if (((JdbcOID)valueOid).copyKeyFields(rs, index)) {
761                         values.add(valueOid);
762                         index += valueScc;
763                         if (valueJoined) {
764                             if (container.isStateRequired(valueOid, field.nextFetchGroup)) {
765                                 container.addState(valueOid,
766                                         sm.createStateImp(rs, valueOid,
767                                         field.nextFetchGroup, forUpdate, index, nextIndex,
768                                         true, container,
769                                         fgDses[0],
770                                         fetchPass2Fields, false, valueJs));
771                                 index = nextIndex.value;
772                             } else {
773                                 index = sm.skipState(index, fgDses[0]);
774                             }
775                         }
776                     } else {
777                         index += valueScc;
778                         values.add(null);
779                     }
780                 } else {
781                     values.add(
782                             valueColumns[0].get(rs, index ));
783                     index += valueScc;
784                 }
785
786                 if (keysAreOIDs && keyJoined
787                         && container.isStateRequired(keyOid, field.nextKeyFetchGroup)) {
788                     container.addState(keyOid, sm.createStateImp(rs, keyOid,
789                             field.nextKeyFetchGroup, forUpdate, index, null,
790                             true, container,
791                             fgDses[1],
792                             fetchPass2Fields, false, keyJs));
793                 }
794             }
795
796             MapEntries me = new MapEntries();
797             if (keysAreOIDs) {
798                 me.keys = new OID[keys.size()];
799                 keys.toArray(me.keys);
800             } else {
801                 me.keys = keys.toArray();
802             }
803             if (valuesAreOIDs) {
804                 me.values = new OID[values.size()];
805                 values.toArray(me.values);
806             } else {
807                 me.values = values.toArray();
808             }
809             state.setInternalObjectField(fmd.stateFieldNo, me);
810             // @todo what about empty/null checking?
811

812             return keys.size();
813         } finally {
814             cleanup(rs);
815             cleanup(ps);
816         }
817     }
818
819     /**
820      * Fetch the values for this field using parallel query processing.
821      */

822     public int fetchWithFilter(JdbcStorageManager sm, StateContainer oidStates,
823             FetchGroupField field, ResultSet rs, boolean forUpdate,
824             OID oidToCheckOn,
825             OID[] lastReadStateOID, ClassMetaData cmd,
826             ColFieldHolder colFHolder) throws SQLException {
827
828         ClassMetaData keyCmd = fmd.keyTypeMetaData;
829         boolean keyJoined = field.jdbcUseKeyJoin != JdbcField.USE_JOIN_NO;
830         FetchGroup keyNFG = field.nextKeyFetchGroup;
831         JdbcColumn kc = keyColumns[0];
832         int keyScc = keyColumns.length;
833
834         // get info to extract the values
835
ClassMetaData valueCmd = fmd.elementTypeMetaData;
836         boolean valueJoined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
837         FetchGroup nfg = field.nextFetchGroup;
838         JdbcColumn vc = valueColumns[0];
839         int valueScc = valueColumns.length;
840
841         int rootOIDLength = ((JdbcClass)cmd.storeClass).table.pkSimpleColumnCount;
842         int stateOIDPKLen = ((JdbcClass)fmd.classMetaData.storeClass).table.pkSimpleColumnCount;
843
844         //the oid just read from the rs row
845
OID rootOid = cmd.createOID(false);
846         //the oid read from the previous rs row
847
OID prevRootOid = cmd.createOID(false);
848         OID tmpOID = null;
849
850         OID stateOID = fmd.classMetaData.createOID(false);
851         OID prevStateOID = fmd.classMetaData.createOID(false);
852         OID tmpStateOID = null;
853
854         boolean currentRowValid = false;
855         boolean prevRowValid = false;
856
857         final ArrayList JavaDoc keys = new ArrayList JavaDoc();
858         final ArrayList JavaDoc values = new ArrayList JavaDoc();
859
860         MutableInt nextIndex = null;
861         if (valuesAreOIDs) nextIndex = new MutableInt();
862         int returnState = 0;
863
864         FgDs fgDsValue = null;
865         if (valuesAreOIDs) {
866             fgDsValue = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getExistingFgDs(
867                     true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER);
868             if (colFHolder != null) {
869                 colFHolder.valueJs = fgDsValue.getJoinStruct();
870             }
871         }
872
873         FgDs fgDsKeys = null;
874         if (keysAreOIDs) {
875             fgDsKeys = ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getFgDs(
876                     true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER);
877             if (colFHolder != null) {
878                 colFHolder.keyJs = fgDsKeys.getJoinStruct();
879             }
880         }
881
882
883         //This oid was read previously so we have to read the rest of the row now.
884
if (lastReadStateOID[0] != null) {
885 // System.out.println("--- FINISHING LAST ROW READ");
886
int index = 1 + rootOIDLength + stateOIDPKLen;
887
888             prevRootOid = lastReadStateOID[0];
889 // index += rootOIDLength;
890

891             stateOID = lastReadStateOID[1];
892 // index += stateOIDPKLen;
893

894             currentRowValid = oidStates.containsKey(stateOID);
895             if (currentRowValid) {
896                 returnState |= STATUS_VALID_ROWS;
897                 OID keyOid = null;
898                 if (keysAreOIDs) {
899                     keyOid = keyCmd.createOID(false);
900                     ((JdbcOID)keyOid).copyKeyFields(rs, index);
901                     keys.add(keyOid);
902                 } else {
903                     keys.add(kc.get(rs, index));
904                 }
905                 index += keyScc;
906
907                 if (valuesAreOIDs) {
908                     OID valueOid = valueCmd.createOID(false);
909                     ((JdbcOID)valueOid).copyKeyFields(rs, index);
910                     index += valueScc;
911                     values.add(valueOid);
912                     if (valueJoined) {
913                         if (oidStates.isStateRequired(valueOid, nfg)) {
914                             State valueState = sm.createStateImp(rs, valueOid,
915                                     field.nextFetchGroup, forUpdate, index, nextIndex,
916                                     true, oidStates,
917                                     fgDsValue,
918                                     false, false, null);
919
920                             index = nextIndex.value;
921 // valueOid.resolve(valueState);
922
oidStates.addState(valueOid, valueState);
923                         } else {
924                             index = sm.skipState(index, fgDsValue);
925                         }
926                     }
927                 } else {
928                     values.add(vc.get(rs, index));
929                     index += valueScc;
930                 }
931
932                 if (keysAreOIDs && keyJoined
933                         && oidStates.isStateRequired(keyOid, keyNFG)) {
934                     State keyState = sm.createStateImp(rs, keyOid,
935                             field.nextKeyFetchGroup, forUpdate, index, nextIndex,
936                             true, oidStates,
937                             fgDsKeys,
938                             false, false, colFHolder.keyJs);
939                     oidStates.addState(keyOid, keyState);
940                 }
941                 if (nextIndex != null) nextIndex.value = 0;
942             }
943
944             //preserve the prevRootOid
945
tmpOID = rootOid;
946             rootOid = prevRootOid;
947             prevRootOid = tmpOID;
948
949             //preserve the prevStateOID
950
tmpStateOID = stateOID;
951             stateOID = prevStateOID;
952             prevStateOID = tmpStateOID;
953
954             prevRowValid = currentRowValid;
955         }
956
957         for (; rs.next();) {
958             int index = 1;
959
960             ((JdbcOID)rootOid).copyKeyFields(rs, index);
961             index += rootOIDLength;
962
963             ((JdbcOID)stateOID).copyKeyFields(rs, index);
964             index += stateOIDPKLen;
965
966             currentRowValid = oidStates.containsKey(stateOID);
967
968             if (!stateOID.equals(prevStateOID) && prevRowValid) {
969                 if (Debug.DEBUG) {
970                     if (oidStates.get(prevStateOID) == null) {
971                         ((StatesReturned)oidStates).dump();
972                         throw new NullPointerException JavaDoc(prevStateOID.toSString() +
973                                 " oidStates " + oidStates);
974                     }
975                 }
976                 if (updateStateFilter(keys, values,
977                         oidStates.get(prevStateOID))) {
978                     returnState |= STATUS_DATA_ADDED;
979                 }
980
981                 if (oidToCheckOn.equals(prevRootOid) && !oidToCheckOn.equals(
982                         rootOid)) {
983                     lastReadStateOID[0] = rootOid;
984                     lastReadStateOID[1] = stateOID;
985                     returnState |= STATUS_VALID_ROWS;
986                     return returnState;
987                 }
988
989                 keys.clear();
990                 values.clear();
991             }
992
993             if (currentRowValid) {
994                 returnState |= STATUS_VALID_ROWS;
995                 OID keyOid = null;
996                 if (keysAreOIDs) {
997                     keyOid = keyCmd.createOID(false);
998                     ((JdbcOID)keyOid).copyKeyFields(rs, index);
999                     keys.add(keyOid);
1000                } else {
1001                    keys.add(kc.get(rs, index));
1002                }
1003                index += keyScc;
1004
1005                if (valuesAreOIDs) {
1006                    OID valueOid = valueCmd.createOID(false);
1007                    ((JdbcOID)valueOid).copyKeyFields(rs, index);
1008                    index += valueScc;
1009                    values.add(valueOid);
1010                    if (valueJoined) {
1011                        if (oidStates.isStateRequired(valueOid, nfg)) {
1012                            State valueState = sm.createStateImp(rs, valueOid,
1013                                    field.nextFetchGroup, forUpdate, index, nextIndex,
1014                                    true, oidStates,
1015                                    ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(
1016                                            true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER),
1017                                    false, false, null);
1018
1019                            index = nextIndex.value;
1020                            oidStates.addState(valueOid, valueState);
1021                        } else {
1022                            index = sm.skipState(index,
1023                                    ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(
1024                                            true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER));
1025                        }
1026                    }
1027                } else {
1028                    values.add(vc.get(rs, index));
1029                    index += valueScc;
1030                }
1031
1032                if (keysAreOIDs && keyJoined
1033                        && oidStates.isStateRequired(keyOid, keyNFG)) {
1034                    State keyState = sm.createStateImp(rs, keyOid,
1035                            field.nextKeyFetchGroup, forUpdate, index, nextIndex,
1036                            true, oidStates,
1037                            ((JdbcFetchGroup)field.nextKeyFetchGroup.storeFetchGroup).getFgDs(
1038                                    true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER),
1039                            false, false, colFHolder.keyJs);
1040                    oidStates.addState(keyOid, keyState);
1041                }
1042
1043                if (nextIndex != null) nextIndex.value = 0;
1044            }
1045
1046            //preserve the prevRootOid
1047
tmpOID = rootOid;
1048            rootOid = prevRootOid;
1049            prevRootOid = tmpOID;
1050
1051            //preserve the prevStateOID
1052
tmpStateOID = stateOID;
1053            stateOID = prevStateOID;
1054            prevStateOID = tmpStateOID;
1055
1056            prevRowValid = currentRowValid;
1057        }
1058
1059        rs.close();
1060        returnState |= STATUS_CLOSED;
1061        if ((returnState & STATUS_VALID_ROWS) == STATUS_VALID_ROWS) {
1062            if (updateStateFilter(keys, values, oidStates.get(prevStateOID))) {
1063                returnState |= STATUS_DATA_ADDED;
1064            }
1065        }
1066        return returnState;
1067    }
1068
1069    public void fillStateWithEmpty(FetchGroupField field, State state) {
1070        if (!state.containsField(fmd.stateFieldNo)) {
1071            MapEntries me = new MapEntries();
1072            me.preGenerated = true;
1073            if (keysAreOIDs) {
1074                me.keys = EMPTY_OID_ARRAY;
1075            } else {
1076                me.keys = EMPTY_OBJECT_ARRAY;
1077            }
1078            if (valuesAreOIDs) {
1079                me.values = EMPTY_OID_ARRAY;
1080            } else {
1081                me.values = EMPTY_OBJECT_ARRAY;
1082            }
1083            state.setInternalObjectField(fmd.stateFieldNo, me);
1084        }
1085    }
1086
1087    /**
1088     * Update the supplied state with a MapEntries intsance. This method expects
1089     * the field to be filled with a non-null value.
1090     */

1091    private boolean updateStateFilter(ArrayList JavaDoc keys, ArrayList JavaDoc values,
1092            State state) {
1093        if (Debug.DEBUG) {
1094            //TODO START IF(DEBUG)
1095
if (!state.containsField(fmd.stateFieldNo)) {
1096                throw BindingSupportImpl.getInstance().internal("The mapField '" + fmd.name
1097                        + "' is not filled");
1098            }
1099            if (state.getInternalObjectField(fmd.stateFieldNo) == null) {
1100                throw BindingSupportImpl.getInstance().internal("The mapField '" + fmd.name
1101                        + "' is filled with a null value");
1102            }
1103            //TODO END IF(DEBUG)
1104
}
1105
1106        /**
1107         * The field is already resolved for client and this should be a state
1108         * that was retrieved from the localPMCache
1109         */

1110        if ((state.getInternalObjectField(fmd.stateFieldNo) instanceof Map JavaDoc)) {
1111// if (state.txId != TxId.PM_STATE_ID) {
1112
// throw BindingSupportImpl.getInstance().internal(
1113
// "The field is filled with " +
1114
// "a Map instance, but the state is not a localPmCache state");
1115
// }
1116
return false;
1117        }
1118
1119        if (!((MapEntries)state.getInternalObjectField(fmd.stateFieldNo)).preGenerated) {
1120            return false;
1121        }
1122
1123        MapEntries me = new MapEntries();
1124        if (keysAreOIDs) {
1125            me.keys = new OID[keys.size()];
1126            keys.toArray(me.keys);
1127        } else {
1128            me.keys = keys.toArray();
1129        }
1130        if (valuesAreOIDs) {
1131            me.values = new OID[values.size()];
1132            values.toArray(me.values);
1133        } else {
1134            me.values = values.toArray();
1135        }
1136        state.setInternalObjectField(fmd.stateFieldNo, me);
1137        return true;
1138    }
1139
1140    /**
1141     * Convert this field into a containsKey expression.
1142     */

1143    public SqlExp toContainsKeySqlExp(JdbcJDOQLCompiler comp, SelectExp root,
1144            Node args) {
1145        return toContainsSqlExp(keyColumns, fmd.keyTypeMetaData, comp, root,
1146                args);
1147    }
1148
1149    /**
1150     * Add our key columns to the row.
1151     */

1152    protected void addFetchAllRowsKey(SqlExp e, SelectExp se) {
1153        for (; e.next != null; e = e.next) ;
1154        e.next = JdbcColumn.toSqlExp(keyColumns, se);
1155    }
1156
1157    /**
1158     * Fetch a row of values for this field. This is used when bulk copying
1159     * one database to another to read all the rows in a given link table.
1160     * Return the index of the last column read + 1.
1161     */

1162    public int readRow(ResultSet rs, JdbcLinkCollectionField.LinkRow row)
1163            throws SQLException {
1164        int pos = super.readRow(rs, row);
1165        if (keysAreOIDs) {
1166            OID keyOid = fmd.keyTypeMetaData.createOID(false);
1167            ((JdbcOID)keyOid).copyKeyFields(rs, pos);
1168            row.key = keyOid;
1169            pos += keyColumns.length;
1170        } else {
1171            row.key = keyColumns[0].get(rs, pos++);
1172        }
1173        return pos;
1174    }
1175
1176    /**
1177     * Set a row of values for this field on a PreparedStatement.
1178     * This is used when bulk copying one database to another.
1179     */

1180    public void writeRow(PreparedStatement ps, LinkRow row)
1181            throws SQLException {
1182        row.owner.setCmd(fmd.classMetaData);
1183        int pos = row.owner.setParams(ps, 1);
1184        if (keysAreOIDs) {
1185            JdbcGenericOID k = (JdbcGenericOID)row.key;
1186            k.setCmd(fmd.classMetaData);
1187            pos = k.setParams(ps, pos);
1188        } else {
1189            keyColumns[0].set(ps, pos++, row.key);
1190        }
1191        if (valuesAreOIDs) {
1192            JdbcGenericOID v = (JdbcGenericOID)row.value;
1193            v.setCmd(fmd.classMetaData);
1194            v.setParams(ps, pos);
1195        } else {
1196            valueColumns[0].set(ps, pos, row.value);
1197        }
1198    }
1199
1200}
1201
Popular Tags