KickJava   Java API By Example, From Geeks To Geeks.

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


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.metadata.parser.JdoElement;
15 import com.versant.core.metadata.parser.JdoExtension;
16 import com.versant.core.metadata.parser.JdoExtensionKeys;
17 import com.versant.core.metadata.*;
18 import com.versant.core.common.OID;
19 import com.versant.core.common.State;
20 import com.versant.core.jdbc.*;
21 import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
22 import com.versant.core.jdbc.sql.exp.*;
23 import com.versant.core.server.PersistGraph;
24 import com.versant.core.server.StateContainer;
25 import com.versant.core.common.*;
26 import com.versant.core.util.CharBuf;
27 import com.versant.core.jdo.query.Node;
28 import com.versant.core.jdo.query.VarNode;
29 import com.versant.core.jdo.query.VarNodeIF;
30 import com.versant.core.common.Debug;
31
32 import java.io.PrintStream JavaDoc;
33 import java.sql.Connection JavaDoc;
34 import java.sql.SQLException JavaDoc;
35 import java.sql.PreparedStatement JavaDoc;
36 import java.sql.ResultSet JavaDoc;
37
38 import com.versant.core.common.BindingSupportImpl;
39
40 /**
41  * A field that is a Collection or array of a PC class stored using a
42  * foreign key in the value class.
43  */

