All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.sql.backends.Oracle8 Maven / Gradle / Ivy

The newest version!
/*
 * Tentackle - https://tentackle.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.tentackle.sql.backends;

import org.tentackle.common.Service;
import org.tentackle.sql.Backend;
import org.tentackle.sql.BackendException;
import org.tentackle.sql.NonStandardCommons;
import org.tentackle.sql.SqlNameType;
import org.tentackle.sql.SqlType;
import org.tentackle.sql.metadata.ColumnMetaData;
import org.tentackle.sql.metadata.OracleColumnMetaData;
import org.tentackle.sql.metadata.TableMetaData;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.format.DateTimeFormatter;
import java.util.Properties;

/**
 * Backend for older Oracle versions 8 to 11.
* Requires a URL with type postfix {@code "|Oracle8"}. * * @author harald */ @Service(Backend.class) public class Oracle8 extends AbstractSql2003Backend { /** window function part 1. */ public static final String SQL_NESTED_SELECT_1 = "/*+ FIRST_ROWS */ * FROM (SELECT "; /** window function part 2. */ public static final String SQL_NESTED_SELECT_2 = ") WHERE "; /** condition for limit and offset. */ public static final String SQL_NESTED_SELECT_ROWNUM = "ROWNUM"; /** condition for limit and offset. */ public static final String SQL_NESTED_SELECT_LIMIT = SQL_NESTED_SELECT_ROWNUM + SQL_LESSOREQUAL_PAR; /** condition for limit and offset. */ public static final String SQL_NESTED_SELECT_OFFSET = SQL_NESTED_SELECT_ROWNUM + SQL_GREATER_PAR; // oracle format strings private static final String ORA_DATE_FORMAT = "YYYY-MM-DD"; private static final String ORA_TIME_FORMAT = "HH24:MI:SS"; private static final String ORA_TIMESTAMP_FORMAT = ORA_DATE_FORMAT + " " + ORA_TIME_FORMAT; private static final String ORA_TO_DATE_LEAD = "TO_DATE('"; private static final String ORA_TO_DATE_SEP = "','"; private static final String ORA_TO_DATE_TAIL = "')"; // java format strings private static final String JAVA_DATE_FORMAT = "yyyy-MM-dd"; private static final String JAVA_TIME_FORMAT = "HH:mm:ss"; private static final String JAVA_TIMESTAMP_FORMAT = JAVA_DATE_FORMAT + " " + JAVA_TIME_FORMAT; // java formats private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(JAVA_DATE_FORMAT); private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(JAVA_TIME_FORMAT); private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern(JAVA_TIMESTAMP_FORMAT); // Oracle empty string (special because Oracle treats it as NULL) private static final String ORACLE_EMPTY_STRING = " "; // one blank // maximum length for identifiers, names, etc... private static final int MAX_NAME_LENGTH = 30; private static final String[] RESERVED_SCHEMAS = { "ANONYMOUS", "APEX_030200", "APEX_PUBLIC_USER", "APPQOSSYS", "BI", "CTXSYS", "DBSNMP", "DIP", "EXFSYS", "FLOWS_FILES", "HR", "IX", "MDDATA", "MDSYS", "MGMT_VIEW", "OE", "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", "OWBSYS", "OWBSYS_AUDIT", "PM", "SCOTT", "SH", "SI_INFORMTN_SCHEMA", "SPATIAL_CSW_ADMIN_USR", "SPATIAL_WFS_ADMIN_USR", "SYS", "SYSMAN", "SYSTEM", "WMSYS", "XDB", "XS$NULL" }; /** * Converts a date to a string. * * @param date the date * @return a TO_DATE-date-string */ public static String dateString(Date date) { return ORA_TO_DATE_LEAD + DATE_FORMAT.format(date.toLocalDate()) + ORA_TO_DATE_SEP + ORA_DATE_FORMAT + ORA_TO_DATE_TAIL; } /** * Converts a time to a string. * * @param time the time * @return a TO_DATE-time-string */ public static String timeString(Time time) { return ORA_TO_DATE_LEAD + TIME_FORMAT.format(time.toLocalTime()) + ORA_TO_DATE_SEP + ORA_TIME_FORMAT + ORA_TO_DATE_TAIL; } /** * Converts a timestamp to a string. * * @param timestamp the timestamp * @return a TO_DATE-timestamp-string */ public static String timestampString(Timestamp timestamp) { return ORA_TO_DATE_LEAD + TIMESTAMP_FORMAT.format(timestamp.toLocalDateTime()) + ORA_TO_DATE_SEP + ORA_TIMESTAMP_FORMAT + ORA_TO_DATE_TAIL; } /** * Creates the backend for Oracle 8. */ public Oracle8() { // see -Xlint:missing-explicit-ctor since Java 16 } @Override public boolean isDeprecated() { return true; } @Override public String getDriverClassName() { return "oracle.jdbc.driver.OracleDriver"; } @Override public Connection createConnection(String url, String username, char[] password) throws SQLException { Properties props = new Properties(); props.put("user", username); if (password != null) { props.put("password", createPassword(password)); } props.put("remarksReporting","true"); // for migrator // DriverManager.getConnection() doesn't work in most containers, // unless the class is preloaded within the application's classloader. try { Class.forName(getDriverClassName()); } catch (ClassNotFoundException e) { // no logger available, will fail below anyway... } return DriverManager.getConnection (url, props); } @Override public boolean isMatchingUrl(String url) { return url.contains(":oracle:"); } @Override public String getName() { return "Oracle8"; } @Override public String getBackendId(Connection connection) { try (Statement stmt = connection.createStatement()) { ResultSet rs = stmt.executeQuery("select sys_context('userenv','sid') from dual"); if (rs.next()) { return "SID-" + rs.getString(1); } return null; } catch (SQLException ex) { throw new BackendException("cannot determine backend id", ex); } } @Override public boolean isSchemaSupported() { return false; } @Override public boolean isTemporaryName(String name) { // oracle does not allow identifiers to start with an underscore -> we use "tmp_" instead. return name != null && (name.startsWith("tmp_") || name.startsWith("TMP_")); } @Override public boolean isReservedTableName(String name) { return super.isReservedTableName(name) || name != null && name.contains("$"); } @Override public boolean isReservedSchemaName(String name) { for (String reserved_schema: RESERVED_SCHEMAS) { if (reserved_schema.equalsIgnoreCase(name)) { return true; } } return super.isReservedSchemaName(name); } @Override public void assertValidName(SqlNameType nameType, String name) { super.assertValidName(nameType, name); if (name != null && name.startsWith("_")) { throw new BackendException(nameType + " '" + name + "' is illegal for backend " + this); } } @Override public int getMaxNameLength(SqlNameType nameType) { return MAX_NAME_LENGTH; } @Override public boolean isTransientTransactionException(SQLException ex) { return super.isTransientTransactionException(ex) || isExceptionErrorCodeMatching(ex, 54, // ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired 60, // ORA-00060: deadlock detected while waiting for resource 1013, // ORA-01013: user requested cancel of current operation 4020, // ORA-04020 deadlock detected while trying to lock object 4021, // ORA-04021 timeout occurred while waiting to lock object 30006); // ORA-30006: resource busy; acquire with WAIT timeout expired } @Override public String getEmptyString() { return ORACLE_EMPTY_STRING; } @Override public String getDummySelect() { return "SELECT 1 FROM DUAL"; } @Override public String columnTypeNullDefaultToString(String columnName, SqlType sqlType, int size, int scale, boolean nullable, Object defaultValue) { StringBuilder buf = new StringBuilder(); buf.append(columnTypeToString(sqlType, size, scale)); // oracle wants default first and then not null if (defaultValue != null) { buf.append(" DEFAULT "); buf.append(valueToLiteral(sqlType, defaultValue)); } if (!nullable) { buf.append(" NOT NULL"); } return buf.toString(); } @Override public String valueToLiteral(SqlType sqlType, Object value) { if (value instanceof String && ((String) value).isEmpty()) { value = ORACLE_EMPTY_STRING; } if (value instanceof Boolean) { return Boolean.TRUE.equals(value) ? "1" : "0"; } return super.valueToLiteral(sqlType, value); } @Override public ColumnMetaData createColumnMetaData(TableMetaData tableMetaData) { return new OracleColumnMetaData(tableMetaData); } @Override public boolean isExtraCommitRequired() { return true; } @Override public void buildSelectSql(StringBuilder sqlBuilder, boolean writeLock, int limit, int offset) { boolean insertSelect = isLeadingSelectMissing(sqlBuilder); if (limit > 0 || offset > 0) { sqlBuilder.insert(0, SQL_NESTED_SELECT_1); } if (insertSelect) { sqlBuilder.insert(0, SQL_SELECT); } if (writeLock) { sqlBuilder.append(SQL_FOR_UPDATE); } if (limit > 0 || offset > 0) { sqlBuilder.append(SQL_NESTED_SELECT_2); if (offset > 0) { sqlBuilder.append(SQL_NESTED_SELECT_OFFSET); } if (limit > 0) { if (offset > 0) { sqlBuilder.append(SQL_AND); } sqlBuilder.append(SQL_NESTED_SELECT_LIMIT); } } } @Override public int getMaxSize(SqlType sqlType) { return switch (sqlType) { case DECIMAL -> 37; case VARCHAR -> 4000; default -> super.getMaxSize(sqlType); }; } @Override public String sqlTypeToString(SqlType sqlType, int size) { return switch (sqlType) { case BIT, TINYINT -> TYPE_NUMBER_1; case SMALLINT -> TYPE_NUMBER_5; case INTEGER -> TYPE_NUMBER_10; case BIGINT -> TYPE_NUMBER_19; case FLOAT -> TYPE_REAL; case DOUBLE -> TYPE_DOUBLE; case DECIMAL -> TYPE_NUMBER; case CHAR -> TYPE_CHAR_1; case VARCHAR -> TYPE_VARCHAR; case DATE, TIME -> TYPE_DATE; case TIMESTAMP -> TYPE_TIMESTAMP; case BLOB -> TYPE_BLOB; case CLOB -> TYPE_CLOB; default -> super.sqlTypeToString(sqlType, size); }; } @Override public SqlType[] jdbcTypeToSqlType(int jdbcType, int size, int scale) { switch(jdbcType) { case Types.BIT: case Types.TINYINT: return new SqlType[] { SqlType.BIT, SqlType.TINYINT }; case Types.NUMERIC: case Types.DECIMAL: if (scale == 0) { switch (size) { case 1: return new SqlType[] {SqlType.BIT, SqlType.TINYINT}; case 5: return new SqlType[] {SqlType.SMALLINT}; case 10: return new SqlType[] {SqlType.INTEGER}; case 19: return new SqlType[] {SqlType.BIGINT}; } } return new SqlType[] { SqlType.DECIMAL }; case Types.DATE: case Types.TIME: return new SqlType[] { SqlType.DATE, SqlType.TIME }; default: return super.jdbcTypeToSqlType(jdbcType, size, scale); } } @Override public String sqlNextFromSequence(String name) { return "SELECT " + name + ".NEXTVAL FROM DUAL"; } @Override public String sqlRenameTable(String tableName, String newTableName) { return "RENAME TABLE " + tableName + " TO " + newTableName + ";\n"; } @Override public String sqlCreateTableComment(String tableName, String comment) { return NonStandardCommons.sqlCreateCommentOnTable(this, tableName, comment); } @Override public String sqlCreateColumnComment(String tableName, String columnName, String comment) { return NonStandardCommons.sqlCreateCommentOnColumn(this, tableName, columnName, comment); } @Override public String sqlAddColumn(String tableName, String columnName, String comment, SqlType sqlType, int size, int scale, boolean nullable, Object defaultValue) { return SQL_ALTER_TABLE + tableName + " ADD (" + sqlCreateTableAttributeWithoutComment(columnName, sqlType, size, scale, nullable, defaultValue, false, false) + ");\n"; } @Override public String sqlAlterColumnNullConstraint(String tableName, String columnName, boolean nullable) { StringBuilder buf = new StringBuilder(SQL_ALTER_TABLE); buf.append(tableName); buf.append(" MODIFY ("); buf.append(columnName); if (nullable) { buf.append(" NULL"); } else { buf.append(" NOT NULL"); } buf.append(");\n"); return buf.toString(); } @Override public String sqlAlterColumnType(String tableName, String columnName, String comment, SqlType sqlType, int size, int scale, boolean nullable, Object defaultValue) { return SQL_ALTER_TABLE + tableName + " MODIFY (" + sqlCreateTableAttributeWithoutComment(columnName, sqlType, size, scale, true, null, false, false) + ");\n"; } @Override public String sqlAlterColumnDefault(String tableName, String columnName, SqlType sqlType, Object defaultValue) { StringBuilder buf = new StringBuilder(SQL_ALTER_TABLE); buf.append(tableName); buf.append(" MODIFY "); buf.append(columnName); if (defaultValue == null) { // functionally equivalent to DROP DEFAULT, but not on the DDL level, // To get rid of the default NULL the column must be added, copied, renamed, dropped // or the table renamed, re-created, copied, dropped. // Oracle... buf.append(" DEFAULT NULL"); } else { buf.append(" DEFAULT "); buf.append(valueToLiteral(sqlType, defaultValue)); } buf.append(";\n"); return buf.toString(); } @Override protected String extractWhereClause(String sql, int whereOffset) { sql = super.extractWhereClause(sql, whereOffset); int ndx = sql.lastIndexOf(SQL_NESTED_SELECT_ROWNUM, whereOffset); if (ndx >= 0) { sql = sql.substring(0, ndx); } return sql; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy