001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.kernel;
020:
021: import java.io.Serializable;
022: import java.sql.Connection;
023: import java.sql.PreparedStatement;
024: import java.sql.ResultSet;
025: import java.sql.SQLException;
026: import java.sql.Types;
027: import java.util.HashMap;
028:
029: import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
030: import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
031: import org.apache.openjpa.jdbc.meta.ClassMapping;
032: import org.apache.openjpa.jdbc.schema.Column;
033: import org.apache.openjpa.jdbc.schema.PrimaryKey;
034: import org.apache.openjpa.jdbc.schema.Schema;
035: import org.apache.openjpa.jdbc.schema.SchemaGroup;
036: import org.apache.openjpa.jdbc.schema.SchemaTool;
037: import org.apache.openjpa.jdbc.schema.Schemas;
038: import org.apache.openjpa.jdbc.schema.Table;
039: import org.apache.openjpa.jdbc.sql.DBDictionary;
040: import org.apache.openjpa.jdbc.sql.RowImpl;
041: import org.apache.openjpa.jdbc.sql.SQLBuffer;
042: import org.apache.openjpa.jdbc.sql.SQLExceptions;
043: import org.apache.openjpa.lib.conf.Configurable;
044: import org.apache.openjpa.lib.conf.Configuration;
045: import org.apache.openjpa.lib.conf.Configurations;
046: import org.apache.openjpa.lib.log.Log;
047: import org.apache.openjpa.lib.util.Localizer;
048: import org.apache.openjpa.lib.util.Options;
049: import org.apache.openjpa.meta.JavaTypes;
050: import org.apache.openjpa.util.InvalidStateException;
051: import serp.util.Numbers;
052: import serp.util.Strings;
053:
054: ////////////////////////////////////////////////////////////
055: // NOTE: Do not change property names; see SequenceMetaData
056: // and SequenceMapping for standard property names.
057: ////////////////////////////////////////////////////////////
058:
059: /**
060: * {@link JDBCSeq} implementation that uses a database table
061: * for sequence number generation. This base implementation uses a single
062: * row for a global sequence number.
063: *
064: * @author Abe White
065: */
066: public class TableJDBCSeq extends AbstractJDBCSeq implements
067: Configurable {
068:
069: public static final String ACTION_DROP = "drop";
070: public static final String ACTION_ADD = "add";
071: public static final String ACTION_GET = "get";
072: public static final String ACTION_SET = "set";
073:
074: private static final Localizer _loc = Localizer
075: .forPackage(TableJDBCSeq.class);
076:
077: private transient JDBCConfiguration _conf = null;
078: private transient Log _log = null;
079: private int _alloc = 50;
080: private int _intValue = 1;
081: private final HashMap _stat = new HashMap();
082:
083: private String _table = "OPENJPA_SEQUENCE_TABLE";
084: private String _seqColumnName = "SEQUENCE_VALUE";
085: private String _pkColumnName = "ID";
086:
087: private Column _seqColumn = null;
088: private Column _pkColumn = null;
089: private int _schemasIdx = 0;
090:
091: /**
092: * The sequence table name. Defaults to <code>OPENJPA_SEQUENCE_TABLE</code>.
093: * By default, the table will be placed in the first schema listed in your
094: * <code>openjpa.jdbc.Schemas</code> property, or in the default schema if
095: * the property is not given. If you specify a table name in the form
096: * <code><schema>.<table></code>, then the given schema
097: * will be used.
098: */
099: public String getTable() {
100: return _table;
101: }
102:
103: /**
104: * The sequence table name. Defaults to <code>OPENJPA_SEQUENCE_TABLE</code>.
105: * By default, the table will be placed in the first schema listed in your
106: * <code>openjpa.jdbc.Schemas</code> property, or in the default schema if
107: * the property is not given. If you specify a table name in the form
108: * <code><schema>.<table></code>, then the given schema
109: * will be used.
110: */
111: public void setTable(String name) {
112: _table = name;
113: }
114:
115: /**
116: * @deprecated Use {@link #setTable}. Retained for
117: * backwards-compatibility with auto-configuration.
118: */
119: public void setTableName(String name) {
120: setTable(name);
121: }
122:
123: /**
124: * The name of the column that holds the sequence value. Defaults
125: * to <code>SEQUENCE_VALUE</code>.
126: */
127: public String getSequenceColumn() {
128: return _seqColumnName;
129: }
130:
131: /**
132: * The name of the column that holds the sequence value. Defaults
133: * to <code>SEQUENCE_VALUE</code>.
134: */
135: public void setSequenceColumn(String sequenceColumn) {
136: _seqColumnName = sequenceColumn;
137: }
138:
139: /**
140: * The name of the table's primary key column. Defaults to
141: * <code>ID</code>.
142: */
143: public String getPrimaryKeyColumn() {
144: return _pkColumnName;
145: }
146:
147: /**
148: * The name of the table's primary key column. Defaults to
149: * <code>ID</code>.
150: */
151: public void setPrimaryKeyColumn(String primaryKeyColumn) {
152: _pkColumnName = primaryKeyColumn;
153: }
154:
155: /**
156: * Return the number of sequences to allocate for each update of the
157: * sequence table. Sequence numbers will be grabbed in blocks of this
158: * value to reduce the number of transactions that must be performed on
159: * the sequence table.
160: */
161: public int getAllocate() {
162: return _alloc;
163: }
164:
165: /**
166: * Return the number of sequences to allocate for each update of the
167: * sequence table. Sequence numbers will be grabbed in blocks of this
168: * value to reduce the number of transactions that must be performed on
169: * the sequence table.
170: */
171: public void setAllocate(int alloc) {
172: _alloc = alloc;
173: }
174:
175: /**
176: * Return the number as the initial number for the
177: * GeneratedValue.TABLE strategy to start with.
178: * @return an initial number
179: */
180: public int getInitialValue() {
181: return _intValue;
182: }
183:
184: /**
185: * Set the initial number in the table for the GeneratedValue.TABLE
186: * strategy to use as initial number.
187: * @param intValue. The initial number
188: */
189: public void setInitialValue(int intValue) {
190: _intValue = intValue;
191: }
192:
193: /**
194: * @deprecated Use {@link #setAllocate}. Retained for backwards
195: * compatibility of auto-configuration.
196: */
197: public void setIncrement(int inc) {
198: setAllocate(inc);
199: }
200:
201: public JDBCConfiguration getConfiguration() {
202: return _conf;
203: }
204:
205: public void setConfiguration(Configuration conf) {
206: _conf = (JDBCConfiguration) conf;
207: _log = _conf.getLog(JDBCConfiguration.LOG_RUNTIME);
208: }
209:
210: public void startConfiguration() {
211: }
212:
213: public void endConfiguration() {
214: buildTable();
215: }
216:
217: public void addSchema(ClassMapping mapping, SchemaGroup group) {
218: // Since the table is created by openjpa internally
219: // we can create the table for each schema within the PU
220: // in here.
221:
222: Schema[] schemas = group.getSchemas();
223: for (int i = 0; i < schemas.length; i++) {
224: String schemaName = Strings.getPackageName(_table);
225: if (schemaName.length() == 0)
226: schemaName = Schemas.getNewTableSchema(_conf);
227: if (schemaName == null)
228: schemaName = schemas[i].getName();
229:
230: // create table in this group
231: Schema schema = group.getSchema(schemaName);
232: if (schema == null)
233: schema = group.addSchema(schemaName);
234:
235: schema.importTable(_pkColumn.getTable());
236: // we need to reset the table name in the column with the
237: // fully qualified name for matching the table name from the
238: // Column.
239: _pkColumn.resetTableName(schemaName + "."
240: + _pkColumn.getTableName());
241: // some databases require to create an index for the sequence table
242: _conf.getDBDictionaryInstance().createIndexIfNecessary(
243: schema, _table, _pkColumn);
244:
245: }
246: }
247:
248: protected Object nextInternal(JDBCStore store, ClassMapping mapping)
249: throws Exception {
250: // if needed, grab the next handful of ids
251: Status stat = getStatus(mapping);
252: if (stat == null)
253: throw new InvalidStateException(_loc.get("bad-seq-type",
254: getClass(), mapping));
255:
256: while (true) {
257: synchronized (stat) {
258: // make sure seq is at least 1, since autoassigned ids of 0 can
259: // conflict with uninitialized values
260: stat.seq = Math.max(stat.seq, 1);
261: if (stat.seq < stat.max)
262: return Numbers.valueOf(stat.seq++);
263: }
264: allocateSequence(store, mapping, stat, _alloc, true);
265: }
266: }
267:
268: protected Object currentInternal(JDBCStore store,
269: ClassMapping mapping) throws Exception {
270: if (current == null) {
271: Connection conn = getConnection(store);
272: try {
273: long cur = getSequence(mapping, conn);
274: if (cur != -1)
275: current = Numbers.valueOf(cur);
276: } finally {
277: closeConnection(conn);
278: }
279: }
280: return super .currentInternal(store, mapping);
281: }
282:
283: protected void allocateInternal(int count, JDBCStore store,
284: ClassMapping mapping) throws SQLException {
285: Status stat = getStatus(mapping);
286: if (stat == null)
287: return;
288:
289: while (true) {
290: int available;
291: synchronized (stat) {
292: available = (int) (stat.max - stat.seq);
293: if (available >= count)
294: return;
295: }
296: allocateSequence(store, mapping, stat, count - available,
297: false);
298: }
299: }
300:
301: /**
302: * Return the appropriate status object for the given class, or null
303: * if cannot handle the given class. The mapping may be null.
304: */
305: protected Status getStatus(ClassMapping mapping) {
306: Status status = (Status) _stat.get(mapping);
307: if (status == null) {
308: status = new Status();
309: _stat.put(mapping, status);
310: }
311: return status;
312:
313: }
314:
315: /**
316: * Add the primary key column to the given table and return it.
317: */
318: protected Column addPrimaryKeyColumn(Table table) {
319: DBDictionary dict = _conf.getDBDictionaryInstance();
320: Column pkColumn = table.addColumn(dict.getValidColumnName(
321: getPrimaryKeyColumn(), table));
322: pkColumn.setType(dict.getPreferredType(Types.TINYINT));
323: pkColumn.setJavaType(JavaTypes.INT);
324: return pkColumn;
325: }
326:
327: /**
328: * Return the primary key value for the given class.
329: */
330: protected Object getPrimaryKey(ClassMapping mapping) {
331: return Numbers.valueOf(0);
332: }
333:
334: /**
335: * Creates the object-level representation of the sequence table.
336: */
337: private void buildTable() {
338: String tableName = Strings.getClassName(_table);
339: String schemaName = Strings.getPackageName(_table);
340: if (schemaName.length() == 0)
341: schemaName = Schemas.getNewTableSchema(_conf);
342:
343: SchemaGroup group = new SchemaGroup();
344: Schema schema = group.addSchema(schemaName);
345:
346: Table table = schema.addTable(tableName);
347: _pkColumn = addPrimaryKeyColumn(table);
348: PrimaryKey pk = table.addPrimaryKey();
349: pk.addColumn(_pkColumn);
350:
351: DBDictionary dict = _conf.getDBDictionaryInstance();
352: _seqColumn = table.addColumn(dict.getValidColumnName(
353: _seqColumnName, table));
354: _seqColumn.setType(dict.getPreferredType(Types.BIGINT));
355: _seqColumn.setJavaType(JavaTypes.LONG);
356: }
357:
358: /**
359: * Updates the max available sequence value.
360: */
361: private void allocateSequence(JDBCStore store,
362: ClassMapping mapping, Status stat, int alloc,
363: boolean updateStatSeq) throws SQLException {
364: Connection conn = getConnection(store);
365: try {
366: if (setSequence(mapping, stat, alloc, updateStatSeq, conn))
367: return;
368: } catch (SQLException se) {
369: throw SQLExceptions.getStore(
370: _loc.get("bad-seq-up", _table), se, _conf
371: .getDBDictionaryInstance());
372: } finally {
373: closeConnection(conn);
374: }
375:
376: try {
377: // possible that we might get errors when inserting if
378: // another thread/process is inserting same pk at same time
379: SQLException err = null;
380: // ### why does this not call getConnection() / closeConnection()?
381: conn = _conf.getDataSource2(store.getContext())
382: .getConnection();
383: try {
384: insertSequence(mapping, conn);
385: } catch (SQLException se) {
386: err = se;
387: } finally {
388: try {
389: conn.close();
390: } catch (SQLException se) {
391: }
392: }
393:
394: // now we should be able to update...
395: conn = getConnection(store);
396: try {
397: if (!setSequence(mapping, stat, alloc, updateStatSeq,
398: conn))
399: throw (err != null) ? err : new SQLException(_loc
400: .get("no-seq-row", mapping, _table)
401: .getMessage());
402: } finally {
403: closeConnection(conn);
404: }
405: } catch (SQLException se2) {
406: throw SQLExceptions.getStore(
407: _loc.get("bad-seq-up", _table), se2, _conf
408: .getDBDictionaryInstance());
409: }
410: }
411:
412: /**
413: * Inserts the initial sequence information into the database, if any.
414: */
415: private void insertSequence(ClassMapping mapping, Connection conn)
416: throws SQLException {
417: if (_log.isTraceEnabled())
418: _log.trace(_loc.get("insert-seq"));
419:
420: Object pk = getPrimaryKey(mapping);
421: if (pk == null)
422: throw new InvalidStateException(_loc.get("bad-seq-type",
423: getClass(), mapping));
424:
425: DBDictionary dict = _conf.getDBDictionaryInstance();
426: String tableName = resolveTableName(mapping, _pkColumn
427: .getTable());
428: SQLBuffer insert = new SQLBuffer(dict).append("INSERT INTO ")
429: .append(tableName).append(" (").append(_pkColumn)
430: .append(", ").append(_seqColumn).append(") VALUES (")
431: .appendValue(pk, _pkColumn).append(", ").appendValue(
432: _intValue, _seqColumn).append(")");
433:
434: boolean wasAuto = conn.getAutoCommit();
435: if (!wasAuto && !suspendInJTA())
436: conn.setAutoCommit(true);
437:
438: PreparedStatement stmnt = null;
439: try {
440: stmnt = prepareStatement(conn, insert);
441: executeUpdate(_conf, conn, stmnt, insert,
442: RowImpl.ACTION_INSERT);
443: } finally {
444: if (stmnt != null)
445: try {
446: stmnt.close();
447: } catch (SQLException se) {
448: }
449: if (!wasAuto && !suspendInJTA())
450: conn.setAutoCommit(false);
451: }
452: }
453:
454: /**
455: * Return the current sequence value, or -1 if unattainable.
456: */
457: protected long getSequence(ClassMapping mapping, Connection conn)
458: throws SQLException {
459: if (_log.isTraceEnabled())
460: _log.trace(_loc.get("get-seq"));
461:
462: Object pk = getPrimaryKey(mapping);
463: if (pk == null)
464: return -1;
465:
466: DBDictionary dict = _conf.getDBDictionaryInstance();
467: SQLBuffer sel = new SQLBuffer(dict).append(_seqColumn);
468: SQLBuffer where = new SQLBuffer(dict).append(_pkColumn).append(
469: " = ").appendValue(pk, _pkColumn);
470: String tableName = resolveTableName(mapping, _seqColumn
471: .getTable());
472: SQLBuffer tables = new SQLBuffer(dict).append(tableName);
473:
474: SQLBuffer select = dict.toSelect(sel, null, tables, where,
475: null, null, null, false, dict.supportsSelectForUpdate,
476: 0, Long.MAX_VALUE, false, true);
477:
478: PreparedStatement stmnt = prepareStatement(conn, select);
479: ResultSet rs = null;
480: try {
481: rs = executeQuery(_conf, conn, stmnt, select);
482: return getSequence(rs, dict);
483: } finally {
484: if (rs != null)
485: try {
486: rs.close();
487: } catch (SQLException se) {
488: }
489: if (stmnt != null)
490: try {
491: stmnt.close();
492: } catch (SQLException se) {
493: }
494: }
495: }
496:
497: /**
498: * Grabs the next handful of sequence numbers.
499: *
500: * @return true if the sequence was updated, false if no sequence
501: * row existed for this mapping
502: */
503: protected boolean setSequence(ClassMapping mapping, Status stat,
504: int inc, boolean updateStatSeq, Connection conn)
505: throws SQLException {
506: if (_log.isTraceEnabled())
507: _log.trace(_loc.get("update-seq"));
508:
509: Object pk = getPrimaryKey(mapping);
510: if (pk == null)
511: throw new InvalidStateException(_loc.get("bad-seq-type",
512: getClass(), mapping));
513:
514: DBDictionary dict = _conf.getDBDictionaryInstance();
515: SQLBuffer where = new SQLBuffer(dict).append(_pkColumn).append(
516: " = ").appendValue(pk, _pkColumn);
517:
518: // loop until we have a successful atomic select/update sequence
519: long cur = 0;
520: PreparedStatement stmnt;
521: ResultSet rs;
522: SQLBuffer upd;
523: for (int updates = 0; updates == 0;) {
524: stmnt = null;
525: rs = null;
526: try {
527: cur = getSequence(mapping, conn);
528: if (cur == -1)
529: return false;
530:
531: // update the value
532: upd = new SQLBuffer(dict);
533: String tableName = resolveTableName(mapping, _seqColumn
534: .getTable());
535: upd.append("UPDATE ").append(tableName).append(" SET ")
536: .append(_seqColumn).append(" = ").appendValue(
537: Numbers.valueOf(cur + inc), _seqColumn)
538: .append(" WHERE ").append(where)
539: .append(" AND ").append(_seqColumn).append(
540: " = ").appendValue(
541: Numbers.valueOf(cur), _seqColumn);
542:
543: stmnt = prepareStatement(conn, upd);
544: updates = executeUpdate(_conf, conn, stmnt, upd,
545: RowImpl.ACTION_UPDATE);
546: } finally {
547: if (rs != null)
548: try {
549: rs.close();
550: } catch (SQLException se) {
551: }
552: if (stmnt != null)
553: try {
554: stmnt.close();
555: } catch (SQLException se) {
556: }
557: }
558: }
559:
560: // setup new sequence range
561: synchronized (stat) {
562: if (updateStatSeq && stat.seq < cur)
563: stat.seq = cur;
564: if (stat.max < cur + inc)
565: stat.max = cur + inc;
566: }
567: return true;
568: }
569:
570: /**
571: * Resolve a fully qualified table name
572: *
573: * @param class
574: * mapping to get the schema name
575: */
576: public String resolveTableName(ClassMapping mapping, Table table) {
577: String sName = mapping.getTable().getSchemaName();
578: String tableName;
579: if (sName == null)
580: tableName = table.getFullName();
581: else
582: tableName = sName + "." + table.getName();
583: return tableName;
584: }
585:
586: /**
587: * Creates the sequence table in the DB.
588: */
589: public void refreshTable() throws SQLException {
590: if (_log.isInfoEnabled())
591: _log.info(_loc.get("make-seq-table"));
592:
593: // create the table
594: SchemaTool tool = new SchemaTool(_conf);
595: tool.setIgnoreErrors(true);
596: tool.createTable(_pkColumn.getTable());
597: }
598:
599: /**
600: * Drops the sequence table in the DB.
601: */
602: public void dropTable() throws SQLException {
603: if (_log.isInfoEnabled())
604: _log.info(_loc.get("drop-seq-table"));
605:
606: // drop the table
607: SchemaTool tool = new SchemaTool(_conf);
608: tool.setIgnoreErrors(true);
609: tool.dropTable(_pkColumn.getTable());
610: }
611:
612: /////////
613: // Main
614: /////////
615:
616: /**
617: * Usage: java org.apache.openjpa.jdbc.schema.TableJDBCSequence [option]*
618: * -action/-a <add | drop | get | set> [value]
619: * Where the following options are recognized.
620: * <ul>
621: * <li><i>-properties/-p <properties file or resource></i>: The
622: * path or resource name of a OpenJPA properties file containing
623: * information such as the license key and connection data as
624: * outlined in {@link JDBCConfiguration}. Optional.</li>
625: * <li><i>-<property name> <property value></i>: All bean
626: * properties of the OpenJPA {@link JDBCConfiguration} can be set by
627: * using their names and supplying a value. For example:
628: * <code>-licenseKey adslfja83r3lkadf</code></li>
629: * </ul>
630: * The various actions are as follows.
631: * <ul>
632: * <li><i>add</i>: Create the sequence table.</li>
633: * <li><i>drop</i>: Drop the sequence table.</li>
634: * <li><i>get</i>: Print the current sequence value.</li>
635: * <li><i>set</i>: Set the sequence value.</li>
636: * </ul>
637: */
638: public static void main(String[] args) throws Exception {
639: Options opts = new Options();
640: final String[] arguments = opts.setFromCmdLine(args);
641: boolean ret = Configurations.runAgainstAllAnchors(opts,
642: new Configurations.Runnable() {
643: public boolean run(Options opts) throws Exception {
644: JDBCConfiguration conf = new JDBCConfigurationImpl();
645: try {
646: return TableJDBCSeq.run(conf, arguments,
647: opts);
648: } finally {
649: conf.close();
650: }
651: }
652: });
653: if (!ret)
654: System.out.println(_loc.get("seq-usage"));
655: }
656:
657: /**
658: * Run the tool. Returns false if invalid options were given.
659: */
660: public static boolean run(JDBCConfiguration conf, String[] args,
661: Options opts) throws Exception {
662: String action = opts.removeProperty("action", "a", null);
663: Configurations.populateConfiguration(conf, opts);
664: return run(conf, args, action);
665: }
666:
667: /**
668: * Run the tool. Return false if an invalid option was given.
669: */
670: public static boolean run(JDBCConfiguration conf, String[] args,
671: String action) throws Exception {
672: if (args.length > 1
673: || (args.length != 0 && !ACTION_SET.equals(action)))
674: return false;
675:
676: TableJDBCSeq seq = new TableJDBCSeq();
677: String props = Configurations.getProperties(conf.getSequence());
678: Configurations.configureInstance(seq, conf, props);
679:
680: if (ACTION_DROP.equals(action))
681: seq.dropTable();
682: else if (ACTION_ADD.equals(action))
683: seq.refreshTable();
684: else if (ACTION_GET.equals(action) || ACTION_SET.equals(action)) {
685: Connection conn = conf.getDataSource2(null).getConnection();
686: try {
687: long cur = seq.getSequence(null, conn);
688: if (ACTION_GET.equals(action))
689: System.out.println(cur);
690: else {
691: long set;
692: if (args.length > 0)
693: set = Long.parseLong(args[0]);
694: else
695: set = cur + seq.getAllocate();
696: if (set < cur)
697: set = cur;
698: else {
699: Status stat = seq.getStatus(null);
700: seq.setSequence(null, stat, (int) (set - cur),
701: true, conn);
702: set = stat.seq;
703: }
704: System.err.println(set);
705: }
706: } catch (NumberFormatException nfe) {
707: return false;
708: } finally {
709: try {
710: conn.close();
711: } catch (SQLException se) {
712: }
713: }
714: } else
715: return false;
716: return true;
717: }
718:
719: /**
720: * Helper struct to hold status information.
721: */
722: protected static class Status implements Serializable {
723:
724: public long seq = 1L;
725: public long max = 0L;
726: }
727:
728: /**
729: * This method is to provide override for non-JDBC or JDBC-like
730: * implementation of preparing statement.
731: */
732: protected PreparedStatement prepareStatement(Connection conn,
733: SQLBuffer buf) throws SQLException {
734: return buf.prepareStatement(conn);
735: }
736:
737: /**
738: * This method is to provide override for non-JDBC or JDBC-like
739: * implementation of executing update.
740: */
741: protected int executeUpdate(JDBCConfiguration conf,
742: Connection conn, PreparedStatement stmnt, SQLBuffer buf,
743: int opcode) throws SQLException {
744: return stmnt.executeUpdate();
745: }
746:
747: /**
748: * This method is to provide override for non-JDBC or JDBC-like
749: * implementation of executing query.
750: */
751: protected ResultSet executeQuery(JDBCConfiguration conf,
752: Connection conn, PreparedStatement stmnt, SQLBuffer buf)
753: throws SQLException {
754: return stmnt.executeQuery();
755: }
756:
757: /**
758: * This method is to provide override for non-JDBC or JDBC-like
759: * implementation of getting sequence from the result set.
760: */
761: protected long getSequence(ResultSet rs, DBDictionary dict)
762: throws SQLException {
763: if (rs == null || !rs.next())
764: return -1;
765: return dict.getLong(rs, 1);
766: }
767: }
|