44 public class JdbcFKCollectionField extends JdbcCollectionField {
45
46     /**
47      * This is the 'foreign key' field in the value class.
48      */

49     public JdbcRefField fkField;
50
51     /**
52      * This is the JdbcClass for the elements.
53      */

54     private JdbcClass elementJdbcClass;
55
56     // Size of the moving window used to calculate avgRowCount.
57
private static final int WINDOW_SIZE = 20;
58     // The initial array size is avgRowCount multiplied by this.
59
private static final float FUDGE_FACTOR = 1.5f;
60     // This is the minimum initial array size.
61
private static final int MIN_LEN = 4;
62
63     // Total number of fetches done so far.
64
protected transient int fetchCount;
65     // Average number of rows retrieved with each fetch.
66
protected transient float avgRowCount;
67     // Total number of times the values array for a fetch has had to be
68
// expanded (i.e. we guessed the size incorrectly. This is the number
69
// of extra objects and array copies we have had to do.
70
protected transient int expansionCount;
71
72     public void dump(PrintStream JavaDoc out, String JavaDoc indent) {
73         super.dump(out, indent);
74         String JavaDoc is = indent + " ";
75         out.println(is + "fkField " + fkField);
76     }
77
78     /**
79      * Complete the meta data for this collection. This must use info
80      * already supplied in the .jdo file and add anything else needed.
81      */

82     public void processMetaData(JdoElement context, JdbcMetaDataBuilder mdb,
83             boolean quiet) {
84         ClassMetaData ecmd = fmd.elementTypeMetaData;
85         elementJdbcClass = (JdbcClass)ecmd.storeClass;
86         if (elementJdbcClass == null) {
87             throw BindingSupportImpl.getInstance().runtime("The inverse extension may only be used for " +
88                     "collections of PC instances stored by JDBC\n" +
89                     context.getContext());
90         }
91         ClassMetaData cmd = fmd.classMetaData;
92
93         super.processMetaData(context, mdb, quiet);
94
95         useJoin = JdbcField.USE_JOIN_INNER;
96         if (fmd.category != MDStatics.CATEGORY_ARRAY) {
97             fmd.managed = mdb.getJdbcConfig().managedOneToMany;
98         } else {
99             fmd.managed = false;
100         }
101
102         JdoExtension[] a;
103         if (fmd.category == MDStatics.CATEGORY_ARRAY) {
104             a = fmd.jdoArray.extensions;
105         } else if (fmd.category == MDStatics.CATEGORY_COLLECTION) {
106             a = fmd.jdoCollection.extensions;
107         } else {
108             throw BindingSupportImpl.getInstance().internal(
109                     "Category '"
110                     + MDStaticUtils.toCategoryString(fmd.category) + "' is not supported for FK Collections");
111         }
112         int len = a.length;
113         for (int i = 0; i < len; i++) {
114             JdoExtension e = a[i];
115             switch (e.key) {
116                 case JdoExtensionKeys.MANAGED:
117                     if (fmd.category == MDStatics.CATEGORY_ARRAY && e.getBoolean()) {
118                         throw BindingSupportImpl.getInstance().invalidOperation(
119                                 "The managed option is not supported for arrays: " + fmd.name);
120                     }
121                     fmd.managed = e.getBoolean();
122                     break;
123                 case JdoExtensionKeys.INVERSE:
124                 case JdoExtensionKeys.JDBC_LINK_FOREIGN_KEY:
125                     String JavaDoc fname = e.getString();
126                     FieldMetaData f = ecmd.getFieldMetaData(fname);
127                     if (f == null) {
128                         f = createFakeFKBackRef(cmd, ecmd, mdb, e, quiet);
129                     }
130                     if (f.isEmbeddedRef()) {
131                         throw BindingSupportImpl.getInstance().invalidOperation("an Inverse field may not be Embedded");
132                     }
133                     if (f.storeField == null) {
134                         throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' is not persistent\n" +
135                                 context.getContext());
136                     }
137                     if (!(f.storeField instanceof JdbcRefField)) {
138                         throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' is not a reference\n" +
139                                 context.getContext());
140                     }
141                     fkField = (JdbcRefField)f.storeField;
142                     if (!cmd.isAncestorOrSelf(fkField.targetClass)) {
143                         throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' references " +
144                                 fkField.targetClass + " and not our class\n" +
145                                 context.getContext());
146                     }
147                     fmd.ordered = false;
148                     createIndex(mdb, ecmd, e.nested);
149                     break;
150
151                 default:
152                     if (e.isJdbc()) {
153                         throw BindingSupportImpl.getInstance().runtime(
154                                 "Unexpected extension: " + e + "\n" + e.getContext());
155                     }
156             }
157         }
158         if (fkField == null) {
159             throw BindingSupportImpl.getInstance().internal("fkField is null");
160         }
161
162         fkField.masterCollectionField = this;
163         ourPkColumns = fkField.cols;
164     }
165
166     private FieldMetaData createFakeFKBackRef(ClassMetaData cmd,
167             ClassMetaData ecmd, JdbcMetaDataBuilder mdb,
168             JdoExtension e, boolean quiet) {
169         fmd.managed = false;
170
171         FieldMetaData f = new FieldMetaData();
172         f.fake = true;
173         f.typeMetaData = cmd;
174         f.name = cmd.getShortName() + "_" + fmd.name;
175         f.category = MDStatics.CATEGORY_REF;
176         f.ordered = false;
177         f.managed = false;
178         f.primaryField = true;
179         JdbcRefField jdbcRefField = new JdbcRefField();
180         jdbcRefField.targetClass = cmd;
181         fkField = jdbcRefField;
182         f.classMetaData = ecmd;
183         jdbcRefField.fmd = f;
184         f.storeField = jdbcRefField;
185         jdbcRefField.fake = true;
186         f.type = cmd.cls;
187         f.inverseFieldMetaData = fmd;
188         mdb.processRefFieldImpl(elementJdbcClass, jdbcRefField,
189                 f, e, e.nested, quiet);
190
191 // JdbcRefMetaDataBuilder rmdb = new JdbcRefMetaDataBuilder(
192
// fmd.classMetaData, mdb, cmd,
193
// cmd.jdbcClass.store, e, fname, e.nested, quiet);
194
// JdbcColumn[] cols = rmdb.getCols();
195
// jdbcRefField.cols = cols;
196
// if (cols != null) {
197
// for (int j = 0; j < cols.length; j++) {
198
// JdbcColumn col = cols[j];
199
// col.nulls = true;
200
// col.comment = "inverse FK for " + cmd.getShortName() + "-" + fmd.name;
201
// }
202
// }
203
mdb.getClassInfo(ecmd).elements.add(f.storeField);
204         return f;
205     }
206
207     /**
208      * Create an index on our pkField unless this has been disabled.
209      */

