001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.catalog.SYSTRIGGERSRowFactory
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.catalog;
023:
024: import org.apache.derby.iapi.types.DataTypeDescriptor;
025: import org.apache.derby.iapi.types.DataValueDescriptor;
026: import org.apache.derby.iapi.types.TypeId;
027:
028: import org.apache.derby.iapi.types.TypeId;
029:
030: import org.apache.derby.iapi.types.DataValueFactory;
031: import org.apache.derby.iapi.types.RowLocation;
032:
033: import org.apache.derby.iapi.sql.dictionary.CatalogRowFactory;
034: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
035: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
036: import org.apache.derby.iapi.sql.dictionary.SystemColumn;
037: import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
038: import org.apache.derby.iapi.sql.dictionary.TupleDescriptor;
039:
040: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
041: import org.apache.derby.iapi.sql.execute.ExecRow;
042: import org.apache.derby.iapi.sql.execute.ExecutionFactory;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.iapi.services.monitor.Monitor;
047: import org.apache.derby.catalog.ReferencedColumns;
048: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
049: import org.apache.derby.catalog.UUID;
050: import org.apache.derby.iapi.services.uuid.UUIDFactory;
051:
052: import org.apache.derby.iapi.services.sanity.SanityManager;
053: import org.apache.derby.iapi.types.SQLTimestamp;
054: import java.sql.Timestamp;
055:
056: /**
057: * Factory for creating a SYSTRIGGERS row.
058: *
059: *
060: * @version 0.1
061: * @author Jamie
062: */
063:
064: public class SYSTRIGGERSRowFactory extends CatalogRowFactory {
065: static final String TABLENAME_STRING = "SYSTRIGGERS";
066:
067: /* Column #s for sysinfo (1 based) */
068: public static final int SYSTRIGGERS_TRIGGERID = 1;
069: public static final int SYSTRIGGERS_TRIGGERNAME = 2;
070: public static final int SYSTRIGGERS_SCHEMAID = 3;
071: public static final int SYSTRIGGERS_CREATIONTIMESTAMP = 4;
072: public static final int SYSTRIGGERS_EVENT = 5;
073: public static final int SYSTRIGGERS_FIRINGTIME = 6;
074: public static final int SYSTRIGGERS_TYPE = 7;
075: public static final int SYSTRIGGERS_STATE = TriggerDescriptor.SYSTRIGGERS_STATE_FIELD;
076: public static final int SYSTRIGGERS_TABLEID = 9;
077: public static final int SYSTRIGGERS_WHENSTMTID = 10;
078: public static final int SYSTRIGGERS_ACTIONSTMTID = 11;
079: public static final int SYSTRIGGERS_REFERENCEDCOLUMNS = 12;
080: public static final int SYSTRIGGERS_TRIGGERDEFINITION = 13;
081: public static final int SYSTRIGGERS_REFERENCINGOLD = 14;
082: public static final int SYSTRIGGERS_REFERENCINGNEW = 15;
083: public static final int SYSTRIGGERS_OLDREFERENCINGNAME = 16;
084: public static final int SYSTRIGGERS_NEWREFERENCINGNAME = 17;
085:
086: public static final int SYSTRIGGERS_COLUMN_COUNT = SYSTRIGGERS_NEWREFERENCINGNAME;
087:
088: public static final int SYSTRIGGERS_INDEX1_ID = 0;
089: public static final int SYSTRIGGERS_INDEX2_ID = 1;
090: public static final int SYSTRIGGERS_INDEX3_ID = 2;
091:
092: private static final int[][] indexColumnPositions = {
093: { SYSTRIGGERS_TRIGGERID },
094: { SYSTRIGGERS_TRIGGERNAME, SYSTRIGGERS_SCHEMAID },
095: { SYSTRIGGERS_TABLEID, SYSTRIGGERS_CREATIONTIMESTAMP } };
096:
097: private static final boolean[] uniqueness = { true, true, false, };
098:
099: private static final String[] uuids = {
100: "c013800d-00d7-c025-4809-000a0a411200" // catalog UUID
101: , "c013800d-00d7-c025-480a-000a0a411200" // heap UUID
102: , "c013800d-00d7-c025-480b-000a0a411200" // SYSTRIGGERS_INDEX1
103: , "c013800d-00d7-c025-480c-000a0a411200" // SYSTRIGGERS_INDEX2
104: , "c013800d-00d7-c025-480d-000a0a411200" // SYSTRIGGERS_INDEX3
105: };
106:
107: /////////////////////////////////////////////////////////////////////////////
108: //
109: // CONSTRUCTORS
110: //
111: /////////////////////////////////////////////////////////////////////////////
112: public SYSTRIGGERSRowFactory(UUIDFactory uuidf,
113: ExecutionFactory ef, DataValueFactory dvf,
114: boolean convertIdToLower) {
115: super (uuidf, ef, dvf, convertIdToLower);
116: initInfo(SYSTRIGGERS_COLUMN_COUNT, TABLENAME_STRING,
117: indexColumnPositions, uniqueness, uuids);
118: }
119:
120: /////////////////////////////////////////////////////////////////////////////
121: //
122: // METHODS
123: //
124: /////////////////////////////////////////////////////////////////////////////
125: /**
126: * Make a SYSTRIGGERS row.
127: *
128: * @return Row suitable for inserting into SYSTRIGGERS.
129: *
130: * @exception StandardException thrown on failure
131: */
132:
133: public ExecRow makeRow(TupleDescriptor td, TupleDescriptor parent)
134: throws StandardException {
135: DataTypeDescriptor dtd;
136: ExecRow row;
137: DataValueDescriptor col;
138: String name = null;
139: UUID uuid = null;
140: UUID suuid = null; // schema
141: UUID tuuid = null; // referenced table
142: UUID actionSPSID = null; // action sps uuid string
143: UUID whenSPSID = null; // when clause sps uuid string
144: Timestamp createTime = null;
145: String event = null;
146: String time = null;
147: String type = null;
148: String enabled = null;
149: String triggerDefinition = null;
150: String oldReferencingName = null;
151: String newReferencingName = null;
152: ReferencedColumns rcd = null;
153: boolean referencingOld = false;
154: boolean referencingNew = false;
155:
156: if (td != null) {
157: TriggerDescriptor triggerDescriptor = (TriggerDescriptor) td;
158: name = triggerDescriptor.getName();
159: uuid = triggerDescriptor.getUUID();
160: suuid = triggerDescriptor.getSchemaDescriptor().getUUID();
161: createTime = triggerDescriptor.getCreationTimestamp();
162: // for now we are assuming that a trigger can only listen to a single event
163: event = triggerDescriptor
164: .listensForEvent(TriggerDescriptor.TRIGGER_EVENT_UPDATE) ? "U"
165: : triggerDescriptor
166: .listensForEvent(TriggerDescriptor.TRIGGER_EVENT_DELETE) ? "D"
167: : "I";
168: time = triggerDescriptor.isBeforeTrigger() ? "B" : "A";
169: type = triggerDescriptor.isRowTrigger() ? "R" : "S";
170: enabled = triggerDescriptor.isEnabled() ? "E" : "D";
171: tuuid = triggerDescriptor.getTableDescriptor().getUUID();
172: int[] refCols = triggerDescriptor.getReferencedCols();
173: rcd = (refCols != null) ? new ReferencedColumnsDescriptorImpl(
174: refCols)
175: : null;
176:
177: actionSPSID = triggerDescriptor.getActionId();
178: whenSPSID = triggerDescriptor.getWhenClauseId();
179: triggerDefinition = triggerDescriptor
180: .getTriggerDefinition();
181: referencingOld = triggerDescriptor.getReferencingOld();
182: referencingNew = triggerDescriptor.getReferencingNew();
183: oldReferencingName = triggerDescriptor
184: .getOldReferencingName();
185: newReferencingName = triggerDescriptor
186: .getNewReferencingName();
187: }
188:
189: /* Build the row to insert */
190: row = getExecutionFactory().getValueRow(
191: SYSTRIGGERS_COLUMN_COUNT);
192:
193: /* 1st column is TRIGGERID */
194: row.setColumn(1, dvf.getCharDataValue((uuid == null) ? null
195: : uuid.toString()));
196:
197: /* 2nd column is TRIGGERNAME */
198: row.setColumn(2, dvf.getVarcharDataValue(name));
199:
200: /* 3rd column is SCHEMAID */
201: row.setColumn(3, dvf.getCharDataValue((suuid == null) ? null
202: : suuid.toString()));
203:
204: /* 4th column is CREATIONTIMESTAMP */
205: row.setColumn(4, dvf.getDataValue(createTime));
206:
207: /* 5th column is EVENT */
208: row.setColumn(5, dvf.getCharDataValue(event));
209:
210: /* 6th column is FIRINGTIME */
211: row.setColumn(6, dvf.getCharDataValue(time));
212:
213: /* 7th column is TYPE */
214: row.setColumn(7, dvf.getCharDataValue(type));
215:
216: /* 8th column is STATE */
217: row.setColumn(8, dvf.getCharDataValue(enabled));
218:
219: /* 9th column is TABLEID */
220: row.setColumn(9, dvf.getCharDataValue((tuuid == null) ? null
221: : tuuid.toString()));
222:
223: /* 10th column is WHENSTMTID */
224: row.setColumn(10, dvf
225: .getCharDataValue((whenSPSID == null) ? null
226: : whenSPSID.toString()));
227:
228: /* 11th column is ACTIONSTMTID */
229: row.setColumn(11, dvf
230: .getCharDataValue((actionSPSID == null) ? null
231: : actionSPSID.toString()));
232:
233: /* 12th column is REFERENCEDCOLUMNS
234: * (user type org.apache.derby.catalog.ReferencedColumns)
235: */
236: row.setColumn(12, dvf.getDataValue(rcd));
237:
238: /* 13th column is TRIGGERDEFINITION */
239: row.setColumn(13, dvf
240: .getLongvarcharDataValue(triggerDefinition));
241:
242: /* 14th column is REFERENCINGOLD */
243: row.setColumn(14, dvf.getDataValue(referencingOld));
244:
245: /* 15th column is REFERENCINGNEW */
246: row.setColumn(15, dvf.getDataValue(referencingNew));
247:
248: /* 16th column is OLDREFERENCINGNAME */
249: row.setColumn(16, dvf.getVarcharDataValue(oldReferencingName));
250:
251: /* 17th column is NEWREFERENCINGNAME */
252: row.setColumn(17, dvf.getVarcharDataValue(newReferencingName));
253:
254: return row;
255: }
256:
257: ///////////////////////////////////////////////////////////////////////////
258: //
259: // ABSTRACT METHODS TO BE IMPLEMENTED BY CHILDREN OF CatalogRowFactory
260: //
261: ///////////////////////////////////////////////////////////////////////////
262:
263: /**
264: * Make an Tuple Descriptor out of a SYSTRIGGERS row
265: *
266: * @param row a SYSTRIGGERS row
267: * @param parentTupleDescriptor unused
268: * @param dd dataDictionary
269: *
270: * @return a descriptor equivalent to a SYSTRIGGERS row
271: *
272: * @exception StandardException thrown on failure
273: */
274: public TupleDescriptor buildDescriptor(ExecRow row,
275: TupleDescriptor parentTupleDescriptor, DataDictionary dd)
276: throws StandardException {
277: DataValueDescriptor col;
278: String name;
279: char theChar;
280: String uuidStr;
281: String triggerDefinition;
282: String oldReferencingName;
283: String newReferencingName;
284: UUID uuid;
285: UUID suuid; // schema
286: UUID tuuid; // referenced table
287: UUID actionSPSID = null; // action sps uuid string
288: UUID whenSPSID = null; // when clause sps uuid string
289: Timestamp createTime;
290: int eventMask = 0;
291: boolean isBefore;
292: boolean isRow;
293: boolean isEnabled;
294: boolean referencingOld;
295: boolean referencingNew;
296: ReferencedColumns rcd;
297: TriggerDescriptor descriptor;
298: DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
299:
300: if (SanityManager.DEBUG) {
301: SanityManager.ASSERT(
302: row.nColumns() == SYSTRIGGERS_COLUMN_COUNT,
303: "Wrong number of columns for a SYSTRIGGERS row");
304: }
305:
306: // 1st column is TRIGGERID (UUID - char(36))
307: col = row.getColumn(1);
308: uuidStr = col.getString();
309: uuid = getUUIDFactory().recreateUUID(uuidStr);
310:
311: // 2nd column is TRIGGERNAME (varchar(128))
312: col = row.getColumn(2);
313: name = col.getString();
314:
315: // 3rd column is SCHEMAID (UUID - char(36))
316: col = row.getColumn(3);
317: uuidStr = col.getString();
318: suuid = getUUIDFactory().recreateUUID(uuidStr);
319:
320: // 4th column is CREATIONTIMESTAMP (TIMESTAMP)
321: col = row.getColumn(4);
322: createTime = (Timestamp) col.getObject();
323:
324: // 5th column is EVENT (char(1))
325: col = row.getColumn(5);
326: theChar = col.getString().charAt(0);
327: switch (theChar) {
328: case 'U':
329: eventMask = TriggerDescriptor.TRIGGER_EVENT_UPDATE;
330: break;
331:
332: case 'I':
333: eventMask = TriggerDescriptor.TRIGGER_EVENT_INSERT;
334: break;
335:
336: case 'D':
337: eventMask = TriggerDescriptor.TRIGGER_EVENT_DELETE;
338: break;
339:
340: default:
341: if (SanityManager.DEBUG) {
342: SanityManager.THROWASSERT("bad event mask: " + theChar);
343: }
344: }
345:
346: // 6th column is FIRINGTIME (char(1))
347: isBefore = getCharBoolean(row.getColumn(6), 'B', 'A');
348:
349: // 7th column is TYPE (char(1))
350: isRow = getCharBoolean(row.getColumn(7), 'R', 'S');
351:
352: // 8th column is STATE (char(1))
353: isEnabled = getCharBoolean(row.getColumn(8), 'E', 'D');
354:
355: // 9th column is TABLEID (UUID - char(36))
356: col = row.getColumn(9);
357: uuidStr = col.getString();
358: tuuid = getUUIDFactory().recreateUUID(uuidStr);
359:
360: // 10th column is WHENSTMTID (UUID - char(36))
361: col = row.getColumn(10);
362: uuidStr = col.getString();
363: if (uuidStr != null)
364: whenSPSID = getUUIDFactory().recreateUUID(uuidStr);
365:
366: // 11th column is ACTIONSTMTID (UUID - char(36))
367: col = row.getColumn(11);
368: uuidStr = col.getString();
369: if (uuidStr != null)
370: actionSPSID = getUUIDFactory().recreateUUID(uuidStr);
371:
372: // 12th column is REFERENCEDCOLUMNS user type org.apache.derby.catalog.ReferencedColumns
373: col = row.getColumn(12);
374: rcd = (ReferencedColumns) col.getObject();
375:
376: // 13th column is TRIGGERDEFINITION (longvarhar)
377: col = row.getColumn(13);
378: triggerDefinition = col.getString();
379:
380: // 14th column is REFERENCINGOLD (boolean)
381: col = row.getColumn(14);
382: referencingOld = col.getBoolean();
383:
384: // 15th column is REFERENCINGNEW (boolean)
385: col = row.getColumn(15);
386: referencingNew = col.getBoolean();
387:
388: // 16th column is REFERENCINGNAME (varchar(128))
389: col = row.getColumn(16);
390: oldReferencingName = col.getString();
391:
392: // 17th column is REFERENCINGNAME (varchar(128))
393: col = row.getColumn(17);
394: newReferencingName = col.getString();
395:
396: descriptor = new TriggerDescriptor(dd, dd.getSchemaDescriptor(
397: suuid, null), uuid, name, eventMask, isBefore, isRow,
398: isEnabled, dd.getTableDescriptor(tuuid), whenSPSID,
399: actionSPSID, createTime, (rcd == null) ? (int[]) null
400: : rcd.getReferencedColumnPositions(),
401: triggerDefinition, referencingOld, referencingNew,
402: oldReferencingName, newReferencingName);
403:
404: return descriptor;
405: }
406:
407: /**
408: * Builds a list of columns suitable for creating this Catalog.
409: * The last column, the serialized statement, is not added
410: * to the column list. This is done deliberately to make it
411: * a 'hidden' column -- one that is not visible to customers,
412: * but is visible to the system.
413: *
414: *
415: * @return array of SystemColumn suitable for making this catalog.
416: */
417: public SystemColumn[] buildColumnList() {
418: SystemColumn[] columnList = new SystemColumn[SYSTRIGGERS_COLUMN_COUNT];
419:
420: // describe columns
421: columnList[SYSTRIGGERS_TRIGGERID - 1] = new SystemColumnImpl(
422: convertIdCase("TRIGGERID"), // name
423: SYSTRIGGERS_TRIGGERID, // column number
424: 0, // precision
425: 0, // scale
426: false, // nullability
427: "CHAR", // dataType
428: true, // built-in type
429: 36 // maxLength
430: );
431:
432: columnList[SYSTRIGGERS_TRIGGERNAME - 1] = new SystemColumnImpl( // SQL IDENTIFIER
433: convertIdCase("TRIGGERNAME"), // column name
434: SYSTRIGGERS_TRIGGERNAME, // column number
435: false // nullability
436: );
437:
438: columnList[SYSTRIGGERS_SCHEMAID - 1] = new SystemColumnImpl(
439: convertIdCase("SCHEMAID"), // name
440: SYSTRIGGERS_SCHEMAID, // column number
441: 0, // precision
442: 0, // scale
443: false, // nullability
444: "CHAR", // dataType
445: true, // built-in type
446: 36 // maxLength
447: );
448:
449: columnList[SYSTRIGGERS_CREATIONTIMESTAMP - 1] = new SystemColumnImpl(
450: convertIdCase("CREATIONTIMESTAMP"), // name
451: SYSTRIGGERS_CREATIONTIMESTAMP, // column number
452: 0, // precision
453: 0, // scale
454: false, // nullability
455: "TIMESTAMP", // dataType
456: true, // built-in type
457: TypeId.TIMESTAMP_MAXWIDTH // maxLength
458: );
459:
460: columnList[SYSTRIGGERS_EVENT - 1] = new SystemColumnImpl(
461: convertIdCase("EVENT"), // name
462: SYSTRIGGERS_EVENT, // column number
463: 0, // precision
464: 0, // scale
465: false, // nullability
466: "CHAR", // dataType
467: true, // built-in type
468: 1 // maxLength
469: );
470:
471: columnList[SYSTRIGGERS_FIRINGTIME - 1] = new SystemColumnImpl(
472: convertIdCase("FIRINGTIME"), // name
473: SYSTRIGGERS_FIRINGTIME, // column number
474: 0, // precision
475: 0, // scale
476: false, // nullability
477: "CHAR", // dataType
478: true, // built-in type
479: 1 // maxLength
480: );
481:
482: columnList[SYSTRIGGERS_TYPE - 1] = new SystemColumnImpl(
483: convertIdCase("TYPE"), // name
484: SYSTRIGGERS_TYPE, // column number
485: 0, // precision
486: 0, // scale
487: false, // nullability
488: "CHAR", // dataType
489: true, // built-in type
490: 1 // maxLength
491: );
492:
493: columnList[SYSTRIGGERS_STATE - 1] = new SystemColumnImpl(
494: convertIdCase("STATE"), // name
495: SYSTRIGGERS_STATE,// column number
496: 0, // precision
497: 0, // scale
498: false, // nullability
499: "CHAR", // dataType
500: true, // built-in type
501: 1 // maxLength
502: );
503:
504: columnList[SYSTRIGGERS_TABLEID - 1] = new SystemColumnImpl(
505: convertIdCase("TABLEID"), // name
506: SYSTRIGGERS_TABLEID, // column number
507: 0, // precision
508: 0, // scale
509: false, // nullability
510: "CHAR", // dataType
511: true, // built-in type
512: 36 // maxLength
513: );
514:
515: columnList[SYSTRIGGERS_WHENSTMTID - 1] = new SystemColumnImpl(
516: convertIdCase("WHENSTMTID"), // name
517: SYSTRIGGERS_WHENSTMTID, // column number
518: 0, // precision
519: 0, // scale
520: true, // nullability
521: "CHAR", // dataType
522: true, // built-in type
523: 36 // maxLength
524: );
525:
526: columnList[SYSTRIGGERS_ACTIONSTMTID - 1] = new SystemColumnImpl(
527: convertIdCase("ACTIONSTMTID"), // name
528: SYSTRIGGERS_ACTIONSTMTID, // column number
529: 0, // precision
530: 0, // scale
531: true, // nullability
532: "CHAR", // dataType
533: true, // built-in type
534: 36 // maxLength
535: );
536:
537: columnList[SYSTRIGGERS_REFERENCEDCOLUMNS - 1] = new SystemColumnImpl(
538: convertIdCase("REFERENCEDCOLUMNS"), // name
539: SYSTRIGGERS_REFERENCEDCOLUMNS, // column number
540: 0, // precision
541: 0, // scale
542: true, // nullability
543: "org.apache.derby.catalog.ReferencedColumns", //datatype
544: false, // built-in type
545: DataTypeDescriptor.MAXIMUM_WIDTH_UNKNOWN // maxLength
546: );
547:
548: columnList[SYSTRIGGERS_TRIGGERDEFINITION - 1] = new SystemColumnImpl(
549: convertIdCase("TRIGGERDEFINITION"), // name
550: SYSTRIGGERS_TRIGGERDEFINITION, // column number
551: 0, // precision
552: 0, // scale
553: true, // nullability
554: "LONG VARCHAR", // dataType
555: true, // built-in type
556: Integer.MAX_VALUE // maxLength
557: );
558:
559: columnList[SYSTRIGGERS_REFERENCINGOLD - 1] = new SystemColumnImpl(
560: convertIdCase("REFERENCINGOLD"), // name
561: SYSTRIGGERS_REFERENCINGOLD,// column number
562: 0, // precision
563: 0, // scale
564: true, // nullability
565: "BOOLEAN", // dataType
566: true, // built-in type
567: 1 // maxLength
568: );
569:
570: columnList[SYSTRIGGERS_REFERENCINGNEW - 1] = new SystemColumnImpl(
571: convertIdCase("REFERENCINGNEW"), // name
572: SYSTRIGGERS_REFERENCINGNEW,// column number
573: 0, // precision
574: 0, // scale
575: true, // nullability
576: "BOOLEAN", // dataType
577: true, // built-in type
578: 1 // maxLength
579: );
580:
581: columnList[SYSTRIGGERS_OLDREFERENCINGNAME - 1] = new SystemColumnImpl( // SQL IDENTIFIER
582: convertIdCase("OLDREFERENCINGNAME"), // column name
583: SYSTRIGGERS_OLDREFERENCINGNAME, // column number
584: true // nullability
585: );
586:
587: columnList[SYSTRIGGERS_NEWREFERENCINGNAME - 1] = new SystemColumnImpl( // SQL IDENTIFIER
588: convertIdCase("NEWREFERENCINGNAME"), // column name
589: SYSTRIGGERS_NEWREFERENCINGNAME, // column number
590: true // nullability
591: );
592:
593: return columnList;
594: }
595:
596: // a little helper
597: private boolean getCharBoolean(DataValueDescriptor col,
598: char trueValue, char falseValue) throws StandardException {
599: char theChar = col.getString().charAt(0);
600: if (theChar == trueValue) {
601: return true;
602: } else if (theChar == falseValue) {
603: return false;
604: } else {
605: if (SanityManager.DEBUG)
606: SanityManager.THROWASSERT("bad char value " + theChar);
607:
608: return true;
609: }
610: }
611: }
|