00001
package com.quadcap.sql;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
import java.io.ByteArrayOutputStream;
00042
import java.io.Externalizable;
00043
import java.io.IOException;
00044
import java.io.ObjectInput;
00045
import java.io.ObjectOutput;
00046
00047
import java.util.Vector;
00048
00049
import java.sql.ResultSet;
00050
import java.sql.SQLException;
00051
00052
import com.quadcap.sql.io.ObjectInputStream;
00053
import com.quadcap.sql.io.ObjectOutputStream;
00054
00055
import com.quadcap.sql.file.BlockFile;
00056
00057
import com.quadcap.sql.index.Btree;
00058
00059
import com.quadcap.sql.types.Op;
00060
import com.quadcap.sql.types.Value;
00061
00062
import com.quadcap.util.Debug;
00063
import com.quadcap.util.Util;
00064
00065
00066
00067
00068
00069
00070
00071 public class ImportedKeyConstraint extends ForeignKeyConstraint
00072 implements
Externalizable
00073 {
00074 long indexRoot = -1;
00075 transient Btree
index = null;
00076 transient Btree
fIndex = null;
00077 transient ExportedKeyConstraint ec = null;
00078
00079
00080
00081
00082 public ImportedKeyConstraint() {}
00083
00084
00085
00086
00087 public ImportedKeyConstraint(String name, String fTableName) {
00088 super(name, fTableName);
00089 }
00090
00091
00092
00093
00094 public ImportedKeyConstraint(String name, Vector colNames,
00095 String fTableName, Vector fColNames) {
00096 super(name, colNames, fTableName, fColNames);
00097 }
00098
00099
00100
00101
00102
00103 public void add(
Session session)
throws SQLException, IOException {
00104
Database db = session.getDatabase();
00105
BlockFile file = db.getFile();
00106 getFCols(db);
00107
if (fConstraint == null) {
00108
throw new SQLException(
"No suitable foreign key",
"23000");
00109 }
00110
00111
indexRoot = file.
newPage();
00112
index =
new Btree(file,
indexRoot,
true);
00113
00114 String ecName =
getExportConstraintName();
00115
ec =
new ExportedKeyConstraint(ecName, fColNames, table.
getName(),
00116 colNames,
this, fConstraint);
00117
ec.
setForeignKeyCols(
getColumns());
00118 session.doStep(
new AddConstraint(session, fTable,
ec,
false));
00119
00120
if (!table.
isUnderConstruction()) {
00121
IndexCursor c = table.
getCursor(session, null);
00122
if (c != null) {
00123
try {
00124
while (c.
next()) {
00125
Row row = c.
getRow();
00126
long rowId = c.
getRowId();
00127
checkInsert(session, row);
00128
applyInsert(session, row, rowId, null);
00129 }
00130 } finally {
00131 c.
close();
00132 }
00133 }
00134 }
00135 db.
updateRelation(fTable);
00136 }
00137
00138
00139
00140
00141
00142 public void delete(
Session session)
throws SQLException, IOException {
00143
00144
if (
indexRoot > 0) {
00145
Database db = session.getDatabase();
00146
getIndex(db);
00147
if (
index != null) {
00148
index.free();
00149
index = null;
00150 }
00151
indexRoot = -1;
00152 }
00153 }
00154
00155
00156
00157
00158 final String
getExportConstraintName() {
00159 StringBuffer sb =
new StringBuffer(
"__ec_");
00160 sb.append(table.
getName());
00161 sb.append(
'_');
00162 sb.append(name);
00163
return sb.toString();
00164 }
00165
00166
00167
00168
00169
00170 public void checkInsert(
Session session,
Row row)
00171
throws SQLException, IOException
00172 {
00173
Database db = session.getDatabase();
00174
int[] k =
getColumns();
00175
int[] fk = getFCols(db);
00176
00177
Row r =
new Row(fTable.
getColumnCount());
00178
boolean anyNull =
false;
00179
boolean allNull =
true;
00180
for (
int i = 0; i < k.length; i++) {
00181
int col = k[i];
00182
int fcol = fk[i];
00183
Value v = row.item(col);
00184
boolean isNull =
Value.isNull(v);
00185 anyNull |= isNull;
00186 allNull &= isNull;
00187 r.
set(fcol, v);
00188 }
00189
UniqueConstraint con = fTable.
getIndexForColumns(fk);
00190 byte[] key = con.
makeKey(session, r, 0);
00191 Btree fTree = con.
getIndex(db);
00192
if (fTree.get(key) == null && !allNull
00193 && (!anyNull || ((spec & (
Constraint.FULL |
00194
Constraint.PARTIAL)) != 0))) {
00195
if (isSelfReferencing(db) &&
checkSelfReferencing(row)) {
00196
00197 }
else {
00198
throw new SQLException(
00199
"Foreign Key Constraint Violation: no parent: " +
00200
this.
toString(),
"23000");
00201 }
00202 }
00203 }
00204
00205
00206
00207
00208 public void applyInsert(
Session session,
Row row,
long rowId,
00209
Constraint activeIndex)
00210
throws SQLException, IOException
00211 {
00212 byte[] key =
makeKey(session, row, rowId);
00213
AddIndexEntry add =
new AddIndexEntry(session,
this, key, rowId);
00214
if (activeIndex ==
this) {
00215 session.addPendingAction(add);
00216 }
else {
00217 session.doStep(add);
00218 }
00219 }
00220
00221
00222
00223
00224
00225 public void applyDelete(
Session session,
Row row,
long rowId,
00226
Constraint activeIndex)
00227
throws SQLException, IOException
00228 {
00229
Database db = session.getDatabase();
00230 byte[] key =
makeKey(session, row, rowId);
00231
if (
index == null)
getIndex(db);
00232
DeleteIndexEntry del =
new DeleteIndexEntry(session,
this, key);
00233
if (activeIndex ==
this) {
00234 session.addPendingAction(del);
00235 }
else {
00236 session.doStep(del);
00237 }
00238 }
00239
00240
00241
00242
00243
00244
00245
00246 public void checkUpdate(
Session session, byte[] oldKey,
Row row,
00247
Row oldRow,
long rowId,
Constraint activeIndex)
00248
throws SQLException, IOException
00249 {
00250 checkInsert(session, row);
00251
getComparator();
00252 byte[] key =
makeKey(session, row, rowId);
00253
if (activeIndex !=
this) oldKey = makeKey(session, oldRow, rowId);
00254
if (compare.
compare(key, oldKey) != 0) {
00255
UpdateIndex ui =
00256 (
UpdateIndex)session.getContext(
this,
isDeferred());
00257
if (ui == null) {
00258 ui =
new UpdateIndex(session,
this);
00259 session.putContext(
this,
isDeferred(), ui);
00260 }
00261 ui.
addEntry(key, oldKey, rowId);
00262 }
00263 }
00264
00265
00266
00267
00268
00269 boolean checkSelfReferencing(
Row row)
throws SQLException {
00270
boolean anyNull =
false;
00271
boolean allNull =
true;
00272
boolean allMatch =
true;
00273
for (
int i = 0; i < fCols.length; i++) {
00274
Value v = row.item(fCols[i]);
00275
Value r = row.item(columns[i]);
00276
boolean isNull =
Value.isNull(v);
00277 anyNull |= isNull;
00278 allNull &= isNull;
00279
if (allMatch) allMatch =
Value.boolOp(
Op.EQ, v, r);
00280 }
00281
if (allMatch || allNull)
return true;
00282
if (anyNull && (spec & (
Constraint.FULL |
Constraint.PARTIAL)) != 0)
00283
return true;
00284
return false;
00285 }
00286
00287
00288
00289
00290 final Btree
getForeignIndex(
Session session)
00291
throws SQLException, IOException
00292 {
00293
if (
fIndex == null) {
00294
fIndex = fConstraint.
getIndex(session.getDatabase());
00295 }
00296
return fIndex;
00297 }
00298
00299
00300
00301
00302 public ExportedKeyConstraint findExportedKeyConstraint(
Database db)
00303
throws IOException, SQLException
00304 {
00305
if (
ec == null) {
00306
Table t = getFTable(db);
00307 String ecName =
getExportConstraintName();
00308
ec = (
ExportedKeyConstraint)t.
getConstraint(ecName);
00309 }
00310
return ec;
00311 }
00312
00313
00314
00315
00316
00317
00318 byte[]
makeKey(
Session session,
Row row,
long rowId)
00319
throws SQLException
00320 {
00321
return Key.
makeKey(table, row,
getColumns(), rowId,
true);
00322 }
00323
00324
00325
00326
00327
00328
00329
00330 public Btree
getIndex(
Database db)
throws IOException {
00331
if (
index == null) {
00332
index =
new Btree(db.getFile(),
indexRoot,
false);
00333 }
00334
return index;
00335 }
00336
00337
00338
00339
00340 public void readExternal(ObjectInput in)
00341
throws IOException, ClassNotFoundException
00342 {
00343 super.readExternal(in);
00344
indexRoot = in.readLong();
00345 }
00346
00347
00348
00349
00350 public void writeExternal(ObjectOutput out)
throws IOException {
00351 super.writeExternal(out);
00352 out.writeLong(
indexRoot);
00353 }
00354
00355 }