210     private void createIndex(JdbcMetaDataBuilder mdb, ClassMetaData refCmd,
211             JdoExtension[] nested) {
212         JdbcClass refJdbcClass = (JdbcClass)refCmd.storeClass;
213         JdbcIndex idx = null;
214         boolean doNotCreateIndex = false;
215
216         int n = nested == null ? 0 : nested.length;
217         for (int i = 0; i < n; i++) {
218             JdoExtension e = nested[i];
219             switch (e.key) {
220                 case JdoExtensionKeys.JDBC_INDEX:
221                     if (idx != null) {
222                         throw BindingSupportImpl.getInstance().runtime("Only one jdbc-index extension is allowed here\n" +
223                                 e.getContext());
224                     }
225                     if (e.isNoValue()) {
226                         doNotCreateIndex = true;
227                         break;
228                     }
229                     idx = new JdbcIndex();
230                     idx.name = e.value;
231                     break;
232                 case JdoExtensionKeys.JDBC_COLUMN:
233                 case JdoExtensionKeys.JDBC_USE_JOIN:
234                 case JdoExtensionKeys.JDBC_CONSTRAINT:
235                 case JdoExtensionKeys.JDBC_REF:
236                     // Handled
237
break;
238                 default:
239                     if (e.isJdbc()) {
240                         MetaDataBuilder.throwUnexpectedExtension(e);
241                     }
242             }
243         }
244
245         if (doNotCreateIndex) return;
246         if (idx == null) idx = new JdbcIndex();
247         if (fkField.cols != null) {
248             idx.setCols(fkField.cols);
249         }
250
251         // register the name of the index if one was specified otherwise one
252
// will be generated later along with user specified indexes
253
if (idx.name != null) {
254             try {
255                 mdb.getNameGenerator().addIndexName(
256                         refJdbcClass.table.name, idx.name);
257             } catch (IllegalArgumentException JavaDoc x) {
258                 throw BindingSupportImpl.getInstance().runtime(x.getMessage(),
259                         x);
260             }
261         }
262
263         mdb.getClassInfo(refCmd).autoIndexes.add(idx);
264     }
265
266     /**
267      * Persist pass 2 field for a block of graph entries all with
268      * the same class. The same ps'es can be used for all entries in the block.
269      */

270     public void persistPass2Block(PersistGraph graph, int blockStart,
271             int blockEnd, CharBuf s, Connection JavaDoc con, boolean batchInserts,
272             boolean batchUpdates) throws SQLException JavaDoc {
273         // nothing to do
274
}
275
276     /**
277      * Fetch the values for this field.
278      */

