提交 840bfed4 authored 作者: thomasmueller's avatar thomasmueller

Merge branch 'master' of https://github.com/h2database/h2database

......@@ -258,13 +258,14 @@ ALTER TABLE TEST RENAME CONSTRAINT FOO TO BAR
"Commands (DDL)","ALTER TABLE ALTER COLUMN","
ALTER TABLE [ IF EXISTS ] tableName ALTER COLUMN columnName
{ { dataType [ DEFAULT expression ] [ [ NOT ] NULL ] [ AUTO_INCREMENT | IDENTITY ] }
{ { dataType [ VISIBLE | INVISIBLE ] [ DEFAULT expression ] [ [ NOT ] NULL ] [ AUTO_INCREMENT | IDENTITY ] }
| { RENAME TO name }
| { RESTART WITH long }
| { SELECTIVITY int }
| { SET DEFAULT expression }
| { SET NULL }
| { SET NOT NULL } }
| { SET NOT NULL }
| { SET { VISIBLE | INVISIBLE } } }
","
Changes the data type of a column, rename a column,
change the identity value, or change the selectivity.
......@@ -287,6 +288,9 @@ Single column indexes on this column are dropped.
SET NOT NULL sets a column to not allow NULL. Rows may not contains NULL in this column.
SET INVISIBLE makes the column hidden, i.e. it will not appear in SELECT * results.
SET VISIBLE has the reverse effect.
This command commits an open transaction in this connection.
","
ALTER TABLE TEST ALTER COLUMN NAME CLOB;
......@@ -296,6 +300,8 @@ ALTER TABLE TEST ALTER COLUMN NAME SELECTIVITY 100;
ALTER TABLE TEST ALTER COLUMN NAME SET DEFAULT '';
ALTER TABLE TEST ALTER COLUMN NAME SET NOT NULL;
ALTER TABLE TEST ALTER COLUMN NAME SET NULL;
ALTER TABLE TEST ALTER COLUMN NAME SET VISIBLE;
ALTER TABLE TEST ALTER COLUMN NAME SET INVISIBLE;
"
"Commands (DDL)","ALTER TABLE DROP COLUMN","
......@@ -1759,6 +1765,7 @@ AES
"Other Grammar","Column Definition","
columnName dataType
[ VISIBLE | INVISIBLE ]
[ { DEFAULT expression | AS computedColumnExpression } ] [ [ NOT ] NULL ]
[ { AUTO_INCREMENT | IDENTITY } [ ( startInt [, incrementInt ] ) ] ]
[ SELECTIVITY selectivity ] [ COMMENT expression ]
......@@ -1771,12 +1778,16 @@ Identity and auto-increment columns are columns with a sequence as the
default. The column declared as the identity columns is implicitly the
primary key column of this table (unlike auto-increment columns).
The invisible column will not be displayed as a result of SELECT * query.
Otherwise, it works as normal column.
The options PRIMARY KEY, UNIQUE, and CHECK are not supported for ALTER statements.
Check constraints can reference columns of the table,
and they can reference objects that exist while the statement is executed.
Conditions are only checked when a row is added or modified
in the table where the constraint exists.
","
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255) DEFAULT '');
CREATE TABLE TEST(ID BIGINT IDENTITY);
......
......@@ -85,6 +85,8 @@ Advanced
Database Upgrade</a><br />
<a href="#java_objects_serialization">
Java Objects Serialization</a><br />
<a href="#custom_data_types_handler_api">
Custom Data Types Handler API</a><br />
<a href="#limits_limitations">
Limits and Limitations</a><br />
<a href="#glossary_links">
......@@ -1691,6 +1693,29 @@ Please note that this SQL statement can only be executed before any tables are d
</ul>
</p>
<h2 id="custom_data_types_handler_api">Custom Data Types Handler API</h2>
<p>
It is possible to extend the type system of the database by providing your own implementation
of minimal required API basically consisting of type identification and conversion routines.
</p>
<p>
In order to enable this feature, set the system property <code>h2.customDataTypesHandler</code> (default: null) to the fully qualified name of the class providing <a href="../javadoc/org/h2/api/CustomDataTypesHandler.html">CustomDataTypesHandler</a> interface implementation. <br>
The instance of that class will be created by H2 and used to:
<ul>
<li>resolve the names and identifiers of extrinsic data types.
</li>
<li>convert values of extrinsic data types to and from values of built-in types.
</li>
<li>provide order of the data types.
</li>
</ul>
</p>
<p>This is a system-level setting, i.e. affects all the databases.</p>
<p><b>Note: </b>Please keep in mind that this feature may not possibly provide the same ABI stability level as other features as it exposes many of the H2 internals. You may be required to update your code occasionally due to internal changes in H2 if you are going to use this feature.
</p>
<h2 id="limits_limitations">Limits and Limitations</h2>
<p>
This database has the following known limitations:
......
......@@ -21,6 +21,10 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Added API for handling custom data types (System property "h2.customDataTypesHandler", API org.h2.api.CustomDataTypesHandler).
</li>
<li>Added support for invisible columns.
</li>
</ul>
<h2>Version 1.4.194 (2017-03-10)</h2>
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.api;
import org.h2.store.DataHandler;
import org.h2.value.DataType;
import org.h2.value.Value;
/**
* Custom data type handler
* Provides means to plug-in custom data types support
*
* Please keep in mind that this feature may not possibly
* provide the same ABI stability level as other features
* as it exposes many of the H2 internals. You may be
* required to update your code occasionally due to internal
* changes in H2 if you are going to use this feature
*/
public interface CustomDataTypesHandler {
/**
* Get custom data type given its name
*
* @param name data type name
* @return custom data type
*/
DataType getDataTypeByName(String name);
/**
* Get custom data type given its integer id
*
* @param type identifier of a data type
* @return custom data type
*/
DataType getDataTypeById(int type);
/**
* Get order for custom data type given its integer id
*
* @param type identifier of a data type
* @return order associated with custom data type
*/
int getDataTypeOrder(int type);
/**
* Convert the provided source value into value of given target data type
* Shall implement conversions to and from custom data types.
*
* @param source source value
* @param targetType identifier of target data type
* @return converted value
*/
Value convert(Value source, int targetType);
/**
* Get custom data type class name given its integer id
*
* @param type identifier of a data type
* @return class name
*/
String getDataTypeClassName(int type);
/**
* Get custom data type identifier given corresponding Java class
* @param cls Java class object
* @return type identifier
*/
int getTypeIdFromClass(Class<?> cls);
/**
* Get {@link org.h2.value.Value} object
* corresponding to given data type identifier and data.
*
* @param type custom data type identifier
* @param data underlying data type value
* @param dataHandler data handler object
* @return Value object
*/
Value getValue(int type, Object data, DataHandler dataHandler);
/**
* Converts {@link org.h2.value.Value} object
* to the specified class.
*
* @param value
* @param cls
* @return result
*/
Object getObject(Value value, Class<?> cls);
/**
* Checks if type supports add operation
*
* @param type custom data type identifier
* @return True, if custom data type supports add operation
*/
boolean supportsAdd(int type);
/**
* Get compatible type identifier that would not overflow
* after many add operations.
*
* @param type identifier of a type
* @return resulting type identifier
*/
int getAddProofType(int type);
}
......@@ -461,6 +461,11 @@ public interface CommandInterface {
*/
int EXPLAIN_ANALYZE = 86;
/**
* The type of a ALTER TABLE ALTER COLUMN SET INVISIBLE statement.
*/
int ALTER_TABLE_ALTER_COLUMN_VISIBILITY = 87;
/**
* Get command type.
*
......
......@@ -3993,6 +3993,11 @@ public class Parser {
} else {
column = parseColumnWithType(columnName);
}
if (readIf("INVISIBLE")) {
column.setVisible(false);
} else if (readIf("VISIBLE")) {
column.setVisible(true);
}
if (readIf("NOT")) {
read("NULL");
column.setNullable(false);
......@@ -5718,6 +5723,14 @@ public class Parser {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
command.setDefaultExpression(defaultExpression);
return command;
} else if (readIf("INVISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(false);
return command;
} else if (readIf("VISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(true);
return command;
}
} else if (readIf("RESTART")) {
readIf("WITH");
......
......@@ -44,6 +44,8 @@ import org.h2.util.New;
* ALTER TABLE ALTER COLUMN SET DEFAULT,
* ALTER TABLE ALTER COLUMN SET NOT NULL,
* ALTER TABLE ALTER COLUMN SET NULL,
* ALTER TABLE ALTER COLUMN SET VISIBLE,
* ALTER TABLE ALTER COLUMN SET INVISIBLE,
* ALTER TABLE DROP COLUMN
*/
public class AlterTableAlterColumn extends SchemaCommand {
......@@ -60,6 +62,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
private boolean ifNotExists;
private ArrayList<Column> columnsToAdd;
private ArrayList<Column> columnsToRemove;
private boolean newVisibility;
public AlterTableAlterColumn(Session session, Schema schema) {
super(session, schema);
......@@ -156,9 +159,13 @@ public class AlterTableAlterColumn extends SchemaCommand {
} else if (!oldColumn.isNullable() && newColumn.isNullable()) {
checkNullable(table);
}
if (oldColumn.getVisible() ^ newColumn.getVisible()) {
oldColumn.setVisible(newColumn.getVisible());
}
convertAutoIncrementColumn(table, newColumn);
copyData(table);
}
table.setModified();
break;
}
case CommandInterface.ALTER_TABLE_ADD_COLUMN: {
......@@ -192,6 +199,12 @@ public class AlterTableAlterColumn extends SchemaCommand {
db.updateMeta(session, table);
break;
}
case CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY: {
oldColumn.setVisible(newVisibility);
table.setModified();
db.updateMeta(session, table);
break;
}
default:
DbException.throwInternalError("type=" + type);
}
......@@ -550,4 +563,8 @@ public class AlterTableAlterColumn extends SchemaCommand {
public void setColumnsToRemove(ArrayList<Column> columnsToRemove) {
this.columnsToRemove = columnsToRemove;
}
public void setVisible(boolean visible) {
this.newVisibility = visible;
}
}
......@@ -731,6 +731,9 @@ public class Select extends Query {
String alias = filter.getTableAlias();
Column[] columns = t.getColumns();
for (Column c : columns) {
if (!c.getVisible()) {
continue;
}
if (filter.isNaturalJoinColumn(c)) {
continue;
}
......
......@@ -522,6 +522,16 @@ public class SysProperties {
public static final String JAVA_OBJECT_SERIALIZER =
Utils.getProperty("h2.javaObjectSerializer", null);
/**
* System property <code>h2.customDataTypesHandler</code>
* (default: null).<br />
* The CustomDataTypesHandler class name that is used
* to provide support for user defined custom data types.
* It must be the same on client and server to work correctly.
*/
public static final String CUSTOM_DATA_TYPES_HANDLER =
Utils.getProperty("h2.customDataTypesHandler", null);
private static final String H2_BASE_DIR = "h2.baseDir";
private SysProperties() {
......
......@@ -22,6 +22,7 @@ import org.h2.mvstore.type.DataType;
import org.h2.result.SortOrder;
import org.h2.store.DataHandler;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueArray;
......@@ -67,6 +68,7 @@ public class ValueDataType implements DataType {
private static final int STRING_0_31 = 68;
private static final int BYTES_0_31 = 100;
private static final int SPATIAL_KEY_2D = 132;
private static final int CUSTOM_DATA_TYPE = 133;
final DataHandler handler;
final CompareMode compareMode;
......@@ -428,6 +430,14 @@ public class ValueDataType implements DataType {
break;
}
default:
if (JdbcUtils.customDataTypesHandler != null) {
byte[] b = v.getBytesNoCopy();
buff.put((byte)CUSTOM_DATA_TYPE).
putVarInt(type).
putVarInt(b.length).
put(b);
break;
}
DbException.throwInternalError("type=" + v.getType());
}
}
......@@ -592,6 +602,16 @@ public class ValueDataType implements DataType {
}
case SPATIAL_KEY_2D:
return getSpatialDataType().read(buff);
case CUSTOM_DATA_TYPE: {
if (JdbcUtils.customDataTypesHandler != null) {
int customType = readVarInt(buff);
int len = readVarInt(buff);
byte[] b = DataUtils.newBytes(len);
buff.get(b, 0, len);
return JdbcUtils.customDataTypesHandler.convert(ValueBytes.getNoCopy(b), customType);
}
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "No CustomDataTypesHandler has been set up");
}
default:
if (type >= INT_0_15 && type < INT_0_15 + 16) {
return ValueInt.get(type - INT_0_15);
......
......@@ -98,13 +98,14 @@ ALTER TABLE [ IF EXISTS ] tableName RENAME oldConstraintName TO newConstraintNam
Renames a constraint."
"Commands (DDL)","ALTER TABLE ALTER COLUMN","
ALTER TABLE [ IF EXISTS ] tableName ALTER COLUMN columnName
{ { dataType [ DEFAULT expression ] [ [ NOT ] NULL ] [ AUTO_INCREMENT | IDENTITY ] }
{ { dataType [ VISIBLE | INVISIBLE ] [ DEFAULT expression ] [ [ NOT ] NULL ] [ AUTO_INCREMENT | IDENTITY ] }
| { RENAME TO name }
| { RESTART WITH long }
| { SELECTIVITY int }
| { SET DEFAULT expression }
| { SET NULL }
| { SET NOT NULL } }
| { SET NOT NULL }
| { SET { VISIBLE | INVISIBLE } } }
","
Changes the data type of a column, rename a column,
change the identity value, or change the selectivity."
......@@ -555,6 +556,7 @@ AES
Only the algorithm AES (""AES-128"") is supported currently."
"Other Grammar","Column Definition","
columnName dataType
[ VISIBLE | INVISIBLE ]
[ { DEFAULT expression | AS computedColumnExpression } ] [ [ NOT ] NULL ]
[ { AUTO_INCREMENT | IDENTITY } [ ( startInt [, incrementInt ] ) ] ]
[ SELECTIVITY selectivity ] [ COMMENT expression ]
......
......@@ -86,6 +86,7 @@ public class Column {
private SingleColumnResolver resolver;
private String comment;
private boolean primaryKey;
private boolean visible = true;
public Column(String name, int type) {
this(name, type, -1, -1, -1);
......@@ -257,6 +258,14 @@ public class Column {
nullable = b;
}
public boolean getVisible() {
return visible;
}
public void setVisible(boolean b) {
visible = b;
}
/**
* Validate the value, convert it if required, and update the sequence value
* if required. If the value is null, the default value (NULL if no default
......@@ -436,6 +445,11 @@ public class Column {
default:
}
}
if (!visible) {
buff.append(" INVISIBLE ");
}
if (defaultExpression != null) {
String sql = defaultExpression.getSQL();
if (sql != null) {
......@@ -748,6 +762,7 @@ public class Column {
isComputed = source.isComputed;
selectivity = source.selectivity;
primaryKey = source.primaryKey;
visible = source.visible;
}
}
......@@ -21,6 +21,7 @@ import java.util.HashSet;
import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;
import org.h2.api.CustomDataTypesHandler;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.SysProperties;
......@@ -38,6 +39,11 @@ public class JdbcUtils {
*/
public static JavaObjectSerializer serializer;
/**
* Custom data types handler to use.
*/
public static CustomDataTypesHandler customDataTypesHandler;
private static final String[] DRIVERS = {
"h2:", "org.h2.Driver",
"Cache:", "com.intersys.jdbc.CacheDriver",
......@@ -117,6 +123,15 @@ public class JdbcUtils {
throw DbException.convert(e);
}
}
String customTypeHandlerClass = SysProperties.CUSTOM_DATA_TYPES_HANDLER;
if (customTypeHandlerClass != null) {
try {
customDataTypesHandler = (CustomDataTypesHandler) loadUserClass(customTypeHandlerClass).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
/**
......
......@@ -682,6 +682,11 @@ public class DataType {
return ValueGeometry.getFromGeometry(x);
}
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getValue(type,
rs.getObject(columnIndex),
session.getDataHandler());
}
throw DbException.throwInternalError("type="+type);
}
return v;
......@@ -764,6 +769,9 @@ public class DataType {
case Value.GEOMETRY:
return GEOMETRY_CLASS_NAME;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getDataTypeClassName(type);
}
throw DbException.throwInternalError("type="+type);
}
}
......@@ -779,6 +787,9 @@ public class DataType {
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?");
}
DataType dt = TYPES_BY_VALUE_TYPE.get(type);
if (dt == null && JdbcUtils.customDataTypesHandler != null) {
dt = JdbcUtils.customDataTypesHandler.getDataTypeById(type);
}
if (dt == null) {
dt = TYPES_BY_VALUE_TYPE.get(Value.NULL);
}
......@@ -971,6 +982,9 @@ public class DataType {
} else if (LocalDateTimeUtils.isOffsetDateTime(x)) {
return Value.TIMESTAMP_TZ;
} else {
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getTypeIdFromClass(x);
}
return Value.JAVA_OBJECT;
}
}
......@@ -1094,6 +1108,9 @@ public class DataType {
} else if (LocalDateTimeUtils.isOffsetDateTime(x.getClass())) {
return LocalDateTimeUtils.offsetDateTimeToValue(x);
} else {
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getValue(type, x, session.getDataHandler());
}
return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
}
}
......@@ -1132,7 +1149,11 @@ public class DataType {
* @return the data type object
*/
public static DataType getTypeByName(String s) {
return TYPES_BY_NAME.get(s);
DataType result = TYPES_BY_NAME.get(s);
if (result == null && JdbcUtils.customDataTypesHandler != null) {
result = JdbcUtils.customDataTypesHandler.getDataTypeByName(s);
}
return result;
}
/**
......@@ -1178,7 +1199,29 @@ public class DataType {
case Value.LONG:
case Value.SHORT:
return true;
case Value.BOOLEAN:
case Value.TIME:
case Value.DATE:
case Value.TIMESTAMP:
case Value.TIMESTAMP_TZ:
case Value.BYTES:
case Value.UUID:
case Value.STRING:
case Value.STRING_IGNORECASE:
case Value.STRING_FIXED:
case Value.BLOB:
case Value.CLOB:
case Value.NULL:
case Value.JAVA_OBJECT:
case Value.UNKNOWN:
case Value.ARRAY:
case Value.RESULT_SET:
case Value.GEOMETRY:
return false;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.supportsAdd(type);
}
return false;
}
}
......@@ -1202,7 +1245,31 @@ public class DataType {
return Value.DECIMAL;
case Value.SHORT:
return Value.LONG;
case Value.BOOLEAN:
case Value.DECIMAL:
case Value.TIME:
case Value.DATE:
case Value.TIMESTAMP:
case Value.TIMESTAMP_TZ:
case Value.BYTES:
case Value.UUID:
case Value.STRING:
case Value.STRING_IGNORECASE:
case Value.STRING_FIXED:
case Value.BLOB:
case Value.CLOB:
case Value.DOUBLE:
case Value.NULL:
case Value.JAVA_OBJECT:
case Value.UNKNOWN:
case Value.ARRAY:
case Value.RESULT_SET:
case Value.GEOMETRY:
return type;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getAddProofType(type);
}
return type;
}
}
......@@ -1253,12 +1320,44 @@ public class DataType {
} else if (paramClass == Array.class) {
return new JdbcArray(conn, v, 0);
}
if (v.getType() == Value.JAVA_OBJECT) {
switch (v.getType()) {
case Value.JAVA_OBJECT: {
Object o = SysProperties.serializeJavaObject ? JdbcUtils.deserialize(v.getBytes(),
conn.getSession().getDataHandler()) : v.getObject();
if (paramClass.isAssignableFrom(o.getClass())) {
return o;
}
break;
}
case Value.BOOLEAN:
case Value.BYTE:
case Value.SHORT:
case Value.INT:
case Value.LONG:
case Value.DECIMAL:
case Value.TIME:
case Value.DATE:
case Value.TIMESTAMP:
case Value.TIMESTAMP_TZ:
case Value.BYTES:
case Value.UUID:
case Value.STRING:
case Value.STRING_IGNORECASE:
case Value.STRING_FIXED:
case Value.BLOB:
case Value.CLOB:
case Value.DOUBLE:
case Value.FLOAT:
case Value.NULL:
case Value.UNKNOWN:
case Value.ARRAY:
case Value.RESULT_SET:
case Value.GEOMETRY:
break;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getObject(v, paramClass);
}
}
throw DbException.getUnsupportedException("converting to class " + paramClass.getName());
}
......
......@@ -530,6 +530,10 @@ public class Transfer {
}
break;
default:
if (JdbcUtils.customDataTypesHandler != null) {
writeBytes(v.getBytesNoCopy());
break;
}
throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "type=" + type);
}
}
......@@ -706,6 +710,9 @@ public class Transfer {
}
return ValueGeometry.get(readString());
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.convert(ValueBytes.getNoCopy(readBytes()), type);
}
throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "type=" + type);
}
}
......
......@@ -274,56 +274,59 @@ public abstract class Value {
static int getOrder(int type) {
switch (type) {
case UNKNOWN:
return 1;
return 1_000;
case NULL:
return 2;
return 2_000;
case STRING:
return 10;
return 10_000;
case CLOB:
return 11;
return 11_000;
case STRING_FIXED:
return 12;
return 12_000;
case STRING_IGNORECASE:
return 13;
return 13_000;
case BOOLEAN:
return 20;
return 20_000;
case BYTE:
return 21;
return 21_000;
case SHORT:
return 22;
return 22_000;
case INT:
return 23;
return 23_000;
case LONG:
return 24;
return 24_000;
case DECIMAL:
return 25;
return 25_000;
case FLOAT:
return 26;
return 26_000;
case DOUBLE:
return 27;
return 27_000;
case TIME:
return 30;
return 30_000;
case DATE:
return 31;
return 31_000;
case TIMESTAMP:
return 32;
return 32_000;
case TIMESTAMP_TZ:
return 34;
return 34_000;
case BYTES:
return 40;
return 40_000;
case BLOB:
return 41;
return 41_000;
case JAVA_OBJECT:
return 42;
return 42_000;
case UUID:
return 43;
return 43_000;
case GEOMETRY:
return 44;
return 44_000;
case ARRAY:
return 50;
return 50_000;
case RESULT_SET:
return 51;
return 51_000;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getDataTypeOrder(type);
}
throw DbException.throwInternalError("type:"+type);
}
}
......@@ -967,6 +970,9 @@ public abstract class Value {
case GEOMETRY:
return ValueGeometry.get(s);
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.convert(this, targetType);
}
throw DbException.throwInternalError("type=" + targetType);
}
} catch (NumberFormatException e) {
......
......@@ -91,6 +91,7 @@ import org.h2.test.jdbc.TestCallableStatement;
import org.h2.test.jdbc.TestCancel;
import org.h2.test.jdbc.TestConcurrentConnectionUsage;
import org.h2.test.jdbc.TestConnection;
import org.h2.test.jdbc.TestCustomDataTypesHandler;
import org.h2.test.jdbc.TestDatabaseEventListener;
import org.h2.test.jdbc.TestDriver;
import org.h2.test.jdbc.TestJavaObject;
......@@ -772,6 +773,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestTransactionIsolation());
addTest(new TestUpdatableResultSet());
addTest(new TestZloty());
addTest(new TestCustomDataTypesHandler());
// jdbcx
addTest(new TestConnectionPool());
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.jdbc;
import org.h2.api.CustomDataTypesHandler;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.store.DataHandler;
import org.h2.test.TestBase;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDouble;
import org.h2.value.ValueJavaObject;
import org.h2.value.ValueString;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.text.DecimalFormat;
import java.util.Locale;
/**
* Tests {@link CustomDataTypesHandler}.
*
*/
public class TestCustomDataTypesHandler extends TestBase {
/** */
public final static String DB_NAME = "customDataTypes";
/** */
public final static String HANDLER_NAME_PROPERTY = "h2.customDataTypesHandler";
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty(HANDLER_NAME_PROPERTY, TestOnlyCustomDataTypesHandler.class.getName());
TestBase test = createCaller().init();
test.config.traceTest = true;
test.config.memory = true;
test.config.networked = true;
test.config.beforeTest();
test.test();
test.config.afterTest();
System.clearProperty(HANDLER_NAME_PROPERTY);
}
@Override
public void test() throws Exception {
try {
JdbcUtils.customDataTypesHandler = new TestOnlyCustomDataTypesHandler();
deleteDb(DB_NAME);
Connection conn = getConnection(DB_NAME);
Statement stat = conn.createStatement();
//Test cast
ResultSet rs = stat.executeQuery("select CAST('1-1i' AS complex) + '1+1i' ");
rs.next();
assertTrue(rs.getObject(1).equals(new ComplexNumber(2, 0)));
//Test create table
stat.execute("create table t(id int, val complex)");
rs = conn.getMetaData().getColumns(null, null, "T", "VAL");
rs.next();
assertEquals(rs.getString("TYPE_NAME"), "complex");
assertEquals(rs.getInt("DATA_TYPE"), Types.JAVA_OBJECT);
rs = stat.executeQuery("select val from t");
assertEquals(ComplexNumber.class.getName(), rs.getMetaData().getColumnClassName(1));
//Test insert
PreparedStatement stmt = conn.prepareStatement("insert into t(id, val) values (0, '1.0+1.0i'), (1, ?), (2, ?), (3, ?)");
stmt.setObject(1, new ComplexNumber(1, -1));
stmt.setObject(2, "5.0+2.0i");
stmt.setObject(3, 100.1);
stmt.executeUpdate();
//Test selects
ComplexNumber[] expected = new ComplexNumber[4];
expected[0] = new ComplexNumber(1, 1);
expected[1] = new ComplexNumber(1, -1);
expected[2] = new ComplexNumber(5, 2);
expected[3] = new ComplexNumber(100.1, 0);
for (int id = 0; id < expected.length; ++id) {
PreparedStatement prepStat = conn.prepareStatement("select val from t where id = ?");
prepStat.setInt(1, id);
rs = prepStat.executeQuery();
assertTrue(rs.next());
assertTrue(rs.getObject(1).equals(expected[id]));
}
for (int id = 0; id < expected.length; ++id) {
PreparedStatement prepStat = conn.prepareStatement("select id from t where val = ?");
prepStat.setObject(1, expected[id]);
rs = prepStat.executeQuery();
assertTrue(rs.next());
assertEquals(rs.getInt(1), id);
}
// Repeat selects with index
stat.execute("create index val_idx on t(val)");
for (int id = 0; id < expected.length; ++id) {
PreparedStatement prepStat = conn.prepareStatement("select id from t where val = ?");
prepStat.setObject(1, expected[id]);
rs = prepStat.executeQuery();
assertTrue(rs.next());
assertEquals(rs.getInt(1), id);
}
// sum function
rs = stat.executeQuery("select sum(val) from t");
rs.next();
assertTrue(rs.getObject(1).equals(new ComplexNumber(107.1, 2)));
// user function
stat.execute("create alias complex_mod for \""+ getClass().getName() + ".complexMod\"");
rs = stat.executeQuery("select complex_mod(val) from t where id=2");
rs.next();
assertEquals(complexMod(expected[2]), rs.getDouble(1));
conn.close();
deleteDb(DB_NAME);
} finally {
JdbcUtils.customDataTypesHandler = null;
}
}
/**
* modulus function
* @param val complex number
* @return result
*/
public static double complexMod(ComplexNumber val) {
return val.mod();
}
/**
* The custom data types handler to use for this test.
*/
public static class TestOnlyCustomDataTypesHandler implements CustomDataTypesHandler {
/** Type name for complex number */
public final static String COMPLEX_DATA_TYPE_NAME = "complex";
/** Type id for complex number */
public final static int COMPLEX_DATA_TYPE_ID = 1000;
/** Order for complex number data type */
public final static int COMPLEX_DATA_TYPE_ORDER = 100_000;
/** Cached DataType instance for complex number */
public final DataType complexDataType;
/** */
public TestOnlyCustomDataTypesHandler() {
complexDataType = createComplex();
}
/** {@inheritDoc} */
@Override
public DataType getDataTypeByName(String name) {
if (name.toLowerCase(Locale.ENGLISH).equals(COMPLEX_DATA_TYPE_NAME)) {
return complexDataType;
}
return null;
}
/** {@inheritDoc} */
@Override
public DataType getDataTypeById(int type) {
if (type == COMPLEX_DATA_TYPE_ID) {
return complexDataType;
}
return null;
}
/** {@inheritDoc} */
@Override
public String getDataTypeClassName(int type) {
if (type == COMPLEX_DATA_TYPE_ID) {
return ComplexNumber.class.getName();
}
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "type:" + type);
}
/** {@inheritDoc} */
@Override
public int getTypeIdFromClass(Class<?> cls) {
if (cls == ComplexNumber.class) {
return COMPLEX_DATA_TYPE_ID;
}
return Value.JAVA_OBJECT;
}
/** {@inheritDoc} */
@Override
public Value convert(Value source, int targetType) {
if (source.getType() == targetType) {
return source;
}
if (targetType == COMPLEX_DATA_TYPE_ID) {
switch (source.getType()) {
case Value.JAVA_OBJECT: {
assert source instanceof ValueJavaObject;
return ValueComplex.get((ComplexNumber)JdbcUtils.deserialize(source.getBytesNoCopy(), null));
}
case Value.STRING: {
assert source instanceof ValueString;
return ValueComplex.get(ComplexNumber.parseComplexNumber(source.getString()));
}
case Value.BYTES: {
assert source instanceof ValueBytes;
return ValueComplex.get((ComplexNumber)JdbcUtils.deserialize(source.getBytesNoCopy(), null));
}
case Value.DOUBLE: {
assert source instanceof ValueDouble;
return ValueComplex.get(new ComplexNumber(source.getDouble(), 0));
}
}
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, source.getString());
} else {
return source.convertTo(targetType);
}
}
/** {@inheritDoc} */
@Override
public int getDataTypeOrder(int type) {
if (type == COMPLEX_DATA_TYPE_ID) {
return COMPLEX_DATA_TYPE_ORDER;
}
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "type:" + type);
}
/** {@inheritDoc} */
@Override
public Value getValue(int type, Object data, DataHandler dataHandler) {
if (type == COMPLEX_DATA_TYPE_ID) {
assert data instanceof ComplexNumber;
return ValueComplex.get((ComplexNumber)data);
}
return ValueJavaObject.getNoCopy(data, null, dataHandler);
}
/** {@inheritDoc} */
@Override
public Object getObject(Value value, Class<?> cls) {
if (cls.equals(ComplexNumber.class)) {
if (value.getType() == COMPLEX_DATA_TYPE_ID) {
return value.getObject();
}
return convert(value, COMPLEX_DATA_TYPE_ID).getObject();
}
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "type:" + value.getType());
}
/** {@inheritDoc} */
@Override
public boolean supportsAdd(int type) {
if (type == COMPLEX_DATA_TYPE_ID) {
return true;
}
return false;
}
/** {@inheritDoc} */
@Override
public int getAddProofType(int type) {
if (type == COMPLEX_DATA_TYPE_ID) {
return type;
}
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "type:" + type);
}
/** Constructs data type instance for complex number type */
private DataType createComplex() {
DataType result = new DataType();
result.type = COMPLEX_DATA_TYPE_ID;
result.name = COMPLEX_DATA_TYPE_NAME;
result.sqlType = Types.JAVA_OBJECT;
return result;
}
}
/**
* Value type implementation that holds the complex number
*/
public static class ValueComplex extends Value {
/**
* data itself
*/
private ComplexNumber val;
/**
* @param val complex number
*/
public ValueComplex(ComplexNumber val) {
assert val != null;
this.val = val;
}
/**
* Get ValueComplex instance for given ComplexNumber
* @param val complex number
* @result resulting instance
*/
public static ValueComplex get(ComplexNumber val) {
return new ValueComplex(val);
}
/** {@inheritDoc} */
@Override
public String getSQL() {
return val.toString();
}
/** {@inheritDoc} */
@Override
public int getType() {
return TestOnlyCustomDataTypesHandler.COMPLEX_DATA_TYPE_ID;
}
/** {@inheritDoc} */
@Override
public long getPrecision() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getDisplaySize() {
return 0;
}
/** {@inheritDoc} */
@Override
public String getString() {
return val.toString();
}
/** {@inheritDoc} */
@Override
public Object getObject() {
return val;
}
/** {@inheritDoc} */
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
Object obj = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
prep.setObject(parameterIndex, obj, Types.JAVA_OBJECT);
}
/** {@inheritDoc} */
@Override
protected int compareSecure(Value v, CompareMode mode) {
return val.compare((ComplexNumber) v.getObject());
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return val.hashCode();
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof ValueComplex)) {
return false;
}
ValueComplex complex = (ValueComplex)other;
return complex.val.equals(val);
}
/** {@inheritDoc} */
@Override
public Value convertTo(int targetType) {
if (getType() == targetType) {
return this;
}
switch (targetType) {
case Value.BYTES: {
return ValueBytes.getNoCopy(JdbcUtils.serialize(val, null));
}
case Value.STRING: {
return ValueString.get(val.toString());
}
case Value.DOUBLE: {
assert val.im == 0;
return ValueDouble.get(val.re);
}
case Value.JAVA_OBJECT: {
return ValueJavaObject.getNoCopy(JdbcUtils.serialize(val, null));
}
}
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
/** {@inheritDoc} */
@Override
public Value add(Value value) {
ValueComplex v = (ValueComplex)value;
return ValueComplex.get(val.add(v.val));
}
}
/**
* Complex number
*/
public static class ComplexNumber implements Serializable {
/** */
private static final long serialVersionUID = 1L;
/** */
public final static DecimalFormat REAL_FMT = new DecimalFormat("###.###");
/** */
public final static DecimalFormat IMG_FMT = new DecimalFormat("+###.###i;-###.###i");
/**
* Real part
*/
private double re;
/**
* Imaginary part
*/
private double im;
/**
* @param re real part
* @param im imaginary part
*/
public ComplexNumber(double re, double im) {
this.re = re;
this.im = im;
}
/**
* Addition
* @param other value to add
* @return result
*/
public ComplexNumber add(ComplexNumber other) {
return new ComplexNumber(re + other.re, im + other.im);
}
/**
* Returns modulus
* @return result
*/
public double mod() {
return Math.sqrt(re * re + im * im);
}
/**
* Compares two complex numbers
*
* True ordering of complex number has no sense,
* so we apply lexicographical order.
*
* @param v number to compare this with
* @return result of comparison
*/
public int compare(ComplexNumber v) {
if (re == v.re && im == v.im) {
return 0;
}
if (re == v.re) {
return im > v.im ? 1 : -1;
} else if (re > v.re) {
return 1;
} else {
return -1;
}
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return (int)re | (int)im;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof ComplexNumber)) {
return false;
}
ComplexNumber complex = (ComplexNumber)other;
return (re==complex.re) && (im == complex.im);
}
/** {@inheritDoc} */
@Override
public String toString() {
if (im == 0.0) {
return REAL_FMT.format(re);
}
if (re == 0.0) {
return IMG_FMT.format(im);
}
return REAL_FMT.format(re) + "" + IMG_FMT.format(im);
}
/**
* Simple parser for complex numbers. Both real and im components
* must be written in non scientific notation.
* @param s String.
* @return {@link ComplexNumber} object.
*/
public static ComplexNumber parseComplexNumber(String s) {
if (StringUtils.isNullOrEmpty(s))
return null;
s = s.replaceAll("\\s", "");
boolean hasIm = (s.charAt(s.length() - 1) == 'i');
int signs = 0;
int pos = 0;
int maxSignPos = -1;
while (pos != -1) {
pos = s.indexOf('-', pos);
if (pos != -1) {
signs++;
maxSignPos = Math.max(maxSignPos, pos++);
}
}
pos = 0;
while (pos != -1) {
pos = s.indexOf('+', pos);
if (pos != -1) {
signs++;
maxSignPos = Math.max(maxSignPos, pos++);
}
}
if (signs > 2 || (signs == 2 && !hasIm))
throw new NumberFormatException();
double real;
double im;
if (signs == 0 || (signs == 1 && maxSignPos == 0)) {
if (hasIm) {
real = 0;
if (signs == 0 && s.length() == 1) {
im = 1.0;
} else if (signs > 0 && s.length() == 2) {
im = (s.charAt(0) == '-') ? -1.0 : 1.0;
} else {
im = Double.parseDouble(s.substring(0, s.length() - 1));
}
} else {
real = Double.parseDouble(s);
im = 0;
}
} else {
real = Double.parseDouble(s.substring(0, maxSignPos));
if (s.length() - maxSignPos == 2) {
im = (s.charAt(maxSignPos) == '-') ? -1.0 : 1.0;
} else {
im = Double.parseDouble(s.substring(maxSignPos, s.length() - 1));
}
}
return new ComplexNumber(real, im);
}
}
}
......@@ -4915,6 +4915,22 @@ SELECT group_concat(name) FROM TEST group by id;
drop table test;
> ok
create table test(a int primary key, b int invisible, c int);
> ok
select * from test;
> A C
> - -
> rows: 0
select a, b, c from test;
> A B C
> - - -
> rows: 0
drop table test;
> ok
--- script drop ---------------------------------------------------------------------------------------------
create memory table test (id int primary key, im_ie varchar(10));
> ok
......@@ -6183,6 +6199,49 @@ SELECT * FROM TEST;
DROP TABLE TEST;
> ok
create table test(id int, name varchar invisible);
> ok
select * from test;
> ID
> --
> rows: 0
alter table test alter column name set visible;
> ok
select * from test;
> ID NAME
> -- ----
> rows: 0
alter table test add modify_date timestamp invisible before name;
> ok
select * from test;
> ID NAME
> -- ----
> rows: 0
alter table test alter column modify_date timestamp visible;
> ok
select * from test;
> ID MODIFY_DATE NAME
> -- ----------- ----
> rows: 0
alter table test alter column modify_date set invisible;
> ok
select * from test;
> ID NAME
> -- ----
> rows: 0
drop table test;
> ok
--- autoIncrement ----------------------------------------------------------------------------------------------
CREATE MEMORY TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
> ok
......
......@@ -815,6 +815,7 @@ public class TestFileSystem extends TestBase {
final int size = getSize(10, 50);
f.write(ByteBuffer.allocate(size * 64 * 1024));
Random random = new Random(1);
System.gc();
Task task = new Task() {
@Override
public void call() throws Exception {
......@@ -864,6 +865,7 @@ public class TestFileSystem extends TestBase {
ra.close();
file.delete();
FileUtils.delete(s);
System.gc();
}
}
......
......@@ -931,6 +931,7 @@ public class Build extends BuildBase {
ret = execJava(args(
"-ea",
"-Xmx128m",
"-XX:MaxDirectMemorySize=2g",
"-cp", cp,
"org.h2.test.TestAll", "fast"));
} else {
......
aacute aaload aastore abalance abba abbreviate abbreviated abbreviates
abbreviation ability able abnormal abnormally abort aborted about above abs
abbreviation abi ability able abnormal abnormally abort aborted about above abs
absence absent absolute absolutely abstract abstraction abstractions abstracts
aca accept acceptable acceptance accepted accepting accepts access accessadmin
accessed accesses accessible accessing accesskey accessor accidentally
......@@ -97,7 +97,7 @@ classifications classloader classloaders classname classpath clause clauses claz
clean cleaned cleaner cleaning cleanup clear cleared clearer clearing clearly
clears cleartext click clicked clicking client clients clinton clip clipboard
clob clobs clock clocks clone cloneable cloned closable close closeable closed
closely closer closes closest closing cloud clubs clue clunky cluster clustered
closely closer closes closest closing cloud cls clubs clue clunky cluster clustered
clustering cmd cms cnf cnrs cnt coalesce code codebase codebook coded codegen
codehaus codes coding codist coffee cognitect col cold coldrick coll
collaborative collapse collate collateral collation collations collator collators
......@@ -234,12 +234,12 @@ exfsys exhausted exhibit exist existed existence existing exists exit exited
exits exp expand expanded expands expansion expect expected expecting expedites
expense expenses expensive experience experimental experiments experts expiration
expired expires explain explanation explicit explicitconstructorcall explicitly
exploit explorer exponent exponential export exported exports expose exposed expr
express expressed expression expressions expressly exps ext extend extendable
exploit explorer exponent exponential export exported exports expose exposed exposes
expr express expressed expression expressions expressly exps ext extend extendable
extended extending extends extensible extension extensions extensively extent
extern external externally extra extract extracted extracting extracts extras
extreme extremely extremes eye fabien fabric facade face facilitate facility fact
factor factorial factories factory factual fadd fail failed failing fails failure
extreme extremely extremes extrinsic eye fabien fabric facade face facilitate facility
fact factor factorial factories factory factual fadd fail failed failing fails failure
failures fair fake fall fallback falls faload false familiar families family faq
far fashion fashioned fast faster fastest fastore fat fatal faulhaber fault
favicon favorite fbj fcmpg fcmpl fconst fdiv feature features feb februar
......@@ -252,7 +252,7 @@ firebird firebirdsql fired firefox firewall first firstname fish fit fitness fit
fitting five fix fixed fixes fixing fkcolumn fktable flag flags flash flashback
flat fle fletcher flexibility flexible flexive flip flipped fload float floating
flooding floor florent flow flower flows fluent fluid flush flushed flushes
flushing flux fly flyway fmb fmc fml fmrn fmul fmxx fmxxx fneg focus focusable
flushing flux fly flyway fmb fmc fml fmrn fmt fmul fmxx fmxxx fneg focus focusable
fog fogh folder follow followed following follows font fontes foo footer footers
footprint for forall forbidden force forced forcefully forces forcing foreign
forever forge forget forgetting forgot forgotten fork form formal format
......@@ -297,12 +297,12 @@ however hprof href hsql hsqldb htime htm html http httpdocs https huang hub huff
huffman huge human hundred hundreds hurt hyc hyde hyperbolic hyperlink hypersonic
hyt iacute iadd iaload iand iastore ibm iced iceland iciql icirc icmpeq icmpge
icmpgt icmple icmplt icmpne ico icon iconified icons iconst icu ide idea ideal
ideas identical identified identifier identifiers identify identifying identities
identity idiomatic idiv idle ids idx idxname iee ieee iexcl iface ifeq ifexists
ifge ifgt ifle iflt ifne ifnonnull ifnull iframe ifx ignore ignorecase ignored
ideas identical identification identified identifier identifiers identify identifying
identities identity idiomatic idiv idle ids idx idxname iee ieee iexcl iface ifeq
ifexists ifge ifgt ifle iflt ifne ifnonnull ifnull iframe ifx ignore ignorecase ignored
ignoredriverprivileges ignorelist ignores ignoring igrave iinc ikura ikvm ikvmc
illegal iload image imageio images img iml immediately immutable imola imp impact
imperial impersonate impl imple implement implementation implementations
illegal iload image imageio images imaginary img iml immediately immutable imola imp
impact imperial impersonate impl imple implement implementation implementations
implemented implementing implements implication implicit implicitly implied
implies import important imported importing imports impose imposes impossible
improperly improve improved improvement improvements improves improving imul
......@@ -329,11 +329,11 @@ instead institutes instr instruction instructions instrument instrumentation
instrumented int intact integer integers integrate integrated integration
integrity intellectual intelli intended intentional inter interaction interactive
intercepted interest interested interesting interface interfaces interleave
interleaved interleaving intermediate intern internal internally international
internationalization internet interpolation interpret interpreted interpreter
interpreting interprets interrupt interrupted interrupting interruption intersect
intersecting intersection intersects intersys interval intervals into intra
introduce introduced introduction inttypes inv inval invalid invalidate
interleaved interleaving intermediate intern internal internally internals
international internationalization internet interpolation interpret interpreted
interpreter interpreting interprets interrupt interrupted interrupting interruption
intersect intersecting intersection intersects intersys interval intervals into
intra introduce introduced introduction inttypes inv inval invalid invalidate
invalidated invectorate invented invention inventor inversed invert inverting
invisible invocation invoice invoiceid invoke invokeinterface invoker
invokespecial invokestatic invokevirtual involve involved involves ior iota ipt
......@@ -363,8 +363,8 @@ lawsuits lax layer layers layout lazily lazy lcase lceil lck lcmp lconst ldap
ldbc ldc ldiv ldquo lea leach lead leading leads leaf leak leaked leaks leaning
leap learning least leave leaves leaving lee left leftmost leftover legacy legal
legend lehmann lempel len length lengths lenient leod less lesser let lets letter
letters level levels lfloor lgpl liability liable lib liberal libraries library
licensable license licensed licensees licenses licensing lies life lifespan
letters level levels lexicographical lfloor lgpl liability liable lib liberal libraries
library licensable license licensed licensees licenses licensing lies life lifespan
lifetime liftweb light lightweight like likely lim limit limitation limitations
limited limiting limits line linear linearly linefeed lines linestring lineup
link linkage linked links linq lint linux liq liqui lir lirs lisboa list listed
......@@ -431,9 +431,9 @@ november now nowait nowrap npl nsi nsis nsub ntext ntfs nth ntilde nucleus nul
null nullable nullid nullif nulls nullsoft num number numbering numbers numeral
numerals numeric numerical nuxeo nvarchar nvl oacute obey obj object objects
obligation obligations observer obsolete obtain obtained obtains obviously
occupied occupies occupy occur occurred occurrence occurrences occurs ocirc octal
octet october octype odbc odbcad odd odg off offending offer offered offering
offers office official offline offset offsets often ogc ograve ohloh oid okay
occasionally occupied occupies occupy occur occurred occurrence occurrences occurs
ocirc octal octet october octype odbc odbcad odd odg off offending offer offered
offering offers office official offline offset offsets often ogc ograve ohloh oid okay
okra olap olapsys old older oldest oline oliver olivier omega omicron omissions
omitted omitting once onchange onclick one ones onfocus ongoing onkeydown onkeyup
online onload only onmousedown onmousemove onmouseout onmouseover onmouseup
......@@ -554,7 +554,7 @@ ridvan rife right rightmost rights rijndael ring rioyxlgt risk risks risky rlm
rmd rmdir rmerr rmi rmiregistry rnd rnfr rnto road roadmap roads robert roc rogue
rojas role roles roll rollback rollbacks rolled rolling rollover rolls roman room
root roots rot rotate round rounded rounding roundmagic rounds routine routinely
row rowcount rowid rowlock rownum rows rowscn rowsize roy royalty rpad rpm rsa
routines row rowcount rowid rowlock rownum rows rowscn rowsize roy royalty rpad rpm rsa
rsaquo rsquo rss rtree rtrim ruby ruebezahl rule rules run rund rundll runnable
runner runners running runs runscript runtime rwd rws sabine safari safe safely
safes safety said sainsbury salary sale sales saload salt salz sam same
......@@ -602,8 +602,8 @@ specification specified specifies specify specifying specs speed speeds speedup
spell spellcheck spellchecker spelled spelling spends spent sphere spi spiced
spin spliced split splits splitting sponsored spot spots spr spread spring
springframework springfuse sql sqlexpress sqli sqlite sqlj sqlnulls sqlserver
sqlstate sqlxml sqrt square squill squirrel src srcs srid ssd ssl stabilize
stable stack stackable stacked stage stages stamp standalone standard
sqlstate sqlxml sqrt square squill squirrel src srcs srid ssd ssl stability
stabilize stable stack stackable stacked stage stages stamp standalone standard
standardized standards standby standing stands star staring start started starter
starting starts startup starvation starves stat state stated statement statements
states static stating station statistic statistical statisticlog statistics stats
......@@ -697,7 +697,7 @@ vast vector vectors vendor venue verbatim verbose verification verified verifier
verifies verify verifying versa version versioned versioning versions versus
vertica vertical very verysmallint veto via vice victor videos view viewed viewer
viewport views vii viii violate violated violation violations virtual virus
viruses visible vision visit visited visitor visitors vista visual visualize
viruses visible visibility vision visit visited visitor visitors vista visual visualize
visualizer vividsolutions vladykin void volatile volume volunteer volunteers von
vpda vulnerabilities vulnerability wait waited waiting waits waives wake wakes
walk walker want wants war warehouse warehouses warn warned warning warnings
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论