git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/modules@1350 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-4.0.0
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.sql.SQLException; | |||||
import java.util.Map; | import java.util.Map; | ||||
import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; | import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
* @usage _general_class_ | * @usage _general_class_ | ||||
*/ | */ | ||||
public interface Column | |||||
public interface Column | |||||
{ | { | ||||
/** | /** | ||||
* Meaningless placeholder object for inserting values in an autonumber | * Meaningless placeholder object for inserting values in an autonumber | ||||
* @usage _general_field_ | * @usage _general_field_ | ||||
*/ | */ | ||||
public static final Object AUTO_NUMBER = "<AUTO_NUMBER>"; | public static final Object AUTO_NUMBER = "<AUTO_NUMBER>"; | ||||
/** | /** | ||||
* Meaningless placeholder object for updating rows which indicates that a | * Meaningless placeholder object for updating rows which indicates that a | ||||
* given column should keep its existing value. | * given column should keep its existing value. | ||||
* @usage _general_field_ | * @usage _general_field_ | ||||
*/ | */ | ||||
public static final Object KEEP_VALUE = "<KEEP_VALUE>"; | public static final Object KEEP_VALUE = "<KEEP_VALUE>"; | ||||
/** | /** | ||||
* @usage _general_method_ | * @usage _general_method_ | ||||
*/ | */ | ||||
/** | /** | ||||
* @usage _general_method_ | * @usage _general_method_ | ||||
*/ | */ | ||||
public int getSQLType() throws SQLException; | |||||
public int getSQLType() throws IOException; | |||||
/** | /** | ||||
* @usage _general_method_ | * @usage _general_method_ | ||||
* @usage _general_method_ | * @usage _general_method_ | ||||
*/ | */ | ||||
public PropertyMap getProperties() throws IOException; | public PropertyMap getProperties() throws IOException; | ||||
/** | /** | ||||
* Returns the column which tracks the version history for an "append only" | * Returns the column which tracks the version history for an "append only" | ||||
* column. | * column. | ||||
* @usage _intermediate_method_ | * @usage _intermediate_method_ | ||||
*/ | */ | ||||
public ColumnValidator getColumnValidator(); | public ColumnValidator getColumnValidator(); | ||||
/** | /** | ||||
* Sets a new ColumnValidator. If {@code null}, resets to the value | * Sets a new ColumnValidator. If {@code null}, resets to the value | ||||
* returned from the Database's ColumnValidatorFactory (if the factory | * returned from the Database's ColumnValidatorFactory (if the factory | ||||
* @usage _intermediate_method_ | * @usage _intermediate_method_ | ||||
*/ | */ | ||||
public void setColumnValidator(ColumnValidator newValidator); | public void setColumnValidator(ColumnValidator newValidator); | ||||
public Object setRowValue(Object[] rowArray, Object value); | public Object setRowValue(Object[] rowArray, Object value); | ||||
public Object setRowValue(Map<String,Object> rowMap, Object value); | public Object setRowValue(Map<String,Object> rowMap, Object value); | ||||
public Object getRowValue(Object[] rowArray); | public Object getRowValue(Object[] rowArray); | ||||
public Object getRowValue(Map<String,?> rowMap); | public Object getRowValue(Map<String,?> rowMap); | ||||
} | } |
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.sql.SQLException; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
/** | /** | ||||
* Sets the type for the new column based on the given SQL type. | * Sets the type for the new column based on the given SQL type. | ||||
*/ | */ | ||||
public ColumnBuilder setSQLType(int type) throws SQLException { | |||||
public ColumnBuilder setSQLType(int type) throws IOException { | |||||
return setSQLType(type, 0, null); | return setSQLType(type, 0, null); | ||||
} | } | ||||
* data length (in type specific units). | * data length (in type specific units). | ||||
*/ | */ | ||||
public ColumnBuilder setSQLType(int type, int lengthInUnits) | public ColumnBuilder setSQLType(int type, int lengthInUnits) | ||||
throws SQLException | |||||
throws IOException | |||||
{ | { | ||||
return setSQLType(type, lengthInUnits, null); | return setSQLType(type, lengthInUnits, null); | ||||
} | } | ||||
*/ | */ | ||||
public ColumnBuilder setSQLType(int type, int lengthInUnits, | public ColumnBuilder setSQLType(int type, int lengthInUnits, | ||||
Database.FileFormat fileFormat) | Database.FileFormat fileFormat) | ||||
throws SQLException | |||||
throws IOException | |||||
{ | { | ||||
return setType(DataType.fromSQLType(type, lengthInUnits, fileFormat)); | return setType(DataType.fromSQLType(type, lengthInUnits, fileFormat)); | ||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
import java.math.BigInteger; | import java.math.BigInteger; | ||||
import java.sql.SQLException; | |||||
import java.sql.Types; | import java.sql.Types; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import com.healthmarketscience.jackcess.impl.DatabaseImpl; | import com.healthmarketscience.jackcess.impl.DatabaseImpl; | ||||
import com.healthmarketscience.jackcess.impl.JetFormat; | import com.healthmarketscience.jackcess.impl.JetFormat; | ||||
import com.healthmarketscience.jackcess.impl.SqlHelper; | |||||
/** | /** | ||||
* Supported access data types. | * Supported access data types. | ||||
return _maxSize; | return _maxSize; | ||||
} | } | ||||
public int getSQLType() throws SQLException { | |||||
public int getSQLType() throws IOException { | |||||
if (_sqlType != null) { | if (_sqlType != null) { | ||||
return _sqlType; | return _sqlType; | ||||
} | } | ||||
throw new SQLException("Unsupported data type: " + toString()); | |||||
throw new JackcessException("Unsupported data type: " + toString()); | |||||
} | } | ||||
public int getMinScale() { | public int getMinScale() { | ||||
} | } | ||||
public static DataType fromSQLType(int sqlType) | public static DataType fromSQLType(int sqlType) | ||||
throws SQLException | |||||
throws IOException | |||||
{ | { | ||||
return fromSQLType(sqlType, 0, null); | return fromSQLType(sqlType, 0, null); | ||||
} | } | ||||
public static DataType fromSQLType(int sqlType, int lengthInUnits) | public static DataType fromSQLType(int sqlType, int lengthInUnits) | ||||
throws SQLException | |||||
throws IOException | |||||
{ | { | ||||
return fromSQLType(sqlType, lengthInUnits, null); | return fromSQLType(sqlType, lengthInUnits, null); | ||||
} | } | ||||
public static DataType fromSQLType(int sqlType, int lengthInUnits, | public static DataType fromSQLType(int sqlType, int lengthInUnits, | ||||
Database.FileFormat fileFormat) | Database.FileFormat fileFormat) | ||||
throws SQLException | |||||
throws IOException | |||||
{ | { | ||||
DataType[] rtnArr = SQL_TYPES.get(sqlType); | DataType[] rtnArr = SQL_TYPES.get(sqlType); | ||||
if(rtnArr == null) { | if(rtnArr == null) { | ||||
throw new SQLException("Unsupported SQL type: " + sqlType); | |||||
throw new JackcessException("Unsupported SQL type: " + sqlType); | |||||
} | } | ||||
JetFormat format = | JetFormat format = | ||||
((fileFormat != null) ? | ((fileFormat != null) ? | ||||
DataType altType) | DataType altType) | ||||
{ | { | ||||
try { | try { | ||||
java.lang.reflect.Field sqlTypeField = Types.class.getField(typeName); | |||||
Integer value = (Integer)sqlTypeField.get(null); | |||||
Integer value = SqlHelper.INSTANCE.getNewSqlType(typeName); | |||||
SQL_TYPES.put(value, new DataType[]{type}); | SQL_TYPES.put(value, new DataType[]{type}); | ||||
if(altType != null) { | if(altType != null) { | ||||
ALT_SQL_TYPES.put(value, altType); | ALT_SQL_TYPES.put(value, altType); |
import java.nio.ByteOrder; | import java.nio.ByteOrder; | ||||
import java.nio.CharBuffer; | import java.nio.CharBuffer; | ||||
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.sql.Blob; | |||||
import java.sql.Clob; | |||||
import java.sql.SQLException; | |||||
import java.time.DateTimeException; | import java.time.DateTimeException; | ||||
import java.time.Duration; | import java.time.Duration; | ||||
import java.time.Instant; | import java.time.Instant; | ||||
} | } | ||||
@Override | @Override | ||||
public int getSQLType() throws SQLException { | |||||
public int getSQLType() throws IOException { | |||||
return _type.getSQLType(); | return _type.getSQLType(); | ||||
} | } | ||||
return null; | return null; | ||||
} else if(value instanceof CharSequence) { | } else if(value instanceof CharSequence) { | ||||
return (CharSequence)value; | return (CharSequence)value; | ||||
} else if(value instanceof Clob) { | |||||
try { | |||||
Clob c = (Clob)value; | |||||
// note, start pos is 1-based | |||||
return c.getSubString(1L, (int)c.length()); | |||||
} catch(SQLException e) { | |||||
throw (IOException)(new IOException(e.getMessage())).initCause(e); | |||||
} | |||||
} else if(SqlHelper.INSTANCE.isClob(value)) { | |||||
return SqlHelper.INSTANCE.getClobString(value); | |||||
} else if(value instanceof Reader) { | } else if(value instanceof Reader) { | ||||
char[] buf = new char[8 * 1024]; | char[] buf = new char[8 * 1024]; | ||||
StringBuilder sout = new StringBuilder(); | StringBuilder sout = new StringBuilder(); | ||||
return null; | return null; | ||||
} else if(value instanceof byte[]) { | } else if(value instanceof byte[]) { | ||||
return (byte[])value; | return (byte[])value; | ||||
} else if(value instanceof OleUtil.OleBlobImpl) { | |||||
return ((OleUtil.OleBlobImpl)value).getBytes(); | |||||
} else if(value instanceof Blob) { | |||||
try { | |||||
Blob b = (Blob)value; | |||||
// note, start pos is 1-based | |||||
return b.getBytes(1L, (int)b.length()); | |||||
} catch(SQLException e) { | |||||
throw (IOException)(new IOException(e.getMessage())).initCause(e); | |||||
} | |||||
} else if(value instanceof RawData) { | |||||
return ((RawData)value).getBytes(); | |||||
} else if(value instanceof InMemoryBlob) { | |||||
return ((InMemoryBlob)value).getBytes(); | |||||
} else if(SqlHelper.INSTANCE.isBlob(value)) { | |||||
return SqlHelper.INSTANCE.getBlobBytes(value); | |||||
} | } | ||||
ByteArrayOutputStream bout = new ByteArrayOutputStream(); | ByteArrayOutputStream bout = new ByteArrayOutputStream(); | ||||
/** | /** | ||||
* Wrapper for raw column data which can be re-written. | * Wrapper for raw column data which can be re-written. | ||||
*/ | */ | ||||
private static class RawData implements Serializable | |||||
private static final class RawData implements Serializable, InMemoryBlob | |||||
{ | { | ||||
private static final long serialVersionUID = 0L; | private static final long serialVersionUID = 0L; | ||||
_bytes = bytes; | _bytes = bytes; | ||||
} | } | ||||
private byte[] getBytes() { | |||||
@Override | |||||
public byte[] getBytes() { | |||||
return _bytes; | return _bytes; | ||||
} | } | ||||
return LocalDateTime.ofInstant(inst, db.getZoneId()); | return LocalDateTime.ofInstant(inst, db.getZoneId()); | ||||
} | } | ||||
} | } | ||||
/** internal interface for types which hold bytes in memory */ | |||||
static interface InMemoryBlob { | |||||
public byte[] getBytes() throws IOException; | |||||
} | |||||
} | } |
} | } | ||||
static final class OleBlobImpl implements OleBlob | |||||
static final class OleBlobImpl implements OleBlob, ColumnImpl.InMemoryBlob | |||||
{ | { | ||||
private byte[] _bytes; | private byte[] _bytes; | ||||
private ContentImpl _content; | private ContentImpl _content; | ||||
return _bytes.length; | return _bytes.length; | ||||
} | } | ||||
@Override | |||||
public byte[] getBytes() throws IOException { | public byte[] getBytes() throws IOException { | ||||
if(_bytes == null) { | if(_bytes == null) { | ||||
throw new IOException("blob is closed"); | throw new IOException("blob is closed"); |
/* | |||||
Copyright (c) 2021 James Ahlborn | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
*/ | |||||
package com.healthmarketscience.jackcess.impl; | |||||
import java.io.IOException; | |||||
/** | |||||
* Helper class to isolate the java.sql module interactions from the core of | |||||
* jackcess (in java 9+ environments). If the module is enabled (indicating | |||||
* that the application is already using sql constructs), then jackcess will | |||||
* seamlessly interact with sql types. If the module is not enabled | |||||
* (indicating that the application is not using any sql constructs), then | |||||
* jackcess will not require the module in order to function otherwise | |||||
* normally. | |||||
* | |||||
* This base class is the "fallback" class if the java.sql module is not | |||||
* available. | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public class SqlHelper { | |||||
public static final SqlHelper INSTANCE = loadInstance(); | |||||
public SqlHelper() {} | |||||
public boolean isBlob(Object value) { | |||||
return false; | |||||
} | |||||
public byte[] getBlobBytes(Object value) throws IOException { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
public boolean isClob(Object value) { | |||||
return false; | |||||
} | |||||
public CharSequence getClobString(Object value) throws IOException { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
public Integer getNewSqlType(String typeName) throws Exception { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
private static final SqlHelper loadInstance() { | |||||
// attempt to load the implementation of this class which works with | |||||
// java.sql classes. if that fails, use this fallback instance instead. | |||||
try { | |||||
return (SqlHelper) | |||||
Class.forName("com.healthmarketscience.jackcess.impl.SqlHelperImpl") | |||||
.newInstance(); | |||||
} catch(Throwable ignored) {} | |||||
return new SqlHelper(); | |||||
} | |||||
} |
/* | |||||
Copyright (c) 2021 James Ahlborn | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
*/ | |||||
package com.healthmarketscience.jackcess.impl; | |||||
import java.io.IOException; | |||||
import java.sql.Blob; | |||||
import java.sql.Clob; | |||||
import java.sql.SQLException; | |||||
import java.sql.Types; | |||||
/** | |||||
* Implementation of SqlHelperImpl which works with the java.sql modules | |||||
* classes. This class is used if the java.sql module is enabled in the | |||||
* application. | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public class SqlHelperImpl extends SqlHelper { | |||||
public SqlHelperImpl() {} | |||||
@Override | |||||
public boolean isBlob(Object value) { | |||||
return (value instanceof Blob); | |||||
} | |||||
@Override | |||||
public byte[] getBlobBytes(Object value) throws IOException { | |||||
try { | |||||
Blob b = (Blob)value; | |||||
// note, start pos is 1-based | |||||
return b.getBytes(1L, (int)b.length()); | |||||
} catch(SQLException e) { | |||||
throw (IOException)(new IOException(e.getMessage())).initCause(e); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean isClob(Object value) { | |||||
return (value instanceof Clob); | |||||
} | |||||
@Override | |||||
public CharSequence getClobString(Object value) throws IOException { | |||||
try { | |||||
Clob c = (Clob)value; | |||||
// note, start pos is 1-based | |||||
return c.getSubString(1L, (int)c.length()); | |||||
} catch(SQLException e) { | |||||
throw (IOException)(new IOException(e.getMessage())).initCause(e); | |||||
} | |||||
} | |||||
@Override | |||||
public Integer getNewSqlType(String typeName) throws Exception { | |||||
java.lang.reflect.Field sqlTypeField = Types.class.getField(typeName); | |||||
return (Integer)sqlTypeField.get(null); | |||||
} | |||||
} |
package com.healthmarketscience.jackcess.util; | package com.healthmarketscience.jackcess.util; | ||||
import com.healthmarketscience.jackcess.ColumnBuilder; | |||||
import com.healthmarketscience.jackcess.DataType; | |||||
import com.healthmarketscience.jackcess.Database; | |||||
import com.healthmarketscience.jackcess.Table; | |||||
import com.healthmarketscience.jackcess.TableBuilder; | |||||
import com.healthmarketscience.jackcess.impl.ByteUtil; | |||||
import com.healthmarketscience.jackcess.impl.DatabaseImpl; | |||||
import java.io.BufferedReader; | import java.io.BufferedReader; | ||||
import java.io.EOFException; | import java.io.EOFException; | ||||
import java.io.File; | import java.io.File; | ||||
import java.util.regex.Matcher; | import java.util.regex.Matcher; | ||||
import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||
import com.healthmarketscience.jackcess.ColumnBuilder; | |||||
import com.healthmarketscience.jackcess.DataType; | |||||
import com.healthmarketscience.jackcess.Database; | |||||
import com.healthmarketscience.jackcess.Table; | |||||
import com.healthmarketscience.jackcess.TableBuilder; | |||||
import com.healthmarketscience.jackcess.impl.ByteUtil; | |||||
import com.healthmarketscience.jackcess.impl.DatabaseImpl; | |||||
/** | /** | ||||
* Utility class for importing tables to an Access database from other | * Utility class for importing tables to an Access database from other | ||||
* sources. See the {@link Builder} for convenient configuration of the | * sources. See the {@link Builder} for convenient configuration of the | ||||
* @return a List of Columns | * @return a List of Columns | ||||
*/ | */ | ||||
public static List<ColumnBuilder> toColumns(ResultSetMetaData md) | public static List<ColumnBuilder> toColumns(ResultSetMetaData md) | ||||
throws SQLException | |||||
throws SQLException, IOException | |||||
{ | { | ||||
List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>(); | List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>(); | ||||
for (int i = 1; i <= md.getColumnCount(); i++) { | for (int i = 1; i <= md.getColumnCount(); i++) { |
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.nio.channels.FileChannel; | import java.nio.channels.FileChannel; | ||||
import java.nio.channels.NonWritableChannelException; | import java.nio.channels.NonWritableChannelException; | ||||
import java.sql.SQLException; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.EnumSet; | import java.util.EnumSet; | ||||
import java.util.List; | import java.util.List; | ||||
import com.healthmarketscience.jackcess.DataType; | import com.healthmarketscience.jackcess.DataType; | ||||
import com.healthmarketscience.jackcess.Database; | import com.healthmarketscience.jackcess.Database; | ||||
import com.healthmarketscience.jackcess.JackcessException; | |||||
import static com.healthmarketscience.jackcess.Database.*; | import static com.healthmarketscience.jackcess.Database.*; | ||||
import com.healthmarketscience.jackcess.DatabaseBuilder; | import com.healthmarketscience.jackcess.DatabaseBuilder; | ||||
import com.healthmarketscience.jackcess.PropertyMap; | import com.healthmarketscience.jackcess.PropertyMap; | ||||
Integer sqlType = null; | Integer sqlType = null; | ||||
try { | try { | ||||
sqlType = dt.getSQLType(); | sqlType = dt.getSQLType(); | ||||
} catch(SQLException ignored) {} | |||||
} catch(JackcessException ignored) {} | |||||
if(sqlType != null) { | if(sqlType != null) { | ||||
assertEquals(dt, DataType.fromSQLType(sqlType)); | assertEquals(dt, DataType.fromSQLType(sqlType)); |