279     public int fetch(JdbcStorageManager sm, OID oid, State state,
280             FetchGroupField field, boolean forUpdate,
281             StateContainer container, boolean fetchPass2Fields,
282             ColFieldHolder colFHolder)
283             throws SQLException JavaDoc {
284
285         String JavaDoc sql = forUpdate ? field.jdbcSelectSqlForUpdate : field.jdbcSelectSql;
286         final boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
287
288         FetchGroup nextFetchGroup = field.nextFetchGroup;
289         FgDs[] fgDses = new FgDs[1];
290
291         if (sql == null) {
292             SelectExp se = getSelectExp(field, sm, fgDses);
293             CharBuf s = sm.generateSql(se);
294             sql = s.toString();
295             if (forUpdate) {
296                 field.jdbcSelectSqlForUpdate = sql;
297             } else {
298                 field.jdbcSelectSql = sql;
299             }
300         } else {
301             fgDses[0] = ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getExistingFgDs(true, false);
302         }
303
304         if (colFHolder != null && fgDses[0] != null) {
305             colFHolder.valueJs = fgDses[0].getJoinStruct();
306         }
307
308         PreparedStatement JavaDoc ps = null;
309         ResultSet JavaDoc rs = null;
310         Struct s = new Struct();
311         try {
312             ps = sm.con().prepareStatement(sql);
313             ((JdbcOID)oid).setParams(ps, 1);
314             try {
315                 rs = ps.executeQuery();
316             } catch (Exception JavaDoc e) {
317                 throw mapException(e,
318                         "Fetch inverse foreign key collection failed: " +
319                         JdbcUtils.toString(e) + "\n" +
320                         "Field: " + fmd.getTypeQName() + "\n" +
321                         "Instance: " + oid.toSString() + "\n" +
322                         JdbcUtils.getPreparedStatementInfo(sql, ps));
323             }
324
325             s.init();
326
327             ClassMetaData valueCmd = fmd.elementTypeMetaData;
328
329             int valuePkLen = 0;
330             if (joined) valuePkLen = elementJdbcClass.table.pkSimpleColumnCount;
331             for (; rs.next();) {
332                 OID valueOid = valueCmd.createOID(false);
333                 boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs, 1);
334                 if (isNull) {
335                     s.add(null);
336                 } else {
337                     s.add(valueOid);
338                 }
339
340                 if (!isNull && joined && container.isStateRequired(valueOid,
341                         nextFetchGroup)) {
342                     State valueState = sm.createStateImp(rs, valueOid,
343                             nextFetchGroup, forUpdate, 1 + valuePkLen,
344                             null, true, container,
345                             fgDses[0], fetchPass2Fields, false, null);
346                     container.addState(valueOid, valueState);
347                 }
348             }
349
350             // this can come out when the bugs are removed from the rest
351
// of the code
352
updateState(s, state);
353             updateStats(s.size);
354         } finally {
355             cleanup(rs);
356             cleanup(ps);
357         }
358         return s.size;
359     }
360
361     public int fetchFrom(ResultSet JavaDoc rs, OID oid, State state,
362             FetchGroupField field, boolean forUpdate,
363             StateContainer container,
364             boolean fetchPass2Fields, int colIndex,
365             FetchInfo fetchInfo, JdbcStorageManager sm)
366             throws SQLException JavaDoc {
367
368         Struct s = new Struct();
369         final boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
370         boolean first = true;
371         s.init();
372         ClassMetaData valueCmd = fmd.elementTypeMetaData;
373
374         FetchGroup nextFetchGroup = field.nextFetchGroup;
375
376         int valuePkLen = 0;
377         if (joined) valuePkLen = elementJdbcClass.table.pkSimpleColumnCount;
378         for (; ;) {
379             boolean mustBreak = false;
380             if (first) {
381                 first = false;
382                 mustBreak = updateForFirstRow(fetchInfo, mustBreak, rs,
383                         colIndex, oid);
384             } else {
385                 if (rs.next()) {
386                     mustBreak = checkKeyOid(rs, colIndex, fetchInfo, mustBreak,
387                             oid);
388                     fetchInfo.onNextRow = true;
389                 } else {
390                     fetchInfo.onNextRow = false;
391                     fetchInfo.finished = true;
392                     mustBreak = true;
393                 }
394             }
395             if (mustBreak) break;
396
397             OID valueOid = valueCmd.createOID(false);
398             boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs,
399                     colIndex + ourPkColumns.length);
400             if (!isNull) {
401                 s.add(valueOid);
402                 if (joined && container.isStateRequired(valueOid,
403                         nextFetchGroup)) {
404                     State valueState = sm.createStateImp(rs, valueOid,
405                             nextFetchGroup, forUpdate, colIndex + ourPkColumns.length + valuePkLen,
406                             null, true, container,
407                             ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getExistingFgDs(true, true),
408                             fetchPass2Fields,
409                             false, null);
410                     container.addState(valueOid, valueState);
411                 }
412             } else {
413                 //no elements
414
break;
415             }
416         }
417
418         updateState(s, state);
419         updateStats(s.size);
420         return s.size;
421     }
422
423     private void updateStats(int size) {
424         // Update statistics. This is not thread safe but it is not a
425
// problem if the avgRowCount is a bit out sometimes.
426
int fc = ++fetchCount;
427         if (fc > WINDOW_SIZE) {
428             fc = WINDOW_SIZE;
429         } else if (fc == 1) {
430             avgRowCount = size;
431         } else if (fc < 0) {
432             fc = fetchCount = WINDOW_SIZE;
433         } else {
434             avgRowCount = (avgRowCount * (fc - 1) + size) / fc;
435         }
436     }
437
438     public int fetchWithFilter(JdbcStorageManager sm, StateContainer oidStates,
439             FetchGroupField field, ResultSet JavaDoc rs, boolean forUpdate,
440             OID oidToCheckOn,
441             OID[] lastReadStateOID, ClassMetaData cmd,
442             ColFieldHolder colFHolder)
443             throws SQLException JavaDoc {
444
445         ClassMetaData valueCmd = fmd.elementTypeMetaData;
446         final boolean joined = field.jdbcUseJoin != JdbcField.USE_JOIN_NO;
447         FetchGroup nextFetchGroup = field.nextFetchGroup;
448
449         if (colFHolder != null) {
450             colFHolder.valueJs = new JoinStructure(nextFetchGroup);
451         }
452
453         int valuePkLen = 0;
454         if (joined) valuePkLen = elementJdbcClass.table.pkSimpleColumnCount;
455
456         int rootOIDLenght = ((JdbcClass)cmd.storeClass).table.pkSimpleColumnCount;
457         int stateOIDPKLen = ((JdbcClass)fmd.classMetaData.storeClass).table.pkSimpleColumnCount;
458
459         //the oid just read from the rs row
460
OID rootOid = cmd.createOID(false);
461         //the oid read from the previous rs row
462
OID prevRootOid = cmd.createOID(false);
463         OID tmpOID = null;
464
465         OID stateOID = fmd.classMetaData.createOID(false);
466         OID prevStateOID = fmd.classMetaData.createOID(false);
467         OID tmpStateOID = null;
468
469         Struct s = new Struct();
470         s.init();
471
472         boolean currentRowValid = false;
473         boolean prevRowValid = false;
474         int returnState = 0;
475
476         FgDs fgDs = ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getExistingFgDs(
477                 true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER);
478         if (colFHolder != null) {
479             colFHolder.valueJs = fgDs.getJoinStruct();
480         }
481
482         //This oid was read previously so we have to read the rest of the row now.
483
if (lastReadStateOID[0] != null) {
484             int index = 1;
485
486             prevRootOid = lastReadStateOID[0];
487             index += rootOIDLenght;
488
489             stateOID = lastReadStateOID[1];
490             index += stateOIDPKLen;
491
492             currentRowValid = oidStates.containsKey(stateOID);
493
494             if (currentRowValid) {
495                 returnState |= STATUS_VALID_ROWS;
496                 OID valueOid = valueCmd.createOID(false);
497                 boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs, index);
498                 index += valuePkLen;
499
500                 if (!isNull) {
501                     s.add(valueOid);
502                 } else {
503                     s.add(null);
504                 }
505
506                 if (!isNull && joined && oidStates.isStateRequired(valueOid,
507                         nextFetchGroup)) {
508
509                     State valueState = sm.createStateImp(rs,
510                             valueOid,
511                             nextFetchGroup, forUpdate, index, null,
512                             true, oidStates, fgDs, false, false,
513                             null);
514                     oidStates.addState(valueOid, valueState);
515                 }
516             }
517
518             //preserve the prevRootOid
519
tmpOID = rootOid;
520             rootOid = prevRootOid;
521             prevRootOid = tmpOID;
522
523             //preserve the prevStateOID
524
tmpStateOID = stateOID;
525             stateOID = prevStateOID;
526             prevStateOID = tmpStateOID;
527
528             prevRowValid = currentRowValid;
529         }
530
531         //go through the rs until we reach the end or oidToCheckOn
532
for (; rs.next();) {
533             int index = 1;
534             ((JdbcOID)rootOid).copyKeyFields(rs, index);
535             index += rootOIDLenght;
536
537             ((JdbcOID)stateOID).copyKeyFields(rs, index);
538             index += stateOIDPKLen;
539
540             currentRowValid = oidStates.containsKey(stateOID);
541
542             //detected a change in stateOid. Only update if the previous
543
// row was for a valid oid.
544
if (!stateOID.equals(prevStateOID) && prevRowValid) {
545                 if (updateStateFilter(s, oidStates.get(prevStateOID))) {
546                     returnState |= STATUS_DATA_ADDED;
547                 }
548                 updateStatistics(s.size);
549                 //if lastRootOid is the oid to check on then return do no process further.
550
if (oidToCheckOn.equals(prevRootOid) && !oidToCheckOn.equals(
551                         rootOid)) {
552                     lastReadStateOID[0] = rootOid;
553                     lastReadStateOID[1] = stateOID;
554                     returnState |= STATUS_VALID_ROWS;
555                     return returnState;
556                 }
557                 //reset the struct for next usage.
558
s.init();
559             }
560
561             if (currentRowValid) {
562                 returnState |= STATUS_VALID_ROWS;
563                 OID valueOid = valueCmd.createOID(false);
564                 boolean isNull = !((JdbcOID)valueOid).copyKeyFields(rs, index);
565                 if (!isNull) {
566                     s.add(valueOid);
567                 } else {
568                     s.add(null);
569                 }
570
571                 index += valuePkLen;
572                 if (!isNull && joined && oidStates.isStateRequired(valueOid,
573                         nextFetchGroup)) {
574                     State valueState = sm.createStateImp(rs,
575                             valueOid,
576                             nextFetchGroup, forUpdate, index, null,
577                             true, oidStates,
578                             ((JdbcFetchGroup)nextFetchGroup.storeFetchGroup).getFgDs(true,
579                                     field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER),
580                             false, false, null);
581                     oidStates.addState(valueOid, valueState);
582                 }
583             }
584
585
586             //preserve the prevRootOid
587
tmpOID = rootOid;
588             rootOid = prevRootOid;
589             prevRootOid = tmpOID;
590
591             //preserve the prevStateOID
592
tmpStateOID = stateOID;
593             stateOID = prevStateOID;
594             prevStateOID = tmpStateOID;
595
596             prevRowValid = currentRowValid;
597         }
598         rs.close();
599         if ((returnState & STATUS_VALID_ROWS) == STATUS_VALID_ROWS) {
600             if (updateStateFilter(s, oidStates.get(prevStateOID))) {
601                 returnState |= STATUS_DATA_ADDED;
602             }
603         }
604         returnState |= STATUS_CLOSED;
605         return returnState;
606     }
607
608     public void fillStateWithEmpty(FetchGroupField field, State state) {
609         if (!state.containsField(fmd.stateFieldNo)) {
610             state.setInternalObjectField(fmd.stateFieldNo,
611                     PRE_GEN_EMPTY_OBJECT_ARRAY);
612         }
613     }
614
615     private boolean updateState(Struct s, State state) {
616         if (state == null) return false;
617         if (s.values == null) s.values = EMPTY_OID_ARRAY;
618         s.trim();
619         state.setInternalObjectField(fmd.stateFieldNo, s.values);
620         return true;
621     }
622
623     private boolean updateStateFilter(Struct s, State state) {
624         if (state == null) return false;
625         if (s.values == null) s.values = EMPTY_OID_ARRAY;
626         if (state.getInternalObjectField(fmd.stateFieldNo) == PRE_GEN_EMPTY_OBJECT_ARRAY) {
627             s.trim();
628             state.setInternalObjectField(fmd.stateFieldNo, s.values);
629             return true;
630         }
631         return false;
632     }
633
634     public void appendOrderExpForFilterExp(SelectExp se, SelectExp root) {
635         if (fmd.elementTypeMetaData != null) {
636             root.appendOrderByForColumns(
637                     ((JdbcClass)fmd.elementTypeMetaData.storeClass).table.pk, se);
638         }
639     }
640
641     private class Struct {
642
643         public int len;
644         public OID[] values;
645         public int size;
646
647         public OID prevRootOID;
648         public OID currentRootOID;
649
650         public OID prevStateOID;
651         public OID currentStateOID;
652
653         public void init() {
654             len = (int)(avgRowCount * FUDGE_FACTOR);
655             if (len < MIN_LEN) len = 0;
656             values = len == 0 ? null : new OID[len];
657             if (Debug.DEBUG) {
658                 if (((fetchCount + 1) % 10) == 0) {
659                     System.out.println("JdbcFkCollectionField.fetch" +
660                             " avgRowCount = " + avgRowCount + " " +
661                             " len = " + len + " " +
662                             " expansionCount = " + expansionCount + " " +
663                             " fetchCount = " + fetchCount);
664                 }
665             }
666             size = 0;
667         }
668
669         private void add(OID value) {
670             //grow if nec.
671
if (size == len) {
672                 if (len == 0) {
673                     values = new OID[len = MIN_LEN];
674                 } else {
675                     len = len * 3 / 2 + 1;
676                     OID[] a = new OID[len];
677                     System.arraycopy(values, 0, a, 0, size);
678                     values = a;
679                     expansionCount++;
680                 }
681             }
682             values[size++] = value;
683         }
684
685         /**
686          * Trim values down to size elements.
687          */

688         public void trim() {
689             if (values.length == size) return;
690             OID[] a = new OID[size];
691             System.arraycopy(values, 0, a, 0, size);
692             values = a;
693         }
694     }
695
696     /**
697      * Update statistics. This is not thread safe but it is not a
698      * problem if the avgRowCount is a bit out sometimes.
699      */

