KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > AlterTableConstantAction


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.AlterTableConstantAction
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.services.sanity.SanityManager;
25
26 import org.apache.derby.iapi.services.io.StreamStorable;
27
28 import org.apache.derby.iapi.error.StandardException;
29
30 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
31
32 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
33 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
34 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
35 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
36 import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;
37 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
38 import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
39 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
40 import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
41 import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;
42 import org.apache.derby.iapi.sql.dictionary.IndexLister;
43 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
44 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
45 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
46 import org.apache.derby.iapi.sql.dictionary.StatisticsDescriptor;
47 import org.apache.derby.iapi.sql.dictionary.DependencyDescriptor;
48 import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
49 import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
50 import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
51 import org.apache.derby.impl.sql.catalog.DDColumnDependableFinder;
52
53 import org.apache.derby.iapi.sql.StatementType;
54
55 import org.apache.derby.iapi.types.DataValueDescriptor;
56 import org.apache.derby.iapi.types.DataTypeDescriptor;
57 import org.apache.derby.iapi.types.DataValueFactory;
58
59 import org.apache.derby.iapi.reference.SQLState;
60
61 import org.apache.derby.iapi.sql.depend.Dependency;
62 import org.apache.derby.iapi.sql.depend.DependencyManager;
63 import org.apache.derby.iapi.sql.depend.Provider;
64 import org.apache.derby.iapi.sql.depend.ProviderInfo;
65
66 import org.apache.derby.iapi.reference.SQLState;
67
68 import org.apache.derby.iapi.sql.execute.ConstantAction;
69 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
70 import org.apache.derby.iapi.sql.execute.ExecRow;
71 import org.apache.derby.iapi.sql.execute.ExecutionFactory;
72
73 import org.apache.derby.iapi.store.access.ColumnOrdering;
74 import org.apache.derby.iapi.store.access.ConglomerateController;
75 import org.apache.derby.iapi.store.access.GroupFetchScanController;
76 import org.apache.derby.iapi.store.access.Qualifier;
77 import org.apache.derby.iapi.store.access.RowLocationRetRowSource;
78 import org.apache.derby.iapi.store.access.RowSource;
79 import org.apache.derby.iapi.store.access.RowUtil;
80 import org.apache.derby.iapi.store.access.ScanController;
81 import org.apache.derby.iapi.store.access.SortController;
82 import org.apache.derby.iapi.store.access.SortObserver;
83 import org.apache.derby.iapi.store.access.TransactionController;
84
85 import org.apache.derby.iapi.types.NumberDataValue;
86
87 import org.apache.derby.iapi.sql.Activation;
88 import org.apache.derby.iapi.sql.ResultSet;
89 import org.apache.derby.iapi.sql.Statement;
90 import org.apache.derby.iapi.sql.PreparedStatement;
91
92 import org.apache.derby.iapi.types.RowLocation;
93
94 import org.apache.derby.catalog.UUID;
95 import org.apache.derby.catalog.IndexDescriptor;
96 import org.apache.derby.catalog.DependableFinder;
97
98 import org.apache.derby.catalog.types.DefaultInfoImpl;
99 import org.apache.derby.catalog.types.StatisticsImpl;
100 import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
101
102 import java.sql.SQLException JavaDoc;
103 import java.util.Properties JavaDoc;
104 import java.util.Enumeration JavaDoc;
105
106 import org.apache.derby.iapi.services.io.FormatableBitSet;
107
108 import java.util.List JavaDoc;
109 import java.util.Iterator JavaDoc;
110
111 /**
112  * This class describes actions that are ALWAYS performed for an
113  * ALTER TABLE Statement at Execution time.
114  *
115  * @author Jerry Brenner.
116  */

