1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.jdbc.sql;
20
21 import java.sql.SQLException;
22
23 import org.apache.commons.lang.ObjectUtils;
24 import org.apache.openjpa.jdbc.meta.ClassMapping;
25 import org.apache.openjpa.jdbc.meta.RelationId;
26 import org.apache.openjpa.jdbc.schema.Column;
27 import org.apache.openjpa.jdbc.schema.ColumnIO;
28 import org.apache.openjpa.jdbc.schema.ForeignKey;
29 import org.apache.openjpa.jdbc.schema.Table;
30 import org.apache.openjpa.kernel.OpenJPAStateManager;
31 import org.apache.openjpa.lib.util.Localizer;
32 import org.apache.openjpa.util.InvalidStateException;
33
34 /**
35 * Primary table row that tracks foreign keys and auto-inc columns.
36 *
37 * @author Abe White
38 * @nojavadoc
39 */
40 public class PrimaryRow
41 extends RowImpl {
42
43 // VALID flag in superclass uses 2 << 0
44 private static final byte PK_SET = 2 << 1;
45 private static final byte PK_WHERE = 2 << 2;
46 private static final byte DEPENDENT = 2 << 4;
47
48 private static final Localizer _loc = Localizer.forPackage
49 (PrimaryRow.class);
50
51 private OpenJPAStateManager _pk = null;
52 private ColumnIO _pkIO = null;
53 private OpenJPAStateManager[] _fkSet = null;
54 private ColumnIO[] _fkIO = null;
55 private OpenJPAStateManager[] _fkWhere = null;
56 private OpenJPAStateManager[] _relSet = null;
57 private RelationId[] _callbacks = null;
58 private Object _failed = null;
59 private int _idx = -1;
60
61 /**
62 * Constructor; supply table and action.
63 */
64 public PrimaryRow(Table table, int action, OpenJPAStateManager owner) {
65 this(table.getColumns(), action, owner);
66 }
67
68 protected PrimaryRow(Column[] cols, int action, OpenJPAStateManager owner) {
69 super(cols, action);
70 _pk = owner;
71 }
72
73 /**
74 * Mark this row as dependent on some other row.
75 */
76 public boolean isDependent() {
77 return (flags & DEPENDENT) > 0;
78 }
79
80 /**
81 * Mark this row as dependent on some other row.
82 */
83 public void setDependent(boolean dependent) {
84 if (dependent)
85 flags |= DEPENDENT;
86 else
87 flags &= ~DEPENDENT;
88 }
89
90 /**
91 * The index of this row in ordered row list.
92 */
93 public int getIndex() {
94 return _idx;
95 }
96
97 /**
98 * The index of this row in ordered row list.
99 */
100 public void setIndex(int idx) {
101 _idx = idx;
102 }
103
104 public Object getFailedObject() {
105 return _failed;
106 }
107
108 public void setFailedObject(Object failed) {
109 _failed = failed;
110 }
111
112 public OpenJPAStateManager getPrimaryKey() {
113 return _pk;
114 }
115
116 public void setPrimaryKey(OpenJPAStateManager sm)
117 throws SQLException {
118 setPrimaryKey(null, sm);
119 }
120
121 public void setPrimaryKey(ColumnIO io, OpenJPAStateManager sm) {
122 _pk = sm;
123 flags |= PK_SET;
124 _pkIO = io;
125
126 // force valid
127 setValid(true);
128 }
129
130 public void wherePrimaryKey(OpenJPAStateManager sm)
131 throws SQLException {
132 _pk = sm;
133 flags |= PK_WHERE;
134
135 // force valid
136 if (getAction() == ACTION_DELETE)
137 setValid(true);
138 }
139
140 /**
141 * Return the I/O information for the given set foreign key.
142 */
143 public ColumnIO getForeignKeyIO(ForeignKey fk) {
144 return (_fkIO == null) ? null : _fkIO[fk.getIndex()];
145 }
146
147 /**
148 * Return the value for the given foreign key. Values not needed for
149 * constraint analyses are not recorded.
150 */
151 public OpenJPAStateManager getForeignKeySet(ForeignKey fk) {
152 return (_fkSet == null) ? null : _fkSet[fk.getIndex()];
153 }
154
155 /**
156 * Return the value for the given foreign key. Values not needed for
157 * constraint analyses are not recorded.
158 */
159 public OpenJPAStateManager getForeignKeyWhere(ForeignKey fk) {
160 return (_fkWhere == null) ? null : _fkWhere[fk.getIndex()];
161 }
162
163 public void setForeignKey(ForeignKey fk, OpenJPAStateManager sm)
164 throws SQLException {
165 setForeignKey(fk, null, sm);
166 }
167
168 public void setForeignKey(ForeignKey fk, ColumnIO io,
169 OpenJPAStateManager sm)
170 throws SQLException {
171 if (!delayForeignKey(fk, sm, true))
172 super.setForeignKey(fk, io, sm);
173 else
174 recordForeignKey(fk, io, sm, true);
175 }
176
177 public void whereForeignKey(ForeignKey fk, OpenJPAStateManager sm)
178 throws SQLException {
179 if (!delayForeignKey(fk, sm, false))
180 super.whereForeignKey(fk, sm);
181 else
182 recordForeignKey(fk, null, sm, false);
183 }
184
185 public void clearForeignKey(ForeignKey fk)
186 throws SQLException {
187 super.clearForeignKey(fk);
188 if (_fkSet != null)
189 _fkSet[fk.getIndex()] = null;
190 if (_fkIO != null)
191 _fkIO[fk.getIndex()] = null;
192 }
193
194 /**
195 * If this is a delete, delay foreign keys to other deleted objects if
196 * the key is restricted. If this is an update or insert, delay foreign
197 * keys to other inserts if the key is not logical. If the foreign key
198 * is to a new record and the columns are auto-inc, record it.
199 */
200 private boolean delayForeignKey(ForeignKey fk, OpenJPAStateManager sm,
201 boolean set) {
202 if (sm == null)
203 return false;
204
205 if (getAction() == ACTION_DELETE)
206 return sm.isDeleted() && !fk.isDeferred()
207 && fk.getDeleteAction() == ForeignKey.ACTION_RESTRICT;
208
209 if (!sm.isNew() || sm.isFlushed())
210 return false;
211 if (!fk.isDeferred() && !fk.isLogical())
212 return true;
213 if (fk.isPrimaryKeyAutoAssigned())
214 return true;
215 return false;
216 }
217
218 /**
219 * Record a delayed foreign key.
220 */
221 private void recordForeignKey(ForeignKey fk, ColumnIO io,
222 OpenJPAStateManager sm, boolean set) {
223 if (set) {
224 // force valid
225 if (canSetAny(io, fk.getColumns().length
226 + fk.getConstantColumns().length, false))
227 setValid(true);
228
229 if (_fkSet == null)
230 _fkSet = new OpenJPAStateManager[getTable().
231 getForeignKeys().length];
232 _fkSet[fk.getIndex()] = sm;
233
234 if (_fkIO != null)
235 _fkIO[fk.getIndex()] = io;
236 else if (io != null && ((getAction() == ACTION_INSERT
237 && !io.isAllInsertable(fk, false))
238 || (getAction() != ACTION_INSERT
239 && !io.isAllUpdatable(fk, false)))) {
240 _fkIO = new ColumnIO[_fkSet.length];
241 _fkIO[fk.getIndex()] = io;
242 }
243 } else {
244 // force valid
245 if (getAction() == ACTION_DELETE)
246 setValid(true);
247
248 if (_fkWhere == null)
249 _fkWhere = new OpenJPAStateManager[getTable().
250 getForeignKeys().length];
251 _fkWhere[fk.getIndex()] = sm;
252 }
253 }
254
255 /**
256 * Return the recorded value for the given relation id column. Only
257 * values that are dependent on a new, unflushed auto-assigned instance
258 * are recorded.
259 */
260 public OpenJPAStateManager getRelationIdSet(Column col) {
261 return (_relSet == null) ? null : _relSet[getRelationIdIndex(col)];
262 }
263
264 /**
265 * Return the recorded callbacks for the given relation id column. Only
266 * values that are dependent on a new, unflushed auto-assigned instance
267 * are recorded.
268 */
269 public RelationId getRelationIdCallback(Column col) {
270 return (_callbacks == null) ? null
271 : _callbacks[getRelationIdIndex(col)];
272 }
273
274 public void setRelationId(Column col, OpenJPAStateManager sm,
275 RelationId rel)
276 throws SQLException {
277 if (sm == null || sm.getObjectId() != null || !sm.isNew()
278 || sm.isFlushed() || !isPrimaryKeyAutoAssigned(sm))
279 super.setRelationId(col, sm, rel);
280 else {
281 if (_relSet == null) {
282 Column[] cols = getTable().getRelationIdColumns();
283 _relSet = new OpenJPAStateManager[cols.length];
284 _callbacks = new RelationId[cols.length];
285 }
286 int idx = getRelationIdIndex(col);
287 _relSet[idx] = sm;
288 _callbacks[idx] = rel;
289 }
290 }
291
292 public void clearRelationId(Column col)
293 throws SQLException {
294 super.clearRelationId(col);
295 if (_relSet != null) {
296 int idx = getRelationIdIndex(col);
297 _relSet[idx] = null;
298 _callbacks[idx] = null;
299 }
300 }
301
302 /**
303 * Return the index into our relation id array of the value for the
304 * given column.
305 */
306 private int getRelationIdIndex(Column col) {
307 Column[] cols = getTable().getRelationIdColumns();
308 for (int i = 0; i < cols.length; i++)
309 if (cols[i] == col)
310 return i;
311 return -1;
312 }
313
314 /**
315 * Return true if any primary key columns of the given instance are
316 * auto-assigned.
317 */
318 private static boolean isPrimaryKeyAutoAssigned(OpenJPAStateManager sm) {
319 ClassMapping cls = (ClassMapping) sm.getMetaData();
320 while (cls.getJoinablePCSuperclassMapping() != null)
321 cls = cls.getJoinablePCSuperclassMapping();
322 Column[] cols = cls.getPrimaryKeyColumns();
323 for (int i = 0; i < cols.length; i++)
324 if (cols[i].isAutoAssigned())
325 return true;
326 return false;
327 }
328
329 protected void setObject(Column col, Object val, int metaType,
330 boolean overrideDefault)
331 throws SQLException {
332 // make sure we're not setting two different values
333 Object prev = getSet(col);
334 if (prev != null) {
335 if (prev == NULL)
336 prev = null;
337 if (!rowValueEquals(prev, val)) {
338 throw new InvalidStateException(_loc.get("diff-values",
339 new Object[]{ col.getFullName(),
340 (prev == null) ? null : prev.getClass(), prev,
341 (val == null) ? null : val.getClass(), val })).
342 setFatal(true);
343 }
344 }
345 super.setObject(col, val, metaType, overrideDefault);
346 }
347
348 /**
349 * Return true if the two values should be considered equal.
350 */
351 private static boolean rowValueEquals(Object o1, Object o2) {
352 if (ObjectUtils.equals(o1, o2))
353 return true;
354
355 // check for numeric equality (bug #1151)
356 return o1 instanceof Number && o2 instanceof Number
357 && ((Number) o1).doubleValue() == ((Number) o2).doubleValue();
358 }
359
360 protected String generateSQL(DBDictionary dict) {
361 try {
362 if ((flags & PK_SET) > 0)
363 super.setPrimaryKey(_pkIO, _pk);
364 if ((flags & PK_WHERE) > 0)
365 super.wherePrimaryKey(_pk);
366 if (_fkSet != null) {
367 ForeignKey[] fks = getTable().getForeignKeys();
368 ColumnIO io;
369 for (int i = 0; i < _fkSet.length; i++) {
370 if (_fkSet[i] != null) {
371 io = (_fkIO == null) ? null : _fkIO[i];
372 super.setForeignKey(fks[i], io, _fkSet[i]);
373 }
374 }
375 }
376 if (_relSet != null) {
377 Column[] cols = getTable().getRelationIdColumns();
378 for (int i = 0; i < _relSet.length; i++)
379 if (_relSet[i] != null)
380 super.setRelationId(cols[i], _relSet[i], _callbacks[i]);
381 }
382 if (_fkWhere != null) {
383 ForeignKey[] fks = getTable().getForeignKeys();
384 for (int i = 0; i < _fkWhere.length; i++)
385 if (_fkWhere[i] != null)
386 super.whereForeignKey(fks[i], _fkWhere[i]);
387 }
388 }
389 catch (SQLException se) {
390 throw SQLExceptions.getStore(se, dict);
391 }
392 return super.generateSQL(dict);
393 }
394
395 protected RowImpl newInstance(Column[] cols, int action) {
396 return new PrimaryRow(cols, action, _pk);
397 }
398
399 public void copyInto(RowImpl row, boolean whereOnly) {
400 super.copyInto(row, whereOnly);
401 if (!(row instanceof PrimaryRow))
402 return;
403
404 PrimaryRow prow = (PrimaryRow) row;
405 prow._pk = _pk;
406 prow._pkIO = _pkIO;
407 if ((flags & PK_WHERE) > 0)
408 prow.flags |= PK_WHERE;
409 if (!whereOnly && (flags & PK_SET) > 0)
410 prow.flags |= PK_SET;
411
412 if (_fkWhere != null) {
413 if (prow._fkWhere == null)
414 prow._fkWhere = new OpenJPAStateManager[_fkWhere.length];
415 System.arraycopy(_fkWhere, 0, prow._fkWhere, 0, _fkWhere.length);
416 }
417 if (!whereOnly && _fkSet != null) {
418 if (prow._fkSet == null)
419 prow._fkSet = new OpenJPAStateManager[_fkSet.length];
420 System.arraycopy(_fkSet, 0, prow._fkSet, 0, _fkSet.length);
421 if (_fkIO != null) {
422 if (prow._fkIO == null)
423 prow._fkIO = new ColumnIO[_fkIO.length];
424 System.arraycopy(_fkIO, 0, prow._fkIO, 0, _fkIO.length);
425 }
426 }
427 if (!whereOnly && _relSet != null) {
428 if (prow._relSet == null) {
429 prow._relSet = new OpenJPAStateManager[_relSet.length];
430 prow._callbacks = new RelationId[_callbacks.length];
431 }
432 System.arraycopy(_relSet, 0, prow._relSet, 0, _relSet.length);
433 System.arraycopy(_callbacks, 0, prow._callbacks, 0,
434 _callbacks.length);
435 }
436 }
437 }