700     private void updateStatistics(int size) {
701         int fc = ++fetchCount;
702         if (fc > WINDOW_SIZE) {
703             fc = WINDOW_SIZE;
704         } else if (fc == 1) {
705             avgRowCount = size;
706         } else if (fc < 0) {
707             fc = fetchCount = WINDOW_SIZE;
708         } else {
709             avgRowCount = (avgRowCount * (fc - 1) + size) / fc;
710         }
711     }
712
713     /**
714      * Get a SelectExp to select all the rows in this collection using the
715      * supplied fetch group field to control joins and so on.
716      */

717     private SelectExp getSelectExp(FetchGroupField field,
718             JdbcStorageManager sm, FgDs[] fgDses) {
719         SelectExp root = new SelectExp();
720         root.table = elementJdbcClass.table;
721         root.selectList = JdbcColumn.toSqlExp(root.table.pk, root);
722
723         if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO) {
724             sm.addSelectFetchGroup(root, field.nextFetchGroup,
725                     true,
726                     fgDses[0] = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, false),
727                     root, root.table.pk, this);
728         }
729
730         // add ourPkColumns to the where clause list creating a new
731
// join to the base table if necessary
732
SelectExp se = root.findTable(ourPkColumns[0].table);
733         if (se == null) {
734             se = new SelectExp();
735             se.table = ourPkColumns[0].table;
736             root.addJoin(root.table.pk, se.table.pk, se);
737         }
738
739         // put the expression at the start of the where clause list
740
root.whereExp = JdbcColumn.createEqualsParamExp(ourPkColumns, se);
741         if (((JdbcClass)fmd.elementTypeMetaData.storeClass).classIdCol != null) {
742             root.whereExp.next = ((JdbcClass)fmd.elementTypeMetaData.storeClass).getCheckClassIdExp(
743                     root);
744             AndExp andExp = new AndExp(root.whereExp);
745             root.whereExp = andExp;
746         }
747
748         // add order by if ordering extension has been used
749
if (fmd.ordering != null) {
750             root.addOrderBy(fmd.ordering, false);
751         }
752
753         if (Debug.DEBUG) {
754             System.out.println("%%% JdbcFKCollectionField.getSelectExp: " +
755                     fmd.getQName());
756             root.dump(" ");
757             System.out.println("%%%");
758         }
759
760         return root;
761     }
762
763     /**
764      * Get a SelectExp to select all the rows in this collection using the
765      * supplied fetch group field to control joins and so on.
766      */