117
118 class AlterTableConstantAction extends DDLSingleTableConstantAction
119  implements RowLocationRetRowSource
120 {
121
122     protected SchemaDescriptor sd;
123     protected String JavaDoc tableName;
124     protected UUID schemaId;
125     protected int tableType;
126     protected long tableConglomerateId;
127     protected ColumnInfo[] columnInfo;
128     protected ConstraintConstantAction[] constraintActions;
129     protected char lockGranularity;
130     private boolean compressTable;
131     private boolean sequential;
132     private int behavior;
133
134     // Alter table compress and Drop column
135
private boolean doneScan;
136     private boolean[] needToDropSort;
137     private boolean[] validRow;
138     private int bulkFetchSize = 16;
139     private int currentCompressRow;
140     private int numIndexes;
141     private int rowCount;
142     private long estimatedRowCount;
143     private long[] indexConglomerateNumbers;
144     private long[] sortIds;
145     private FormatableBitSet indexedCols;
146     private ConglomerateController compressHeapCC;
147     private ExecIndexRow[] indexRows;
148     private ExecRow[] baseRow;
149     private ExecRow currentRow;
150     private GroupFetchScanController compressHeapGSC;
151     private IndexRowGenerator[] compressIRGs;
152     private DataValueDescriptor[][] baseRowArray;
153     private RowLocation[] compressRL;
154     private SortController[] sorters;
155     private int columnPosition;
156     private ColumnOrdering[][] ordering;
157
158     private TableDescriptor td;
159
160
161     //truncate table
162
private boolean truncateTable;
163
164     // CONSTRUCTORS
165
private LanguageConnectionContext lcc;
166         private DataDictionary dd;
167         private DependencyManager dm;
168         private TransactionController tc;
169         private Activation activation;
170
171     /**
172      * Make the AlterAction for an ALTER TABLE statement.
173      *
174      * @param sd descriptor for the schema that table lives in.
175      * @param tableName Name of table.
176      * @param tableId UUID of table
177      * @param tableConglomerateId heap conglomerate number of table
178      * @param tableType Type of table (e.g., BASE).
179      * @param columnInfo Information on all the columns in the table.
180      * @param constraintActions ConstraintConstantAction[] for constraints
181      * @param lockGranularity The lock granularity.
182      * @param compressTable Whether or not this is a compress table
183      * @param behavior drop behavior for dropping column
184      * @param sequential If compress table/drop column, whether or not sequential
185      * @param truncateTable Whether or not this is a truncate table
186      */

187     AlterTableConstantAction(
188                                 SchemaDescriptor sd,
189                                 String JavaDoc tableName,
190                                 UUID tableId,
191                                 long tableConglomerateId,
192                                 int tableType,
193                                 ColumnInfo[] columnInfo,
194                                 ConstraintConstantAction[] constraintActions,
195                                 char lockGranularity,
196                                 boolean compressTable,
197                                 int behavior,
198                                 boolean sequential,
199                                 boolean truncateTable)
200     {
201         super(tableId);
202         this.sd = sd;
203         this.tableName = tableName;
204         this.tableConglomerateId = tableConglomerateId;
205         this.tableType = tableType;
206         this.columnInfo = columnInfo;
207         this.constraintActions = constraintActions;
208         this.lockGranularity = lockGranularity;
209         this.compressTable = compressTable;
210         this.behavior = behavior;
211         this.sequential = sequential;
212         this.truncateTable = truncateTable;
213
214         if (SanityManager.DEBUG)
215         {
216             SanityManager.ASSERT(sd != null, "schema descriptor is null");
217         }
218     }
219
220     // OBJECT METHODS
221

222     public String JavaDoc toString()
223     {
224         // Do not put this under SanityManager.DEBUG - it is needed for
225
// error reporting.
226

227         // we don't bother trying to print out the
228
// schema because we don't have it until execution
229
if(truncateTable)
230             return "TRUNCATE TABLE " + tableName;
231         else
232             return "ALTER TABLE " + tableName;
233     }
234
235     // INTERFACE METHODS
236

237     /**
238      * This is the guts of the Execution-time logic for ALTER TABLE.
239      *
240      * @see ConstantAction#executeConstantAction
241      *
242      * @exception StandardException Thrown on failure
243      */

244     public void executeConstantAction( Activation activation )
245                         throws StandardException
246     {
247         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
248         DataDictionary dd = lcc.getDataDictionary();
249         DependencyManager dm = dd.getDependencyManager();
250         TransactionController tc = lcc.getTransactionExecute();
251
252         /*
253         ** Inform the data dictionary that we are about to write to it.
254         ** There are several calls to data dictionary "get" methods here
255         ** that might be done in "read" mode in the data dictionary, but
256         ** it seemed safer to do this whole operation in "write" mode.
257         **
258         ** We tell the data dictionary we're done writing at the end of
259         ** the transaction.
260         */

261         dd.startWriting(lcc);
262
263         // now do the real work
264

265         // get an exclusive lock of the heap, to avoid deadlock on rows of
266
// SYSCOLUMNS etc datadictionary tables (track 879) and phantom table
267
// descriptor, in which case table shape could be changed by a
268
// concurrent thread doing add/drop column (track 3804 and 3825)
269

270         // older version (or at target) has to get td first, potential deadlock
271
if (tableConglomerateId == 0)
272         {
273             td = dd.getTableDescriptor(tableId);
274             if (td == null)
275             {
276                 throw StandardException.newException(
277                     SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tableName);
278             }
279             tableConglomerateId = td.getHeapConglomerateId();
280         }
281
282         lockTableForDDL(tc, tableConglomerateId, true);
283
284         td = dd.getTableDescriptor(tableId);
285         if (td == null)
286         {
287             throw StandardException.newException(
288                 SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tableName);
289         }
290
291         if(truncateTable)
292             dm.invalidateFor(td, DependencyManager.TRUNCATE_TABLE, lcc);
293         else
294             dm.invalidateFor(td, DependencyManager.ALTER_TABLE, lcc);
295         execGuts( activation );
296     }
297
298
299     /**
300       * Wrapper for this DDL action. Factored out so that our child,
301       * RepAlterTableConstantAction
302       * could enjoy the benefits of the startWriting() method above.
303       *
304       *
305       * @exception StandardException Thrown on failure
306       */

307     public void execGuts( Activation activation)
308                         throws StandardException
309     {
310         ColumnDescriptor columnDescriptor;
311         int numRows = 0;
312         boolean tableNeedsScanning = false;
313         boolean tableScanned = false;
314
315         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
316         DataDictionary dd = lcc.getDataDictionary();
317         DependencyManager dm = dd.getDependencyManager();
318         TransactionController tc = lcc.getTransactionExecute();
319
320         // Save the TableDescriptor off in the Activation
321
activation.setDDLTableDescriptor(td);
322
323         /*
324         ** If the schema descriptor is null, then
325         ** we must have just read ourselves in.
326         ** So we will get the corresponding schema
327         ** descriptor from the data dictionary.
328         */

329         if (sd == null)
330         {
331             sd = getAndCheckSchemaDescriptor(dd, schemaId, "ALTER TABLE");
332         }
333         
334
335         /* Prepare all dependents to invalidate. (This is there chance
336          * to say that they can't be invalidated. For example, an open
337          * cursor referencing a table/view that the user is attempting to
338          * alter.) If no one objects, then invalidate any dependent objects.
339          */

340         if(truncateTable)
341             dm.invalidateFor(td, DependencyManager.TRUNCATE_TABLE, lcc);
342         else
343             dm.invalidateFor(td, DependencyManager.ALTER_TABLE, lcc);
344
345         // Are we working on columns?
346
if (columnInfo != null)
347         {
348             /* NOTE: We only allow a single column to be added within
349              * each ALTER TABLE command at the language level. However,
350              * this may change some day, so we will try to plan for it.
351              */

352             /* for each new column, see if the user is adding a non-nullable
353              * column. This is only allowed on an empty table.
354              */

355             for (int ix = 0; ix < columnInfo.length; ix++)
356             {
357
358                 /* Is this new column non-nullable?
359                  * If so, it can only be added to an
360                  * empty table if it does not have a default value.
361                  * We need to scan the table to find out how many rows
362                  * there are.
363                  */

364                 if ((columnInfo[ix].action == ColumnInfo.CREATE) &&
365                     !(columnInfo[ix].dataType.isNullable()) &&
366                     (columnInfo[ix].defaultInfo == null) &&
367                     (columnInfo[ix].autoincInc == 0)
368                     )
369                 {
370                     tableNeedsScanning = true;
371                 }
372             }
373
374             // Scan the table if necessary
375
if (tableNeedsScanning)
376             {
377                 numRows = getSemiRowCount(tc);
378                 // Don't allow user to add non-nullable column to non-empty table
379
if (numRows > 0)
380                 {
381                     throw StandardException.newException(SQLState.LANG_ADDING_NON_NULL_COLUMN_TO_NON_EMPTY_TABLE,
382                                     td.getQualifiedName());
383                 }
384                 tableScanned = true;
385             }
386
387             // for each related column, stuff system.column
388
for (int ix = 0; ix < columnInfo.length; ix++)
389             {
390                 ColumnDescriptorList cdl = new ColumnDescriptorList();
391
392                 /* If there is a default value, use it, otherwise use null */
393                 
394                 // Are we adding a new column or modifying a default?
395

396                 if (columnInfo[ix].action == ColumnInfo.CREATE)
397                 {
398                     addNewColumnToTable(activation, ix);
399                 }
400                 else if (columnInfo[ix].action ==
401                          ColumnInfo.MODIFY_COLUMN_DEFAULT_RESTART ||
402                          columnInfo[ix].action == ColumnInfo.MODIFY_COLUMN_DEFAULT_INCREMENT)
403                 {
404                     modifyColumnDefault(activation, ix);
405                 }
406                 else if (columnInfo[ix].action ==
407                          ColumnInfo.MODIFY_COLUMN_TYPE)
408                 {
409                     modifyColumnType(activation, ix);
410                 }
411                 else if (columnInfo[ix].action ==
412                          ColumnInfo.MODIFY_COLUMN_CONSTRAINT)
413                 {
414                     modifyColumnConstraint(activation, columnInfo[ix].name, true);
415                 }
416                 else if (columnInfo[ix].action ==
417                          ColumnInfo.MODIFY_COLUMN_CONSTRAINT_NOT_NULL)
418                 {
419                     if (! tableScanned)
420                     {
421                         tableScanned = true;
422                         numRows = getSemiRowCount(tc);
423                     }
424                     // check that the data in the column is not null
425
String JavaDoc colNames[] = new String JavaDoc[1];
426                     colNames[0] = columnInfo[ix].name;
427                     boolean nullCols[] = new boolean[1];
428
429                     /* note validateNotNullConstraint returns true if the
430                      * column is nullable
431                      */

432                     if (validateNotNullConstraint(colNames, nullCols,
433                             numRows, lcc, SQLState.LANG_NULL_DATA_IN_NON_NULL_COLUMN))
434                     {
435                         /* nullable column - modify it to be not null
436                          * This is O.K. at this point since we would have
437                          * thrown an exception if any data was null
438                          */

439                         modifyColumnConstraint(activation, columnInfo[ix].name, false);
440                     }
441                 }
442                 else if (columnInfo[ix].action == ColumnInfo.DROP)
443                 {
444                     dropColumnFromTable(activation, ix);
445                 }
446                 else if (SanityManager.DEBUG)
447                 {
448                     SanityManager.THROWASSERT(
449                               "Unexpected action in AlterTableConstantAction");
450                 }
451             }
452         }
453
454         /* Create/Drop any constraints */
455         if (constraintActions != null)
456         {
457             for (int conIndex = 0; conIndex < constraintActions.length; conIndex++)
458             {
459                 ConstraintConstantAction cca = constraintActions[conIndex];
460
461                 if (cca instanceof CreateConstraintConstantAction)
462                 {
463                     int constraintType = cca.getConstraintType();
464
465                     /* Some constraint types require special checking:
466                      * Check - table must be empty, for now
467                      * Primary Key - table cannot already have a primary key
468                      */

469                     switch (constraintType)
470                     {
471                         case DataDictionary.PRIMARYKEY_CONSTRAINT:
472                             // Check to see if a constraint of the same type already exists
473
ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
474                             if (cdl.getPrimaryKey() != null)
475                             {
476                                 throw StandardException.newException(SQLState.LANG_ADD_PRIMARY_KEY_FAILED1,
477                                             td.getQualifiedName());
478                             }
479                             if (! tableScanned)
480                             {
481                                 tableScanned = true;
482                                 numRows = getSemiRowCount(tc);
483                             }
484
485                             break;
486                         case DataDictionary.CHECK_CONSTRAINT:
487                             if (! tableScanned)
488                             {
489                                 tableScanned = true;
490                                 numRows = getSemiRowCount(tc);
491                             }
492                             if (numRows > 0)
493                             {
494                                 /*
495                                 ** We are assuming that there will only be one
496                                 ** check constraint that we are adding, so it
497                                 ** is ok to do the check now rather than try
498                                 ** to lump together several checks.
499                                 */

500                                 ConstraintConstantAction.validateConstraint(
501                                             cca.getConstraintName(),
502                                             ((CreateConstraintConstantAction)cca).getConstraintText(),
503                                             td,
504                                             lcc, true);
505                             }
506                             break;
507                     }
508                 }
509                 else
510                 {
511                     if (SanityManager.DEBUG)
512                     {
513                         if (!(cca instanceof DropConstraintConstantAction))
514                         {
515                             SanityManager.THROWASSERT("constraintActions[" + conIndex +
516                             "] expected to be instanceof DropConstraintConstantAction not " +
517                             cca.getClass().getName());
518                         }
519                     }
520                 }
521                 constraintActions[conIndex].executeConstantAction(activation);
522             }
523         }
524
525         // Are we changing the lock granularity?
526
if (lockGranularity != '\0')
527         {
528             if (SanityManager.DEBUG)
529             {
530                 if (lockGranularity != 'T' &&
531                     lockGranularity != 'R')
532                 {
533                     SanityManager.THROWASSERT(
534                         "lockGranularity expected to be 'T'or 'R', not " + lockGranularity);
535                 }
536             }
537
538             // update the TableDescriptor
539
td.setLockGranularity(lockGranularity);
540             // update the DataDictionary
541
dd.updateLockGranularity(td, sd, lockGranularity, tc);
542         }
543
544         // Are we doing a compress table?
545
if (compressTable)
546         {
547             compressTable(activation);
548         }
549
550         // Are we doing a truncate table?
551
if (truncateTable)
552         {
553             truncateTable(activation);
554         }
555
556         
557     }
558
559     /**
560      * Workhorse for adding a new column to a table.
561      *
562      * @param ix the index of the column specfication in the ALTER
563      * statement-- currently we allow only one.
564      * @exception StandardException thrown on failure.
565      */

566     private void addNewColumnToTable(Activation activation,
567                                      int ix)
568             throws StandardException
569     {
570         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
571         DataDictionary dd = lcc.getDataDictionary();
572         DependencyManager dm = dd.getDependencyManager();
573         TransactionController tc = lcc.getTransactionExecute();
574
575         ColumnDescriptor columnDescriptor =
576             td.getColumnDescriptor(columnInfo[ix].name);
577         DataValueDescriptor storableDV;
578         int colNumber = td.getMaxColumnID() + ix;
579         DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
580
581         /* We need to verify that the table does not have an existing
582          * column with the same name before we try to add the new
583          * one as addColumnDescriptor() is a void method.
584          */

585         if (columnDescriptor != null)
586         {
587             throw
588                 StandardException.newException(
589                                                SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT,
590                                                columnDescriptor.getDescriptorType(),
591                                                columnInfo[ix].name,
592                                                td.getDescriptorType(),
593                                                td.getQualifiedName());
594         }
595
596         if (columnInfo[ix].defaultValue != null)
597             storableDV = columnInfo[ix].defaultValue;
598         else
599             storableDV = columnInfo[ix].dataType.getNull();
600
601         // Add the column to the conglomerate.(Column ids in store are 0-based)
602
tc.addColumnToConglomerate(td.getHeapConglomerateId(), colNumber,
603                                    storableDV);
604
605         UUID defaultUUID = columnInfo[ix].newDefaultUUID;
606
607         /* Generate a UUID for the default, if one exists
608          * and there is no default id yet.
609          */

610         if (columnInfo[ix].defaultInfo != null &&
611             defaultUUID == null)
612         {
613             defaultUUID = dd.getUUIDFactory().createUUID();
614         }
615
616         // Add the column to syscolumns.
617
// Column ids in system tables are 1-based
618
columnDescriptor = new ColumnDescriptor(
619                                                    columnInfo[ix].name,
620                                                    colNumber + 1,
621                                                    columnInfo[ix].dataType,
622                                                    columnInfo[ix].defaultValue,
623                                                    columnInfo[ix].defaultInfo,
624                                                    td,
625                                                    defaultUUID,
626                                                    columnInfo[ix].autoincStart,
627                                                    columnInfo[ix].autoincInc
628                                                    );
629
630         dd.addDescriptor(columnDescriptor, td,
631                          DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
632
633         // now add the column to the tables column descriptor list.
634
td.getColumnDescriptorList().add(columnDescriptor);
635
636         if (columnDescriptor.isAutoincrement())
637         {
638             updateNewAutoincrementColumn(activation, columnInfo[ix].name,
639                                          columnInfo[ix].autoincStart,
640                                          columnInfo[ix].autoincInc);
641         }
642
643         // Update the new column to its default, if it has a non-null default
644
if (columnDescriptor.hasNonNullDefault())
645         {
646             updateNewColumnToDefault(activation,
647                                 columnInfo[ix].name,
648                                 columnInfo[ix].defaultInfo.getDefaultText(),
649                                 lcc);
650         }
651
652         // Update SYSCOLPERMS table which tracks the permissions granted
653
// at columns level. The sytem table has a bit map of all the columns
654
// in the user table to help determine which columns have the
655
// permission granted on them. Since we are adding a new column,
656
// that bit map needs to be expanded and initialize the bit for it
657
// to 0 since at the time of ADD COLUMN, no permissions have been
658
// granted on that new column.
659
//
660
dd.updateSYSCOLPERMSforAddColumnToUserTable(td.getUUID(), tc);
661     }
662
663     /**
664      * Workhorse for dropping a column from a table.
665      *
666      * @param ix the index of the column specfication in the ALTER
667      * statement-- currently we allow only one.
668      * @exception StandardException thrown on failure.
669      */

670     private void dropColumnFromTable(Activation activation,
671                                      int ix)
672             throws StandardException
673     {
674         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
675         DataDictionary dd = lcc.getDataDictionary();
676         DependencyManager dm = dd.getDependencyManager();
677         TransactionController tc = lcc.getTransactionExecute();
678
679
680         ColumnDescriptor columnDescriptor =
681             td.getColumnDescriptor(columnInfo[ix].name);
682
683         // We already verified this in bind, but do it again
684
if (columnDescriptor == null)
685         {
686             throw
687                 StandardException.newException(
688                                         SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
689                                         columnInfo[ix].name,
690                                         td.getQualifiedName());
691         }
692
693         DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
694         ColumnDescriptorList tab_cdl = td.getColumnDescriptorList();
695         int size = tab_cdl.size();
696
697         // can NOT drop a column if it is the only one in the table
698
if (size == 1)
699         {
700             throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
701                             dm.getActionString(DependencyManager.DROP_COLUMN),
702                             "THE *LAST* COLUMN " + columnInfo[ix].name,
703                             "TABLE",
704                             td.getQualifiedName() );
705         }
706
707         columnPosition = columnDescriptor.getPosition();
708         boolean cascade = (behavior == StatementType.DROP_CASCADE);
709
710         FormatableBitSet toDrop = new FormatableBitSet(size + 1);
711         toDrop.set(columnPosition);
712         td.setReferencedColumnMap(toDrop);
713
714         dm.invalidateFor(td, DependencyManager.DROP_COLUMN, lcc);
715                     
716         // If column has a default we drop the default and any dependencies
717
if (columnDescriptor.getDefaultInfo() != null)
718         {
719             DefaultDescriptor defaultDesc = columnDescriptor.getDefaultDescriptor(dd);
720             dm.clearDependencies(lcc, defaultDesc);
721         }
722
723         // need to deal with triggers if has referencedColumns
724
GenericDescriptorList tdl = dd.getTriggerDescriptors(td);
725         Enumeration JavaDoc descs = tdl.elements();
726         while (descs.hasMoreElements())
727         {
728             TriggerDescriptor trd = (TriggerDescriptor) descs.nextElement();
729             int[] referencedCols = trd.getReferencedCols();
730             if (referencedCols == null)
731                 continue;
732             int refColLen = referencedCols.length, j;
733             boolean changed = false;
734             for (j = 0; j < refColLen; j++)
735             {
736                 if (referencedCols[j] > columnPosition)
737                     changed = true;
738                 else if (referencedCols[j] == columnPosition)
739                 {
740                     if (cascade)
741                     {
742                         DropTriggerConstantAction.dropTriggerDescriptor(lcc, dm, dd, tc, trd, activation);
743                         activation.addWarning(
744                             StandardException.newWarning(SQLState.LANG_TRIGGER_DROPPED,
745                                 trd.getName(), td.getName()));
746                     }
747                     else
748                     { // we'd better give an error if don't drop it,
749
// otherwsie there would be unexpected behaviors
750
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
751                                         dm.getActionString(DependencyManager.DROP_COLUMN),
752                                         columnInfo[ix].name, "TRIGGER",
753                                         trd.getName() );
754                     }
755                     break;
756                 }
757             }
758
759             // change triggers to refer to columns in new positions
760
if (j == refColLen && changed)
761             {
762                 dd.dropTriggerDescriptor(trd, tc);
763                 for (j = 0; j < refColLen; j++)
764                 {
765                     if (referencedCols[j] > columnPosition)
766                         referencedCols[j]--;
767                 }
768                 dd.addDescriptor(trd, sd,
769                                  DataDictionary.SYSTRIGGERS_CATALOG_NUM,
770                                  false, tc);
771             }
772         }
773
774         ConstraintDescriptorList csdl = dd.getConstraintDescriptors(td);
775         int csdl_size = csdl.size();
776
777         // we want to remove referenced primary/unique keys in the second
778
// round. This will ensure that self-referential constraints will
779
// work OK.
780
int tbr_size = 0;
781         ConstraintDescriptor[] toBeRemoved = new ConstraintDescriptor[csdl_size];
782
783         // let's go downwards, don't want to get messed up while removing
784
for (int i = csdl_size - 1; i >= 0; i--)
785         {
786             ConstraintDescriptor cd = csdl.elementAt(i);
787             int[] referencedColumns = cd.getReferencedColumns();
788             int numRefCols = referencedColumns.length, j;
789             boolean changed = false;
790             for (j = 0; j < numRefCols; j++)
791             {
792                 if (referencedColumns[j] > columnPosition)
793                     changed = true;
794                 if (referencedColumns[j] == columnPosition)
795                     break;
796             }
797             if (j == numRefCols) // column not referenced
798
{
799                 if ((cd instanceof CheckConstraintDescriptor) && changed)
800                 {
801                     dd.dropConstraintDescriptor(td, cd, tc);
802                     for (j = 0; j < numRefCols; j++)
803                     {
804                         if (referencedColumns[j] > columnPosition)
805                             referencedColumns[j]--;
806                     }
807                     ((CheckConstraintDescriptor) cd).setReferencedColumnsDescriptor(new ReferencedColumnsDescriptorImpl(referencedColumns));
808                     dd.addConstraintDescriptor(cd, tc);
809                 }
810                 continue;
811             }
812
813             if (! cascade)
814             {
815                 if (numRefCols > 1 || cd.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT)
816                 {
817                     throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
818                                         dm.getActionString(DependencyManager.DROP_COLUMN),
819                                         columnInfo[ix].name, "CONSTRAINT",
820                                         cd.getConstraintName() );
821                 }
822             }
823
824             if (cd instanceof ReferencedKeyConstraintDescriptor)
825             {
826                 // restrict will raise an error in invalidate if really referenced
827
toBeRemoved[tbr_size++] = cd;
828                 continue;
829             }
830
831             // drop now in all other cases
832
dm.invalidateFor(cd, DependencyManager.DROP_CONSTRAINT,
833                                     lcc);
834             DropConstraintConstantAction.dropConstraintAndIndex(dm, td, dd,
835                              cd, tc, lcc, true);
836             activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED,
837                 cd.getConstraintName(), td.getName()));
838         }
839
840         for (int i = tbr_size - 1; i >= 0; i--)
841         {
842             ConstraintDescriptor cd = toBeRemoved[i];
843             DropConstraintConstantAction.dropConstraintAndIndex(dm, td, dd, cd,
844                         tc, lcc, false);
845             activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED,
846                     cd.getConstraintName(), td.getName()));
847
848             if (cascade)
849             {
850                 ConstraintDescriptorList fkcdl = dd.getForeignKeys(cd.getUUID());
851                 for (int j = 0; j < fkcdl.size(); j++)
852                 {
853                     ConstraintDescriptor fkcd = (ConstraintDescriptor) fkcdl.elementAt(j);
854                     dm.invalidateFor(fkcd,
855                                     DependencyManager.DROP_CONSTRAINT,
856                                     lcc);
857
858                     DropConstraintConstantAction.dropConstraintAndIndex(
859                         dm, fkcd.getTableDescriptor(), dd, fkcd, tc, lcc, true);
860                     activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED,
861                         fkcd.getConstraintName(), fkcd.getTableDescriptor().getName()));
862                 }
863             }
864
865             dm.invalidateFor(cd, DependencyManager.DROP_CONSTRAINT, lcc);
866             dm.clearDependencies(lcc, cd);
867         }
868
869         compressTable(activation);
870
871         // drop the column from syscolumns
872
dd.dropColumnDescriptor(td.getUUID(), columnInfo[ix].name, tc);
873         ColumnDescriptor[] cdlArray = new ColumnDescriptor[size - columnDescriptor.getPosition()];
874
875         for (int i = columnDescriptor.getPosition(), j = 0; i < size; i++, j++)
876         {
877             ColumnDescriptor cd = (ColumnDescriptor) tab_cdl.elementAt(i);
878             dd.dropColumnDescriptor(td.getUUID(), cd.getColumnName(), tc);
879             cd.setPosition(i);
880             cdlArray[j] = cd;
881         }
882         dd.addDescriptorArray(cdlArray, td,
883                               DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
884
885         List deps = dd.getProvidersDescriptorList(td.getObjectID().toString());
886         for (Iterator JavaDoc depsIterator = deps.listIterator(); depsIterator.hasNext();)
887         {
888             DependencyDescriptor depDesc = (DependencyDescriptor) depsIterator.next();
889             DependableFinder finder = depDesc.getProviderFinder();
890             if (finder instanceof DDColumnDependableFinder)
891             {
892                 DDColumnDependableFinder colFinder = (DDColumnDependableFinder) finder;
893                 FormatableBitSet oldColumnBitMap = new FormatableBitSet(colFinder.getColumnBitMap());
894                 FormatableBitSet newColumnBitMap = new FormatableBitSet(oldColumnBitMap);
895                 newColumnBitMap.clear();
896                 int bitLen = oldColumnBitMap.getLength();
897                 for (int i = 0; i < bitLen; i++)
898                 {
899                     if (i < columnPosition && oldColumnBitMap.isSet(i))
900                         newColumnBitMap.set(i);
901                     if (i > columnPosition && oldColumnBitMap.isSet(i))
902                         newColumnBitMap.set(i - 1);
903                 }
904                 if (newColumnBitMap.equals(oldColumnBitMap))
905                     continue;
906                 dd.dropStoredDependency(depDesc, tc);
907                 colFinder.setColumnBitMap(newColumnBitMap.getByteArray());
908                 dd.addDescriptor(depDesc, null,
909                                  DataDictionary.SYSDEPENDS_CATALOG_NUM,
910                                  true, tc);
911             }
912         }
913     }
914
915     private void modifyColumnType(Activation activation,
916                                   int ix)
917         throws StandardException
918     {
919         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
920         DataDictionary dd = lcc.getDataDictionary();
921         TransactionController tc = lcc.getTransactionExecute();
922
923         ColumnDescriptor columnDescriptor =
924             td.getColumnDescriptor(columnInfo[ix].name),
925             newColumnDescriptor = null;
926
927         newColumnDescriptor =
928             new ColumnDescriptor(columnInfo[ix].name,
929                                     columnDescriptor.getPosition(),
930                                     columnInfo[ix].dataType,
931                                     columnDescriptor.getDefaultValue(),
932                                     columnDescriptor.getDefaultInfo(),
933                                     td,
934                                     columnDescriptor.getDefaultUUID(),
935                                     columnInfo[ix].autoincStart,
936                                     columnInfo[ix].autoincInc
937                                     );
938         
939
940
941         // Update the ColumnDescriptor with new default info
942
dd.dropColumnDescriptor(td.getUUID(), columnInfo[ix].name, tc);
943         dd.addDescriptor(newColumnDescriptor, td,
944                          DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
945     }
946
947     /**
948      * Workhorse for modifying column level constraints.
949      * Right now it is restricted to modifying a null constraint to a not null
950      * constraint.
951      */

952     private void modifyColumnConstraint(Activation activation,
953                                         String JavaDoc colName,
954                                         boolean nullability)
955         throws StandardException
956     {
957         LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
958         DataDictionary dd = lcc.getDataDictionary();
959         TransactionController tc = lcc.getTransactionExecute();
960
961         ColumnDescriptor columnDescriptor =
962             td.getColumnDescriptor(colName),
963             newColumnDescriptor = null;
964         DataTypeDescriptor dataType = columnDescriptor.getType();
965
966         // set nullability
967
dataType.setNullability(nullability);
968
969         newColumnDescriptor =
970              new ColumnDescriptor(colName,
971                                     columnDescriptor.getPosition(),
972                                     dataType,
973                                     columnDescriptor.getDefaultValue(),
974                                     columnDescriptor.getDefaultInfo(),
975                                     td,
976                                     columnDescriptor.getDefaultUUID(),
977                                     columnDescriptor.getAutoincStart(),
978                                     columnDescriptor.getAutoincInc());
979         
980
981
982         // Update the ColumnDescriptor with new default info
983
dd.dropColumnDescriptor(td.getUUID(), colName, tc);
984         dd.addDescriptor(newColumnDescriptor, td,
985                          DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
986         
987     }
988     /**
989      * Workhorse for modifying the default value of a column.
990      *
991      * @param activation activation
992      * @param ix the index of the column specfication in the ALTER
993      * statement-- currently we allow only one.
994      * @exception StandardException, thrown on error.
995      */

996     private void modifyColumnDefault(Activation activation,
997                                      int ix)
998             throws StandardException
999     {
1000        LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
1001        DataDictionary dd = lcc.getDataDictionary();
1002        DependencyManager dm = dd.getDependencyManager();
1003        TransactionController tc = lcc.getTransactionExecute();
1004
1005        ColumnDescriptor columnDescriptor =
1006            td.getColumnDescriptor(columnInfo[ix].name);
1007        DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
1008        int columnPosition = columnDescriptor.getPosition();
1009
1010        // Clean up after the old default, if non-null
1011
if (columnDescriptor.hasNonNullDefault())
1012        {
1013            // Invalidate off of the old default
1014
DefaultDescriptor defaultDescriptor = new DefaultDescriptor(dd, columnInfo[ix].oldDefaultUUID,
1015                                         td.getUUID(), columnPosition);
1016
1017        
1018            dm.invalidateFor(defaultDescriptor, DependencyManager.MODIFY_COLUMN_DEFAULT, lcc);
1019        
1020            // Drop any dependencies
1021
dm.clearDependencies(lcc, defaultDescriptor);
1022        }
1023
1024        UUID defaultUUID = columnInfo[ix].newDefaultUUID;
1025
1026        /* Generate a UUID for the default, if one exists
1027         * and there is no default id yet.
1028         */

1029        if (columnInfo[ix].defaultInfo != null &&
1030            defaultUUID == null)
1031        {
1032            defaultUUID = dd.getUUIDFactory().createUUID();
1033        }
1034
1035        /* Get a ColumnDescriptor reflecting the new default */
1036        columnDescriptor = new ColumnDescriptor(
1037                                                   columnInfo[ix].name,
1038                                                   columnPosition,
1039                                                   columnInfo[ix].dataType,
1040                                                   columnInfo[ix].defaultValue,
1041                                                   columnInfo[ix].defaultInfo,
1042                                                   td,
1043                                                   defaultUUID,
1044                                                   columnInfo[ix].autoincStart,
1045                                                   columnInfo[ix].autoincInc,
1046                                                   columnInfo[ix].autoinc_create_or_modify_Start_Increment
1047                                                   );
1048
1049        // Update the ColumnDescriptor with new default info
1050
dd.dropColumnDescriptor(td.getUUID(), columnInfo[ix].name, tc);
1051        dd.addDescriptor(columnDescriptor, td,
1052                         DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
1053    
1054        if (columnInfo[ix].action == ColumnInfo.MODIFY_COLUMN_DEFAULT_INCREMENT)
1055        {
1056            // adding an autoincrement default-- calculate the maximum value
1057
// of the autoincrement column.
1058
long maxValue = getColumnMax(activation, td, columnInfo[ix].name,
1059                                         columnInfo[ix].autoincInc,
1060                                         columnInfo[ix].autoincStart);
1061            dd.setAutoincrementValue(tc, td.getUUID(), columnInfo[ix].name,
1062                                     maxValue, true);
1063        } else if (columnInfo[ix].action == ColumnInfo.MODIFY_COLUMN_DEFAULT_RESTART)
1064        {
1065            dd.setAutoincrementValue(tc, td.getUUID(), columnInfo[ix].name,
1066                     columnInfo[ix].autoincStart, false);
1067        }
1068    }
1069
1070
1071    /* NOTE: compressTable can also be called for
1072     * ALTER TABLE <t> DROP COLUMN <c>;
1073     */

1074    private void compressTable(Activation activation)
1075        throws StandardException
1076    {
1077        ExecRow emptyHeapRow;
1078        long newHeapConglom;
1079        Properties JavaDoc properties = new Properties JavaDoc();
1080        RowLocation rl;
1081        this.lcc = activation.getLanguageConnectionContext();
1082        this.dd = lcc.getDataDictionary();
1083        this.dm = dd.getDependencyManager();
1084        this.tc = lcc.getTransactionExecute();
1085        this.activation = activation;
1086
1087        if (SanityManager.DEBUG)
1088        {
1089            if (lockGranularity != '\0')
1090            {
1091                SanityManager.THROWASSERT(
1092                    "lockGranularity expected to be '\0', not " + lockGranularity);
1093            }
1094            SanityManager.ASSERT(! compressTable || columnInfo == null,
1095                "columnInfo expected to be null");
1096            SanityManager.ASSERT(constraintActions == null,
1097                "constraintActions expected to be null");
1098        }
1099        emptyHeapRow = td.getEmptyExecRow(lcc.getContextManager());
1100        compressHeapCC = tc.openConglomerate(
1101                                td.getHeapConglomerateId(),
1102                                false,
1103                                TransactionController.OPENMODE_FORUPDATE,
1104                                TransactionController.MODE_TABLE,
1105                                TransactionController.ISOLATION_SERIALIZABLE);
1106
1107        // invalidate any prepared statements that
1108
// depended on this table (including this one)
1109
// bug 3653 has threads that start up and block on our lock, but do
1110
// not see they have to recompile their plan. We now invalidate earlier
1111
// however they still might recompile using the old conglomerate id before we
1112
// commit our DD changes.
1113
//
1114
dm.invalidateFor(td, DependencyManager.COMPRESS_TABLE, lcc);
1115
1116
1117        rl = compressHeapCC.newRowLocationTemplate();
1118
1119        // Get the properties on the old heap
1120
compressHeapCC.getInternalTablePropertySet(properties);
1121        compressHeapCC.close();
1122        compressHeapCC = null;
1123
1124        // Create an array to put base row template
1125
baseRow = new ExecRow[bulkFetchSize];
1126        baseRowArray = new DataValueDescriptor[bulkFetchSize][];
1127        validRow = new boolean[bulkFetchSize];
1128
1129        /* Set up index info */
1130        getAffectedIndexes(activation);
1131        // Get an array of RowLocation template
1132
compressRL = new RowLocation[bulkFetchSize];
1133        indexRows = new ExecIndexRow[numIndexes];
1134        if (! compressTable)
1135        {
1136            ExecRow newRow = activation.getExecutionFactory().getValueRow(emptyHeapRow.nColumns() - 1);
1137            for (int i = 0; i < newRow.nColumns(); i++)
1138            {
1139                newRow.setColumn(i + 1, i < columnPosition - 1 ?
1140                                         emptyHeapRow.getColumn(i + 1) :
1141                                         emptyHeapRow.getColumn(i + 1 + 1));
1142            }
1143            emptyHeapRow = newRow;
1144        }
1145        setUpAllSorts(emptyHeapRow, rl);
1146
1147        // Start by opening a full scan on the base table.
1148
openBulkFetchScan(td.getHeapConglomerateId());
1149
1150        // Get the estimated row count for the sorters
1151
estimatedRowCount = compressHeapGSC.getEstimatedRowCount();
1152
1153        // Create the array of base row template
1154
for (int i = 0; i < bulkFetchSize; i++)
1155        {
1156            // create a base row template
1157
baseRow[i] = td.getEmptyExecRow(lcc.getContextManager());
1158            baseRowArray[i] = baseRow[i].getRowArray();
1159            compressRL[i] = compressHeapGSC.newRowLocationTemplate();
1160        }
1161
1162
1163        newHeapConglom = tc.createAndLoadConglomerate(
1164                                    "heap",
1165                                    emptyHeapRow.getRowArray(),
1166                                    null, //column sort order - not required for heap
1167
properties,
1168                                    TransactionController.IS_DEFAULT,
1169                                    this,
1170                                    (long[]) null);
1171
1172        closeBulkFetchScan();
1173
1174        // Set the "estimated" row count
1175
ScanController compressHeapSC = tc.openScan(
1176                            newHeapConglom,
1177                            false,
1178                            TransactionController.OPENMODE_FORUPDATE,
1179                            TransactionController.MODE_TABLE,
1180                            TransactionController.ISOLATION_SERIALIZABLE,
1181                            (FormatableBitSet) null,
1182                            (DataValueDescriptor[]) null,
1183                            0,
1184                            (Qualifier[][]) null,
1185                            (DataValueDescriptor[]) null,
1186                            0);
1187        
1188        compressHeapSC.setEstimatedRowCount(rowCount);
1189
1190        compressHeapSC.close();
1191        compressHeapSC = null; // RESOLVE DJD CLEANUP
1192

1193        /*
1194        ** Inform the data dictionary that we are about to write to it.
1195        ** There are several calls to data dictionary "get" methods here
1196        ** that might be done in "read" mode in the data dictionary, but
1197        ** it seemed safer to do this whole operation in "write" mode.
1198        **
1199        ** We tell the data dictionary we're done writing at the end of
1200        ** the transaction.
1201        */

1202        dd.startWriting(lcc);
1203
1204        // Update all indexes
1205
if (compressIRGs.length > 0)
1206        {
1207            updateAllIndexes(newHeapConglom, dd);
1208        }
1209
1210        /* Update the DataDictionary
1211         * RESOLVE - this will change in 1.4 because we will get
1212         * back the same conglomerate number
1213         */

1214        // Get the ConglomerateDescriptor for the heap
1215
long oldHeapConglom = td.getHeapConglomerateId();
1216        ConglomerateDescriptor cd = td.getConglomerateDescriptor(oldHeapConglom);
1217
1218        // Update sys.sysconglomerates with new conglomerate #
1219
dd.updateConglomerateDescriptor(cd, newHeapConglom, tc);
1220        // Drop the old conglomerate
1221
tc.dropConglomerate(oldHeapConglom);
1222        cleanUp();
1223    }
1224
1225
1226    
1227    /*
1228     * TRUNCATE TABLE TABLENAME; (quickly removes all the rows from table and
1229     * it's correctponding indexes).
1230     * Truncate is implemented by dropping the existing conglomerates(heap,indexes) and recreating a
1231     * new ones with the properties of dropped conglomerates. Currently Store
1232     * does not have support to truncate existing conglomerated until store
1233     * supports it , this is the only way to do it.
1234     * Error Cases: Truncate error cases same as other DDL's statements except
1235     * 1)Truncate is not allowed when the table is references by another table.
1236     * 2)Truncate is not allowed when there are enabled delete triggers on the table.
1237     * Note: Because conglomerate number is changed during recreate process all the statements will be
1238     * marked as invalide and they will get recompiled internally on their next
1239     * execution. This is okay because truncate makes the number of rows to zero
1240     * it may be good idea to recompile them becuase plans are likely to be
1241     * incorrect. Recompile is done internally by cloudscape, user does not have
1242     * any effect.
1243     */

1244    private void truncateTable(Activation activation)
1245        throws StandardException
1246    {
1247        ExecRow emptyHeapRow;
1248        long newHeapConglom;
1249        Properties JavaDoc properties = new Properties JavaDoc();
1250        RowLocation rl;
1251        this.lcc = activation.getLanguageConnectionContext();
1252        this.dd = lcc.getDataDictionary();
1253        this.dm = dd.getDependencyManager();
1254        this.tc = lcc.getTransactionExecute();
1255        this.activation = activation;
1256
1257        if (SanityManager.DEBUG)
1258        {
1259            if (lockGranularity != '\0')
1260            {
1261                SanityManager.THROWASSERT(
1262                    "lockGranularity expected to be '\0', not " + lockGranularity);
1263            }
1264            SanityManager.ASSERT(columnInfo == null,
1265                "columnInfo expected to be null");
1266            SanityManager.ASSERT(constraintActions == null,
1267                 "constraintActions expected to be null");
1268        }
1269
1270
1271        //truncate table is not allowed if there are any tables referencing it.
1272
//except if it is self referencing.
1273
ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
1274        for(int index = 0; index < cdl.size(); index++)
1275        {
1276            ConstraintDescriptor cd = cdl.elementAt(index);
1277            if (cd instanceof ReferencedKeyConstraintDescriptor)
1278            {
1279                ReferencedKeyConstraintDescriptor rfcd = (ReferencedKeyConstraintDescriptor) cd;
1280                if(rfcd.hasNonSelfReferencingFK(ConstraintDescriptor.ENABLED))
1281                {
1282                    throw StandardException.newException(SQLState.LANG_NO_TRUNCATE_ON_FK_REFERENCE_TABLE,td.getName());
1283                }
1284            }
1285        }
1286
1287        //truncate is not allowed when there are enabled DELETE triggers
1288
GenericDescriptorList tdl = dd.getTriggerDescriptors(td);
1289        Enumeration JavaDoc descs = tdl.elements();
1290        while (descs.hasMoreElements())
1291        {
1292            TriggerDescriptor trd = (TriggerDescriptor) descs.nextElement();
1293            if (trd.listensForEvent(TriggerDescriptor.TRIGGER_EVENT_DELETE) &&
1294                trd.isEnabled())
1295            {
1296                throw
1297                    StandardException.newException(SQLState.LANG_NO_TRUNCATE_ON_ENABLED_DELETE_TRIGGERS,
1298                                                   td.getName(),trd.getName());
1299            }
1300        }
1301
1302        //gather information from the existing conglomerate to create new one.
1303
emptyHeapRow = td.getEmptyExecRow(lcc.getContextManager());
1304        compressHeapCC = tc.openConglomerate(
1305                                td.getHeapConglomerateId(),
1306                                false,
1307                                TransactionController.OPENMODE_FORUPDATE,
1308                                TransactionController.MODE_TABLE,
1309                                TransactionController.ISOLATION_SERIALIZABLE);
1310
1311        // invalidate any prepared statements that
1312
// depended on this table (including this one)
1313
// bug 3653 has threads that start up and block on our lock, but do
1314
// not see they have to recompile their plan. We now invalidate earlier
1315
// however they still might recompile using the old conglomerate id before we
1316
// commit our DD changes.
1317
//
1318
dm.invalidateFor(td, DependencyManager.TRUNCATE_TABLE, lcc);
1319
1320        rl = compressHeapCC.newRowLocationTemplate();
1321        // Get the properties on the old heap
1322
compressHeapCC.getInternalTablePropertySet(properties);
1323        compressHeapCC.close();
1324        compressHeapCC = null;
1325
1326        //create new conglomerate
1327
newHeapConglom = tc.createConglomerate(
1328                                    "heap",
1329                                    emptyHeapRow.getRowArray(),
1330                                    null, //column sort order - not required for heap
1331
properties,
1332                                    TransactionController.IS_DEFAULT);
1333        
1334        /* Set up index info to perform truncate on them*/
1335        getAffectedIndexes(activation);
1336        if(numIndexes > 0)
1337        {
1338            indexRows = new ExecIndexRow[numIndexes];
1339            ordering = new ColumnOrdering[numIndexes][];
1340            for (int index = 0; index < numIndexes; index++)
1341            {
1342                // create a single index row template for each index
1343
indexRows[index] = compressIRGs[index].getIndexRowTemplate();
1344                compressIRGs[index].getIndexRow(emptyHeapRow,
1345                                              rl,
1346                                              indexRows[index],
1347                                              (FormatableBitSet) null);
1348                /* For non-unique indexes, we order by all columns + the RID.
1349                 * For unique indexes, we just order by the columns.
1350                 * No need to try to enforce uniqueness here as
1351                 * index should be valid.
1352                 */

1353                int[] baseColumnPositions = compressIRGs[index].baseColumnPositions();
1354                boolean[] isAscending = compressIRGs[index].isAscending();
1355                int numColumnOrderings;
1356                numColumnOrderings = baseColumnPositions.length + 1;
1357                ordering[index] = new ColumnOrdering[numColumnOrderings];
1358                for (int ii =0; ii < numColumnOrderings - 1; ii++)
1359                {
1360                    ordering[index][ii] = new IndexColumnOrder(ii, isAscending[ii]);
1361                }
1362                ordering[index][numColumnOrderings - 1] = new IndexColumnOrder(numColumnOrderings - 1);
1363            }
1364        }
1365
1366        /*
1367        ** Inform the data dictionary that we are about to write to it.
1368        ** There are several calls to data dictionary "get" methods here
1369        ** that might be done in "read" mode in the data dictionary, but
1370        ** it seemed safer to do this whole operation in "write" mode.
1371        **
1372        ** We tell the data dictionary we're done writing at the end of
1373        ** the transaction.
1374        */

1375        dd.startWriting(lcc);
1376
1377        // truncate all indexes
1378
if(numIndexes > 0)
1379        {
1380            long[] newIndexCongloms = new long[numIndexes];
1381            for (int index = 0; index < numIndexes; index++)
1382            {
1383                updateIndex(newHeapConglom, dd, index, newIndexCongloms);
1384            }
1385        }
1386
1387        // Update the DataDictionary
1388
// Get the ConglomerateDescriptor for the heap
1389
long oldHeapConglom = td.getHeapConglomerateId();
1390        ConglomerateDescriptor cd = td.getConglomerateDescriptor(oldHeapConglom);
1391
1392        // Update sys.sysconglomerates with new conglomerate #
1393
dd.updateConglomerateDescriptor(cd, newHeapConglom, tc);
1394        // Drop the old conglomerate
1395
tc.dropConglomerate(oldHeapConglom);
1396        cleanUp();
1397    }
1398
1399
1400    /**
1401     * Update all of the indexes on a table when doing a bulk insert
1402     * on an empty table.
1403     *
1404     * @exception StandardException thrown on error
1405     */

1406    private void updateAllIndexes(long newHeapConglom,
1407                                  DataDictionary dd)
1408        throws StandardException
1409    {
1410        long[] newIndexCongloms = new long[numIndexes];
1411
1412        /* Populate each index (one at a time or all at once). */
1413        if (sequential)
1414        {
1415            // First sorter populated during heap compression
1416
if (numIndexes >= 1)
1417            {
1418                updateIndex(newHeapConglom, dd, 0, newIndexCongloms);
1419            }
1420            for (int index = 1; index < numIndexes; index++)
1421            {
1422                // Scan heap and populate next sorter
1423
openBulkFetchScan(newHeapConglom);
1424                while (getNextRowFromRowSource() != null)
1425                {
1426                    objectifyStreamingColumns();
1427                    insertIntoSorter(index, compressRL[currentCompressRow - 1]);
1428                }
1429                updateIndex(newHeapConglom, dd, index, newIndexCongloms);
1430                closeBulkFetchScan();
1431            }
1432        }
1433        else
1434        {
1435            for (int index = 0; index < numIndexes; index++)
1436            {
1437                updateIndex(newHeapConglom, dd, index, newIndexCongloms);
1438            }
1439        }
1440    }
1441
1442    private void updateIndex(long newHeapConglom, DataDictionary dd,
1443                             int index, long[] newIndexCongloms)
1444        throws StandardException
1445    {
1446        ConglomerateController indexCC;
1447        Properties JavaDoc properties = new Properties JavaDoc();
1448        ConglomerateDescriptor cd;
1449        // Get the ConglomerateDescriptor for the index
1450
cd = td.getConglomerateDescriptor(indexConglomerateNumbers[index]);
1451
1452        // Build the properties list for the new conglomerate
1453
indexCC = tc.openConglomerate(
1454                            indexConglomerateNumbers[index],
1455                            false,
1456                            TransactionController.OPENMODE_FORUPDATE,
1457                            TransactionController.MODE_TABLE,
1458                            TransactionController.ISOLATION_SERIALIZABLE);
1459
1460        // Get the properties on the old index
1461
indexCC.getInternalTablePropertySet(properties);
1462
1463        /* Create the properties that language supplies when creating the
1464         * the index. (The store doesn't preserve these.)
1465         */

1466        int indexRowLength = indexRows[index].nColumns();
1467        properties.put("baseConglomerateId", Long.toString(newHeapConglom));
1468        if (cd.getIndexDescriptor().isUnique())
1469        {
1470            properties.put("nUniqueColumns",
1471                           Integer.toString(indexRowLength - 1));
1472        }
1473        else
1474        {
1475            properties.put("nUniqueColumns",
1476                           Integer.toString(indexRowLength));
1477        }
1478        properties.put("rowLocationColumn",
1479                        Integer.toString(indexRowLength - 1));
1480        properties.put("nKeyFields", Integer.toString(indexRowLength));
1481
1482        indexCC.close();
1483
1484        // We can finally drain the sorter and rebuild the index
1485
// RESOLVE - all indexes are btrees right now
1486
// Populate the index.
1487

1488        RowLocationRetRowSource cCount = null;
1489        boolean updateStatistics = false;
1490        if(!truncateTable)
1491        {
1492            sorters[index].close();
1493            sorters[index] = null;
1494
1495            if (td.statisticsExist(cd))
1496            {
1497                cCount = new CardinalityCounter(tc.openSortRowSource(sortIds[index]));
1498                updateStatistics = true;
1499            }
1500            else
1501                cCount = new CardinalityCounter(tc.openSortRowSource(sortIds[index]));
1502
1503            newIndexCongloms[index] = tc.createAndLoadConglomerate(
1504                                   "BTREE",
1505                                   indexRows[index].getRowArray(),
1506                                   ordering[index],
1507                                   properties,
1508                                   TransactionController.IS_DEFAULT,
1509                                   cCount,
1510                                   (long[]) null);
1511
1512            //For an index, if the statistics already exist, then drop them.
1513
//The statistics might not exist for an index if the index was
1514
//created when the table was empty.
1515
//At ALTER TABLE COMPRESS time, for both kinds of indexes
1516
//(ie one with preexisting statistics and with no statistics),
1517
//create statistics for them if the table is not empty.
1518
//DERBY-737 "SYSCS_UTIL.SYSCS_COMPRESS_TABLE should create
1519
//statistics if they do not exist"
1520
if (updateStatistics)
1521                dd.dropStatisticsDescriptors(td.getUUID(), cd.getUUID(), tc);
1522            
1523            long numRows;
1524            if ((numRows = ((CardinalityCounter)cCount).getRowCount()) > 0)
1525            {
1526                long[] c = ((CardinalityCounter)cCount).getCardinality();
1527                for (int i = 0; i < c.length; i++)
1528                {
1529                    StatisticsDescriptor statDesc =
1530                        new StatisticsDescriptor(dd, dd.getUUIDFactory().createUUID(),
1531                                cd.getUUID(), td.getUUID(), "I", new StatisticsImpl(numRows, c[i]),
1532                                i + 1);
1533                    dd.addDescriptor(statDesc, null, // no parent descriptor
1534
DataDictionary.SYSSTATISTICS_CATALOG_NUM,
1535                            true, tc); // no error on duplicate.
1536
}
1537            }
1538        }else
1539        {
1540            newIndexCongloms[index] = tc.createConglomerate(
1541                                   "BTREE",
1542                                    indexRows[index].getRowArray(),
1543                                    ordering[index],
1544                                    properties,
1545                                   TransactionController.IS_DEFAULT);
1546
1547
1548            //on truncate drop the statistics because we know for sure
1549
//rowscount is zero and existing statistic will be invalid.
1550
if (td.statisticsExist(cd))
1551                dd.dropStatisticsDescriptors(td.getUUID(), cd.getUUID(), tc);
1552        }
1553
1554        /* Update the DataDictionary
1555         * RESOLVE - this will change in 1.4 because we will get
1556         * back the same conglomerate number
1557         *
1558         * Update sys.sysconglomerates with new conglomerate #, we need to
1559         * update all (if any) duplicate index entries sharing this same
1560         * conglomerate.
1561         */

1562        dd.updateConglomerateDescriptor(
1563                td.getConglomerateDescriptors(indexConglomerateNumbers[index]),
1564                newIndexCongloms[index], tc);
1565
1566        // Drop the old conglomerate
1567
tc.dropConglomerate(indexConglomerateNumbers[index]);
1568    }
1569
1570
1571    /**
1572     * Get info on the indexes on the table being compressed.
1573     *
1574     * @exception StandardException Thrown on error
1575     */

1576    private void getAffectedIndexes(Activation activation)
1577        throws StandardException
1578    {
1579        IndexLister indexLister = td.getIndexLister( );
1580
1581        /* We have to get non-distinct index row generaters and conglom numbers
1582         * here and then compress it to distinct later because drop column
1583         * will need to change the index descriptor directly on each index
1584         * entry in SYSCONGLOMERATES, on duplicate indexes too.
1585         */

1586        compressIRGs = indexLister.getIndexRowGenerators();
1587        numIndexes = compressIRGs.length;
1588        indexConglomerateNumbers = indexLister.getIndexConglomerateNumbers();
1589
1590        if (! (compressTable || truncateTable)) // then it's drop column
1591
{
1592            for (int i = 0; i < compressIRGs.length; i++)
1593            {
1594                int[] baseColumnPositions = compressIRGs[i].baseColumnPositions();
1595                int j;
1596                for (j = 0; j < baseColumnPositions.length; j++)
1597                    if (baseColumnPositions[j] == columnPosition) break;
1598                if (j == baseColumnPositions.length) // not related
1599
continue;
1600                    
1601                if (baseColumnPositions.length == 1 ||
1602                    (behavior == StatementType.DROP_CASCADE && compressIRGs[i].isUnique()))
1603                {
1604                    numIndexes--;
1605                    /* get first conglomerate with this conglom number each time
1606                     * and each duplicate one will be eventually all dropped
1607                     */

1608                    ConglomerateDescriptor cd = td.getConglomerateDescriptor
1609                                                (indexConglomerateNumbers[i]);
1610                    DropIndexConstantAction.dropIndex(dm, dd, tc, cd, td, activation.getLanguageConnectionContext());
1611
1612                    compressIRGs[i] = null; // mark it
1613
continue;
1614                }
1615                // give an error for unique index on multiple columns including
1616
// the column we are to drop (restrict), such index is not for
1617
// a constraint, because constraints have already been handled
1618
if (compressIRGs[i].isUnique())
1619                {
1620                    ConglomerateDescriptor cd = td.getConglomerateDescriptor
1621                                                (indexConglomerateNumbers[i]);
1622                    throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
1623                                        dm.getActionString(DependencyManager.DROP_COLUMN),
1624                                        columnInfo[0].name, "UNIQUE INDEX",
1625                                        cd.getConglomerateName() );
1626                }
1627            }
1628            IndexRowGenerator[] newIRGs = new IndexRowGenerator[numIndexes];
1629            long[] newIndexConglomNumbers = new long[numIndexes];
1630
1631            for (int i = 0, j = 0; i < numIndexes; i++, j++)
1632            {
1633                while (compressIRGs[j] == null)
1634                    j++;
1635
1636                int[] baseColumnPositions = compressIRGs[j].baseColumnPositions();
1637                newIRGs[i] = compressIRGs[j];
1638                newIndexConglomNumbers[i] = indexConglomerateNumbers[j];
1639
1640                boolean[] isAscending = compressIRGs[j].isAscending();
1641                boolean reMakeArrays = false;
1642                int size = baseColumnPositions.length;
1643                for (int k = 0; k < size; k++)
1644                {
1645                    if (baseColumnPositions[k] > columnPosition)
1646                        baseColumnPositions[k]--;
1647                    else if (baseColumnPositions[k] == columnPosition)
1648                    {
1649                        baseColumnPositions[k] = 0; // mark it
1650
reMakeArrays = true;
1651                    }
1652                }
1653                if (reMakeArrays)
1654                {
1655                    size--;
1656                    int[] newBCP = new int[size];
1657                    boolean[] newIsAscending = new boolean[size];
1658                    for (int k = 0, step = 0; k < size; k++)
1659                    {
1660                        if (step == 0 && baseColumnPositions[k + step] == 0)
1661                            step++;
1662                        newBCP[k] = baseColumnPositions[k + step];
1663                        newIsAscending[k] = isAscending[k + step];
1664                    }
1665                    IndexDescriptor id = compressIRGs[j].getIndexDescriptor();
1666                    id.setBaseColumnPositions(newBCP);
1667                    id.setIsAscending(newIsAscending);
1668                    id.setNumberOfOrderedColumns(id.numberOfOrderedColumns() - 1);
1669                }
1670            }
1671            compressIRGs = newIRGs;
1672            indexConglomerateNumbers = newIndexConglomNumbers;
1673        }
1674
1675        /* Now we are done with updating each index descriptor entry directly
1676         * in SYSCONGLOMERATES (for duplicate index as well), from now on, our
1677         * work should apply ONLY once for each real conglomerate, so we
1678         * compress any duplicate indexes now.
1679         */

1680        Object JavaDoc[] compressIndexResult =
1681            compressIndexArrays(indexConglomerateNumbers, compressIRGs);
1682
1683        if (compressIndexResult != null)
1684        {
1685            indexConglomerateNumbers = (long[]) compressIndexResult[1];
1686            compressIRGs = (IndexRowGenerator[]) compressIndexResult[2];
1687            numIndexes = indexConglomerateNumbers.length;
1688        }
1689
1690        indexedCols = new FormatableBitSet(compressTable || truncateTable ? td.getNumberOfColumns() + 1 :
1691                                                  td.getNumberOfColumns());
1692        for (int index = 0; index < numIndexes; index++)
1693        {
1694            int[] colIds = compressIRGs[index].getIndexDescriptor().baseColumnPositions();
1695
1696            for (int index2 = 0; index2 < colIds.length; index2++)
1697            {
1698                indexedCols.set(colIds[index2]);
1699            }
1700        }
1701    }
1702
1703    /**
1704     * Set up to update all of the indexes on a table when doing a bulk insert
1705     * on an empty table.
1706     *
1707     * @exception StandardException thrown on error
1708     */

1709    private void setUpAllSorts(ExecRow sourceRow,
1710                               RowLocation rl)
1711        throws StandardException
1712    {
1713        ordering = new ColumnOrdering[numIndexes][];
1714
1715        needToDropSort = new boolean[numIndexes];
1716        sortIds = new long[numIndexes];
1717
1718        /* For each index, build a single index row and a sorter. */
1719        for (int index = 0; index < numIndexes; index++)
1720        {
1721            // create a single index row template for each index
1722
indexRows[index] = compressIRGs[index].getIndexRowTemplate();
1723
1724            // Get an index row based on the base row
1725
// (This call is only necessary here because we need to pass a template to the sorter.)
1726
compressIRGs[index].getIndexRow(sourceRow,
1727                                              rl,
1728                                              indexRows[index],
1729                                              (FormatableBitSet) null);
1730
1731            /* For non-unique indexes, we order by all columns + the RID.
1732             * For unique indexes, we just order by the columns.
1733             * No need to try to enforce uniqueness here as
1734             * index should be valid.
1735             */

1736            int[] baseColumnPositions = compressIRGs[index].baseColumnPositions();
1737            boolean[] isAscending = compressIRGs[index].isAscending();
1738            int numColumnOrderings;
1739            SortObserver sortObserver = null;
1740            /* We can only reuse the wrappers when doing an
1741             * external sort if there is only 1 index. Otherwise,
1742             * we could get in a situation where 1 sort reuses a
1743             * wrapper that is still in use in another sort.
1744             */

1745            boolean reuseWrappers = (numIndexes == 1);
1746            numColumnOrderings = baseColumnPositions.length + 1;
1747            sortObserver = new BasicSortObserver(false, false,
1748                                                 indexRows[index],
1749                                                 reuseWrappers);
1750            ordering[index] = new ColumnOrdering[numColumnOrderings];
1751            for (int ii =0; ii < numColumnOrderings - 1; ii++)
1752            {
1753                ordering[index][ii] = new IndexColumnOrder(ii, isAscending[ii]);
1754            }
1755            ordering[index][numColumnOrderings - 1] = new IndexColumnOrder(numColumnOrderings - 1);
1756
1757            // create the sorters
1758
sortIds[index] = tc.createSort(
1759                               (Properties JavaDoc)null,
1760                                indexRows[index].getRowArrayClone(),
1761                                ordering[index],
1762                                sortObserver,
1763                                false, // not in order
1764
estimatedRowCount, // est rows
1765
-1 // est row size, -1 means no idea
1766
);
1767        }
1768    
1769        sorters = new SortController[numIndexes];
1770        // Open the sorts
1771
for (int index = 0; index < numIndexes; index++)
1772        {
1773            sorters[index] = tc.openSort(sortIds[index]);
1774            needToDropSort[index] = true;
1775        }
1776    }
1777
1778    // RowSource interface
1779

1780    /**
1781     * @see RowSource#getValidColumns
1782     */

1783    public FormatableBitSet getValidColumns()
1784    {
1785        // All columns are valid
1786
return null;
1787    }
1788    
1789    /**
1790     * @see RowSource#getNextRowFromRowSource
1791     * @exception StandardException on error
1792     */

1793    public DataValueDescriptor[] getNextRowFromRowSource()
1794        throws StandardException
1795    {
1796        currentRow = null;
1797        // Time for a new bulk fetch?
1798
if ((! doneScan) &&
1799            (currentCompressRow == bulkFetchSize || !validRow[currentCompressRow]))
1800        {
1801            int bulkFetched = 0;
1802
1803            bulkFetched = compressHeapGSC.fetchNextGroup(baseRowArray, compressRL);
1804
1805            doneScan = (bulkFetched != bulkFetchSize);
1806            currentCompressRow = 0;
1807            rowCount += bulkFetched;
1808            for (int index = 0; index < bulkFetched; index++)
1809            {
1810                validRow[index] = true;
1811            }
1812            for (int index = bulkFetched; index < bulkFetchSize; index++)
1813            {
1814                validRow[index] = false;
1815            }
1816        }
1817
1818        if (validRow[currentCompressRow])
1819        {
1820            if (compressTable)
1821                currentRow = baseRow[currentCompressRow];
1822            else
1823            {
1824                if (currentRow == null)
1825                    currentRow = activation.getExecutionFactory().getValueRow(baseRowArray[currentCompressRow].length - 1);
1826                for (int i = 0; i < currentRow.nColumns(); i++)
1827                {
1828                    currentRow.setColumn(i + 1, i < columnPosition - 1 ?
1829                                        baseRow[currentCompressRow].getColumn(i+1) :
1830                                        baseRow[currentCompressRow].getColumn(i+1+1));
1831                }
1832            }
1833            currentCompressRow++;
1834        }
1835
1836        if (currentRow != null)
1837        {
1838            /* Let the target preprocess the row. For now, this
1839             * means doing an in place clone on any indexed columns
1840             * to optimize cloning and so that we don't try to drain
1841             * a stream multiple times.
1842             */

1843            if (compressIRGs.length > 0)
1844            {
1845                /* Do in-place cloning of all of the key columns */
1846                currentRow = currentRow.getClone(indexedCols);
1847            }
1848
1849            return currentRow.getRowArray();
1850        }
1851
1852        return null;
1853    }
1854
1855    /**
1856     * @see RowSource#needsToClone
1857     */

1858    public boolean needsToClone()
1859    {
1860        return(true);
1861    }
1862
1863    /**
1864     * @see RowSource#closeRowSource
1865     */

1866    public void closeRowSource()
1867    {
1868        // Do nothing here - actual work will be done in close()
1869
}
1870
1871
1872    // RowLocationRetRowSource interface
1873

1874    /**
1875     * @see RowLocationRetRowSource#needsRowLocation
1876     */

1877    public boolean needsRowLocation()
1878    {
1879        // Only true if table has indexes
1880
return (numIndexes > 0);
1881    }
1882
1883    /**
1884     * @see RowLocationRetRowSource#rowLocation
1885     * @exception StandardException on error
1886     */

1887    public void rowLocation(RowLocation rl)
1888        throws StandardException
1889    {
1890        /* Set up sorters, etc. if 1st row and there are indexes */
1891        if (compressIRGs.length > 0)
1892        {
1893            objectifyStreamingColumns();
1894
1895            /* Put the row into the indexes. If sequential,
1896             * then we only populate the 1st sorter when compressing
1897             * the heap.
1898             */

1899            int maxIndex = compressIRGs.length;
1900            if (maxIndex > 1 && sequential)
1901            {
1902                maxIndex = 1;
1903            }
1904            for (int index = 0; index < maxIndex; index++)
1905            {
1906                insertIntoSorter(index, rl);
1907            }
1908        }
1909    }
1910
1911    private void objectifyStreamingColumns()
1912        throws StandardException
1913    {
1914        // Objectify any the streaming columns that are indexed.
1915
for (int i = 0; i < currentRow.getRowArray().length; i++)
1916        {
1917            /* Object array is 0-based,
1918             * indexedCols is 1-based.
1919             */

1920            if (! indexedCols.get(i + 1))
1921            {
1922                continue;
1923            }
1924
1925            if (currentRow.getRowArray()[i] instanceof StreamStorable)
1926            {
1927                ((DataValueDescriptor) currentRow.getRowArray()[i]).getObject();
1928            }
1929        }
1930    }
1931
1932    private void insertIntoSorter(int index, RowLocation rl)
1933        throws StandardException
1934    {
1935        // Get a new object Array for the index
1936
indexRows[index].getNewObjectArray();
1937        // Associate the index row with the source row
1938
compressIRGs[index].getIndexRow(currentRow,
1939                                        (RowLocation) rl.cloneObject(),
1940                                        indexRows[index],
1941                                        (FormatableBitSet) null);
1942
1943        // Insert the index row into the matching sorter
1944
sorters[index].insert(indexRows[index].getRowArray());
1945    }
1946
1947    /**
1948     * @see ResultSet#cleanUp
1949     *
1950     * @exception StandardException Thrown on error
1951     */

1952    public void cleanUp() throws StandardException
1953    {
1954        if (compressHeapCC != null)
1955        {
1956            compressHeapCC.close();
1957            compressHeapCC = null;
1958        }
1959
1960        if (compressHeapGSC != null)
1961        {
1962            closeBulkFetchScan();
1963        }
1964
1965        // Close each sorter
1966
if (sorters != null)
1967        {
1968            for (int index = 0; index < compressIRGs.length; index++)
1969            {
1970                if (sorters[index] != null)
1971                {
1972                    sorters[index].close();
1973                }
1974                sorters[index] = null;
1975            }
1976        }
1977
1978        if (needToDropSort != null)
1979        {
1980            for (int index = 0; index < needToDropSort.length; index++)
1981            {
1982                if (needToDropSort[index])
1983                {
1984                    tc.dropSort(sortIds[index]);
1985                    needToDropSort[index] = false;
1986                }
1987            }
1988        }
1989    }
1990
1991    // class implementation
1992

1993    /**
1994     * Return the "semi" row count of a table. We are only interested in
1995     * whether the table has 0, 1 or > 1 rows.
1996     *
1997     *
1998     * @return Number of rows (0, 1 or > 1) in table.
1999     *
2000     * @exception StandardException Thrown on failure
2001     */

2002    private int getSemiRowCount(TransactionController tc)
2003        throws StandardException
2004    {
2005        int numRows = 0;
2006
2007        ScanController sc = tc.openScan(td.getHeapConglomerateId(),
2008                         false, // hold
2009
0, // open read only
2010
TransactionController.MODE_TABLE,
2011                         TransactionController.ISOLATION_SERIALIZABLE,
2012                         RowUtil.EMPTY_ROW_BITSET, // scanColumnList
2013
null, // start position
2014
ScanController.GE, // startSearchOperation
2015
null, // scanQualifier
2016
null, //stop position - through last row
2017
ScanController.GT); // stopSearchOperation
2018

2019        while (sc.next())
2020        {
2021            numRows++;
2022
2023            // We're only interested in whether the table has 0, 1 or > 1 rows
2024
if (numRows == 2)
2025            {
2026                break;
2027            }
2028        }
2029        sc.close();
2030
2031        return numRows;
2032    }
2033
2034    /**
2035     * Update a new column with its default.
2036     * We could do the scan ourself here, but
2037     * instead we get a nested connection and
2038     * issue the appropriate update statement.
2039     *
2040     * @param columnName column name
2041     * @param defaultText default text
2042     * @param lcc the language connection context
2043     *
2044     * @exception StandardException if update to default fails
2045     */

2046    private void updateNewColumnToDefault
2047    (
2048        Activation activation,
2049        String JavaDoc columnName,
2050        String JavaDoc defaultText,
2051        LanguageConnectionContext lcc
2052    )
2053        throws StandardException
2054    {
2055        /* Need to use delimited identifiers for all object names
2056         * to ensure correctness.
2057         */

2058        String JavaDoc updateStmt = "UPDATE \"" + td.getSchemaName() + "\".\"" +
2059                            td.getName() + "\" SET \"" +
2060                             columnName + "\" = " + defaultText;
2061
2062
2063        AlterTableConstantAction.executeUpdate(lcc, updateStmt);
2064    }
2065
2066    private static void executeUpdate(LanguageConnectionContext lcc, String JavaDoc updateStmt) throws StandardException
2067    {
2068        PreparedStatement ps = lcc.prepareInternalStatement(updateStmt);
2069
2070        // This is a substatement; for now, we do not set any timeout
2071
// for it. We might change this behaviour later, by linking
2072
// timeout to its parent statement's timeout settings.
2073
ResultSet rs = ps.execute(lcc, true, 0L);
2074        rs.close();
2075        rs.finish();
2076    }
2077
2078    /**
2079     * computes the minimum/maximum value in a column of a table.
2080     */

2081    private long getColumnMax(Activation activation, TableDescriptor td, String JavaDoc columnName,
2082                              long increment, long initial)
2083                              throws StandardException
2084    {
2085        String JavaDoc maxStr = (increment > 0) ? "MAX" : "MIN";
2086        String JavaDoc maxStmt = "SELECT " + maxStr + "(\"" + columnName + "\")" +
2087                "FROM \"" + td.getSchemaName() + "\".\"" + td.getName() + "\"";
2088
2089
2090        LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
2091        PreparedStatement ps = lcc.prepareInternalStatement(maxStmt);
2092
2093        // This is a substatement, for now we do not set any timeout for it
2094
// We might change this later by linking timeout to parent statement
2095
ResultSet rs = ps.execute(lcc, false, 0L);
2096        DataValueDescriptor[] rowArray = rs.getNextRow().getRowArray();
2097        rs.close();
2098        rs.finish();
2099
2100        return rowArray[0].getLong();
2101    }
2102
2103    private void dropAllColumnDefaults(UUID tableId, DataDictionary dd)
2104        throws StandardException
2105    {
2106        ColumnDescriptorList cdl = td.getColumnDescriptorList();
2107        int cdlSize = cdl.size();
2108        
2109        for(int index = 0; index < cdlSize; index++)
2110        {
2111            ColumnDescriptor cd = (ColumnDescriptor) cdl.elementAt(index);
2112
2113            // If column has a default we drop the default and
2114
// any dependencies
2115
if (cd.getDefaultInfo() != null)
2116            {
2117                DefaultDescriptor defaultDesc = cd.getDefaultDescriptor(dd);
2118                dm.clearDependencies(lcc, defaultDesc);
2119            }
2120        }
2121    }
2122
2123    private void openBulkFetchScan(long heapConglomNumber)
2124        throws StandardException
2125    {
2126        doneScan = false;
2127        compressHeapGSC = tc.openGroupFetchScan(
2128                            heapConglomNumber,
2129                            false, // hold
2130
0, // open base table read only
2131
TransactionController.MODE_TABLE,
2132                            TransactionController.ISOLATION_SERIALIZABLE,
2133                            null, // all fields as objects
2134
(DataValueDescriptor[]) null, // startKeyValue
2135
0, // not used when giving null start posn.
2136
null, // qualifier
2137
(DataValueDescriptor[]) null, // stopKeyValue
2138
0); // not used when giving null stop posn.
2139
}
2140
2141    private void closeBulkFetchScan()
2142        throws StandardException
2143    {
2144        compressHeapGSC.close();
2145        compressHeapGSC = null;
2146    }
2147
2148    /**
2149     * Update values in a new autoincrement column being added to a table.
2150     * This is similar to updateNewColumnToDefault whereby we issue an
2151     * update statement using a nested connection. The UPDATE statement
2152     * uses a static method in ConnectionInfo (which is not documented)
2153     * which returns the next value to be inserted into the autoincrement
2154     * column.
2155     *
2156     * @param columnName autoincrement column name that is being added.
2157     * @param initial initial value of the autoincrement column.
2158     * @param increment increment value of the autoincrement column.
2159     *
2160     * @see #updateNewColumnToDefault
2161     */

2162    private void updateNewAutoincrementColumn(Activation activation, String JavaDoc columnName, long initial,
2163                                             long increment)
2164        throws StandardException
2165    {
2166        LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
2167
2168        // Don't throw an error in bind when we try to update the
2169
// autoincrement column.
2170
lcc.setAutoincrementUpdate(true);
2171
2172        lcc.autoincrementCreateCounter(td.getSchemaName(),
2173                                       td.getName(),
2174                                       columnName, new Long JavaDoc(initial),
2175                                       increment, 0);
2176        // the sql query is.
2177
// UPDATE table
2178
// set ai_column = ConnectionInfo.nextAutoincrementValue(
2179
// schemaName, tableName,
2180
// columnName)
2181
String JavaDoc updateStmt = "UPDATE \"" + td.getSchemaName() + "\".\"" +
2182            td.getName() + "\" SET \"" + columnName + "\" = " +
2183            "org.apache.derby.iapi.db.ConnectionInfo::" +
2184            "nextAutoincrementValue(" +
2185            "'" + td.getSchemaName() + "'" + "," +
2186            "'" + td.getName() + "'" + "," +
2187            "'" + columnName + "'" + ")";
2188
2189
2190
2191        try
2192        {
2193            AlterTableConstantAction.executeUpdate(lcc, updateStmt);
2194        }
2195        catch (StandardException se)
2196        {
2197            if (se.getMessageId().equals(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE))
2198            {
2199                // If overflow, override with more meaningful message.
2200
throw StandardException.newException(SQLState.LANG_AI_OVERFLOW,
2201                                                     se,
2202                                                     td.getName(),
2203                                                     columnName);
2204            }
2205            throw se;
2206        }
2207        finally
2208        {
2209            // and now update the autoincrement value.
2210
lcc.autoincrementFlushCache(td.getUUID());
2211            lcc.setAutoincrementUpdate(false);
2212        }
2213
2214    }
2215    /**
2216     * Make sure that the columns are non null
2217     * If any column is nullable, check that the data is null.
2218     *
2219     * @param columnNames names of columns to be checked
2220     * @param nullCols true if corresponding column is nullable
2221     * @param numRows number of rows in the table
2222     * @param lcc language context
2223     * @param errorMsg error message to use for exception
2224     *
2225     * @return true if any nullable columns found (nullable columns must have
2226     * all non null data or exception is thrown
2227     * @exception StandardException on error
2228     */

2229    private boolean validateNotNullConstraint
2230    (
2231        String JavaDoc columnNames[],
2232        boolean nullCols[],
2233        int numRows,
2234        LanguageConnectionContext lcc,
2235        String JavaDoc errorMsg
2236    )
2237        throws StandardException
2238    {
2239        boolean foundNullable = false;
2240        StringBuffer JavaDoc constraintText = new StringBuffer JavaDoc();
2241
2242        /*
2243         * Check for nullable columns and create a constraint string which can
2244         * be used in validateConstraint to check whether any of the
2245         * data is null.
2246         */

2247        for (int colCtr = 0; colCtr < columnNames.length; colCtr++)
2248        {
2249            ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]);
2250
2251            if (cd == null)
2252            {
2253                throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
2254                                                        columnNames[colCtr],
2255                                                        td.getName());
2256            }
2257
2258            if (cd.getType().isNullable())
2259            {
2260                if (numRows > 0)
2261                {
2262                    // already found a nullable column so add "AND"
2263
if (foundNullable)
2264                        constraintText.append(" AND ");
2265                    constraintText.append(columnNames[colCtr] + " IS NOT NULL ");
2266                }
2267                foundNullable = true;
2268                nullCols[colCtr] = true;
2269            }
2270        }
2271
2272        /* if the table has nullable columns and isn't empty
2273         * we need to validate the data
2274         */