767     public SelectExp getSelectExpFrom(JdbcStorageManager sm, SelectExp joinToExp,
768             FetchGroupField field, FgDs owningFgDs) {
769         SelectExp root = new SelectExp();
770         root.outer = true;
771         root.table = elementJdbcClass.table;
772         root.selectList = JdbcColumn.toSqlExp(ourPkColumns, root,
773                 JdbcColumn.toSqlExp(root.table.pk, root));
774
775         if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO) {
776             FgDs fgDs = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(true, true);
777             sm.addSelectFetchGroup(root, field.nextFetchGroup,
778                     true, fgDs, root, root.table.pk, this);
779             owningFgDs.valueJs = fgDs.getJoinStruct();
780         }
781
782         // add order by if ordering extension has been used
783
if (fmd.ordering != null) {
784             root.addOrderBy(fmd.ordering, false);
785         }
786
787         if (Debug.DEBUG) {
788             System.out.println("%%% JdbcFKCollectionField.getSelectExp: " +
789                     fmd.getQName());
790             root.dump(" ");
791             System.out.println("%%%");
792         }
793
794
795         joinToExp.addJoin(joinToExp.table.pk, ourPkColumns, root);
796         joinToExp.appendOrderByExp(root.orderByList);
797         root.orderByList = null;
798         return root;
799     }
800
801     public SelectExp getSelectFilterExp(JdbcStorageManager sm, FetchGroupField field,
802             ColFieldHolder colFHolder) {
803         SelectExp root = new SelectExp();
804         root.table = elementJdbcClass.table;
805         root.selectList = JdbcColumn.toSqlExp(root.table.pk, root);
806
807         root.whereExp = ((JdbcClass)fmd.elementTypeMetaData.storeClass).getCheckClassIdExp(
808                 root);
809
810 // prepend our pk columns to the select list
811
SqlExp e = JdbcColumn.toSqlExp(ourPkColumns, root, root.selectList);
812         root.selectList = e;
813         root.appendOrderByForColumns(ourPkColumns);
814
815         if (field.jdbcUseJoin != JdbcField.USE_JOIN_NO) {
816             FgDs fgDs = ((JdbcFetchGroup)field.nextFetchGroup.storeFetchGroup).getFgDs(
817                     true, field.jdbcUseJoin == JdbcField.USE_JOIN_OUTER);
818             colFHolder.valueJs = fgDs.getJoinStruct();
819             sm.addSelectFetchGroup(root, field.nextFetchGroup, true, fgDs,
820                     false);
821         }
822
823         // add order by if ordering extension has been used
824
if (fmd.ordering != null) {
825             root.addOrderBy(fmd.ordering, true);
826         }
827
828         return root;
829     }
830
831     public SelectExp getSelectFilterJoinExp(boolean value, SelectExp lhSe,
832             SelectExp rootSe, boolean addRootJoin) {
833         SelectExp root = new SelectExp();
834         root.table = elementJdbcClass.table;
835         lhSe.addJoin(lhSe.table.pk, ourPkColumns, root);
836         return root;
837     }
838
839     /**
840      * Convert this field into an isEmpty expression.
841      */