2275        if (foundNullable && numRows > 0)
2276        {
2277            if (!ConstraintConstantAction.validateConstraint(
2278                                    (String JavaDoc) null,
2279                                    constraintText.toString(),
2280                                    td,
2281                                    lcc,
2282                                    false))
2283            {
2284                if (errorMsg.equals(SQLState.LANG_NULL_DATA_IN_PRIMARY_KEY))
2285                { //alter table add primary key
2286
throw StandardException.newException(
2287                        SQLState.LANG_NULL_DATA_IN_PRIMARY_KEY,
2288                        td.getQualifiedName());
2289                }
2290                else
2291                { //alter table modify column not null
2292
throw StandardException.newException(
2293                        SQLState.LANG_NULL_DATA_IN_NON_NULL_COLUMN,
2294                        td.getQualifiedName(), columnNames[0]);
2295                }
2296            }
2297        }
2298        return foundNullable;
2299    }
2300
2301    /**
2302     * Get rid of duplicates from a set of index conglomerate numbers and
2303     * index descriptors.
2304     *
2305     * @param indexCIDS array of index conglomerate numbers
2306     * @param irgs array of index row generaters
2307     *
2308     * @return value: If no duplicates, returns NULL; otherwise,
2309     * a size-3 array of objects, first element is an
2310     * array of duplicates' indexes in the input arrays;
2311     * second element is the compact indexCIDs; third
2312     * element is the compact irgs.
2313     */

2314    private Object JavaDoc[] compressIndexArrays(
2315                                long[] indexCIDS,
2316                                IndexRowGenerator[] irgs)
2317    {
2318        /* An efficient way to compress indexes. From one end of workSpace,
2319         * we save unique conglom IDs; and from the other end we save
2320         * duplicate indexes' indexes. We save unique conglom IDs so that
2321         * we can do less amount of comparisons. This is efficient in
2322         * space as well. No need to use hash table.
2323         */

2324        long[] workSpace = new long[indexCIDS.length];
2325        int j = 0, k = indexCIDS.length - 1;
2326        for (int i = 0; i < indexCIDS.length; i++)
2327        {
2328            int m;
2329            for (m = 0; m < j; m++) // look up our unique set
2330
{
2331                if (indexCIDS[i] == workSpace[m]) // it's a duplicate
2332
{
2333                    workSpace[k--] = i; // save dup index's index
2334
break;
2335                }
2336            }
2337            if (m == j)
2338                workSpace[j++] = indexCIDS[i]; // save unique conglom id
2339
}
2340        if (j < indexCIDS.length) // duplicate exists
2341
{
2342            long[] newIndexCIDS = new long[j];
2343            IndexRowGenerator[] newIrgs = new IndexRowGenerator[j];
2344            int[] duplicateIndexes = new int[indexCIDS.length - j];
2345            k = 0;
2346            // do everything in one loop
2347
for (int m = 0, n = indexCIDS.length - 1; m < indexCIDS.length; m++)
2348            {
2349                // we already gathered our indexCIDS and duplicateIndexes
2350
if (m < j)
2351                    newIndexCIDS[m] = workSpace[m];
2352                else
2353                    duplicateIndexes[indexCIDS.length - m - 1] = (int) workSpace[m];
2354
2355                // stack up our irgs, indexSCOCIs, indexDCOCIs
2356
if ((n >= j) && (m == (int) workSpace[n]))
2357                    n--;
2358                else
2359                {
2360                    newIrgs[k] = irgs[m];
2361                    k++;
2362                }
2363            }
2364
2365            // construct return value
2366
Object JavaDoc[] returnValue = new Object JavaDoc[3]; // [indexSCOCIs == null ? 3 : 5];
2367
returnValue[0] = duplicateIndexes;
2368            returnValue[1] = newIndexCIDS;
2369            returnValue[2] = newIrgs;
2370            return returnValue;
2371        }
2372        else // no duplicates
2373
return null;
2374    }
2375
2376}
2377
2378
Popular Tags