842     public SqlExp toIsEmptySqlExp(JdbcJDOQLCompiler comp, SelectExp root) {
843         SelectExp se = new SelectExp();
844         se.table = elementJdbcClass.table;
845         se.jdbcField = this;
846         se.subSelectJoinExp = root.createJoinExp(root.table.pk, ourPkColumns,
847                 se);
848         // @todo what about empty/null checking?
849
return new UnaryOpExp(new ExistsExp(se, true), UnaryOpExp.OP_NOT);
850     }
851
852     /**
853      * Convert this field into a contains expression.
854      */

855     public SqlExp toContainsSqlExp(JdbcJDOQLCompiler comp, SelectExp root,
856             Node args) {
857         if (args instanceof VarNodeIF) {
858             VarNode v = ((VarNodeIF)args).getVarNode();
859             SelectExp vse = (SelectExp)v.getStoreExtent();
860
861             //same table
862
if (vse.table == ourPkColumns[0].table) {
863                 vse.subSelectJoinExp = root.createJoinExp(root.table.pk,
864                         ourPkColumns, vse);
865             } else {
866                 SelectExp se = new SelectExp();
867                 se.table = ourPkColumns[0].table;
868                 se.outer = vse.outer;
869                 vse.addJoin(vse.table.pk, ourPkColumns[0].table.pk, se);
870             }
871
872             if (v.getCmd() != fmd.elementTypeMetaData) {
873                 //should be a subclass
874
vse.whereExp = SelectExp.appendWithAnd(vse.whereExp,
875                         ((JdbcClass)fmd.elementTypeMetaData.storeClass).getCheckClassIdExp(
876                                 vse));
877             }
878
879             return new ExistsExp(vse, true, v);
880         } else {
881             SelectExp se = new SelectExp();
882             se.table = elementJdbcClass.table;
883             se.jdbcField = this;
884             se.subSelectJoinExp = root.createJoinExp(root.table.pk,
885                     ourPkColumns, se);
886             SqlExp left = JdbcColumn.toSqlExp(se.table.pkSimpleCols, se);
887             for (SqlExp e = left; e != null; e = e.next) {
888                 ((ColumnExp)e).cmd = fmd.elementTypeMetaData;
889             }
890             SqlExp right = comp.getVisitor().toSqlExp(args, root, left, 0, null);
891             if (left.next == null && right.next == null) {
892                 BinaryOpExp ans = new BinaryOpExp(left, BinaryOpExp.EQUAL,
893                         right);
894                 if (right instanceof ParamExp) {
895                     ParamExp p = (ParamExp)right;
896                     p.usage.expList = ans;
897                     p.usage.expCount = 1;
898                 }
899                 se.whereExp = ans;
900             } else {
901                 throw BindingSupportImpl.getInstance().internal(
902                         "not implemented");
903             }
904             return new ExistsExp(se, false);
905         }
906     }
907
908 }
909
Popular Tags