提交 437073c1 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 da6e461b
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
public class ClassUtils {
public static Class loadClass(String className) throws ClassNotFoundException {
// TODO support special syntax to load classes using another classloader
return Class.forName(className);
}
}
......@@ -151,6 +151,7 @@ public class DateTimeUtils {
if(s.endsWith("Z")) {
s = s.substring(0, s.length()-1);
tz = TimeZone.getTimeZone("UTC");
} else {
int timezoneStart = s.indexOf('+', s2 + 1);
if(timezoneStart < 0) {
......
......@@ -314,7 +314,7 @@ public class FileUtils {
return fileName.startsWith(MEMORY_PREFIX) || fileName.startsWith(MEMORY_PREFIX_2);
}
public static String createTempFile(String name, String suffix, boolean deleteOnExit) throws IOException, SQLException {
public static String createTempFile(String name, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException, SQLException {
name += ".";
if(isInMemory(name)) {
for(int i=0;; i++) {
......@@ -327,12 +327,18 @@ public class FileUtils {
}
}
String prefix = new File(name).getName();
File dir = new File(name).getAbsoluteFile().getParentFile();
dir.mkdirs();
File dir;
if(inTempDir) {
dir = null;
} else {
dir = new File(name).getAbsoluteFile().getParentFile();
dir.mkdirs();
}
File f = File.createTempFile(prefix, suffix, dir);
if(deleteOnExit) {
f.deleteOnExit();
}
// return f.getPath();
return f.getCanonicalPath();
}
......@@ -344,6 +350,7 @@ public class FileUtils {
}
public static String[] listFiles(String path) throws SQLException {
//System.out.println("listFiles: " + path);
if(isInMemory(path)) {
String[] list = new String[memoryFiles.size()];
MemoryFile[] l = new MemoryFile[memoryFiles.size()];
......@@ -353,19 +360,34 @@ public class FileUtils {
}
return list;
}
File f = new File(path);
try {
File[] files = new File(path).listFiles();
if(files == null) {
String[] list = f.list();
if(list == null) {
return new String[0];
}
String[] list = new String[files.length];
for(int i=0; i<files.length; i++) {
list[i] = files[i].getCanonicalPath();
String base = f.getCanonicalPath() + File.separator;
for(int i=0; i<list.length; i++) {
list[i] = base + list[i];
}
return list;
} catch (IOException e) {
throw Message.convert(e);
}
// try {
// File[] files = new File(path).listFiles();
// if(files == null) {
// return new String[0];
// }
// String[] list = new String[files.length];
// for(int i=0; i<files.length; i++) {
// list[i] = files[i].getCanonicalPath();
// }
// return list;
// } catch (IOException e) {
// throw Message.convert(e);
// }
}
public static boolean isDirectory(String fileName) {
......
......@@ -5,6 +5,7 @@
package org.h2.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
......@@ -13,6 +14,8 @@ import java.sql.Statement;
import javax.sql.XAConnection;
//#endif
import org.h2.message.Message;
public class JdbcUtils {
public static void closeSilently(Statement stat) {
......@@ -65,4 +68,15 @@ public class JdbcUtils {
}
//#endif
public static Connection getConnection(String driver, String url, String user, String password) throws SQLException {
if(!StringUtils.isNullOrEmpty(driver)) {
try {
ClassUtils.loadClass(driver);
} catch (ClassNotFoundException e) {
throw Message.getSQLException(Message.CLASS_NOT_FOUND_1, new String[]{driver}, e);
}
}
return DriverManager.getConnection(url, user, password);
}
}
......@@ -601,4 +601,8 @@ public class StringUtils {
return buff.append('\"').toString();
}
public static boolean isNullOrEmpty(String s) {
return s == null || s.length() == 0;
}
}
......@@ -175,11 +175,13 @@ public class DataType {
createString(true),
new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"}
);
DataType dataType = new DataType();
dataType.prefix = "(";
dataType.suffix = "')";
add(Value.ARRAY, Types.ARRAY, "Array",
createString(false),
dataType,
new String[]{"ARRAY"}
);
// TODO data types: try to support other types as well (longvarchar for odbc/access,...) - maybe map them to regular types?
}
......
......@@ -262,7 +262,13 @@ public class Transfer {
}
writeLong(length);
Reader reader = v.getReader();
Writer writer = new OutputStreamWriter(out, Constants.UTF8);
// below, writer.flush needs to be called to ensure the buffer is written
// but, this will also flush the output stream, and this slows things down
// so construct an output stream that will ignore this chained flush call
java.io.OutputStream out2 = new java.io.FilterOutputStream(out) {
public void flush() {}
};
Writer writer = new OutputStreamWriter(out2, Constants.UTF8);
long written = IOUtils.copyAndCloseInput(reader, writer);
if(Constants.CHECK && written != length) {
throw Message.getInternalError("length:" + length + " written:" + written);
......
......@@ -194,9 +194,9 @@ public class ValueLob extends Value {
return name;
}
private static int getNewObjectId(String path) throws SQLException {
int objectId;
objectId = 0;
private int getNewObjectId(DataHandler handler) throws SQLException {
String path = handler.getDatabasePath();
int objectId = 0;
while(true) {
String dir = getFileNamePrefix(path, objectId);
String[] list = FileUtils.listFiles(dir);
......@@ -204,7 +204,7 @@ public class ValueLob extends Value {
boolean[] used = new boolean[Constants.LOB_FILES_PER_DIRECTORY];
for(int i=0; i<list.length; i++) {
String name = list[i];
if(name.endsWith(".db")) {
if(name.endsWith(Constants.SUFFIX_DB_FILE)) {
name = name.substring(name.lastIndexOf(File.separatorChar) + 1);
String n = name.substring(0, name.indexOf('.'));
int id;
......@@ -282,7 +282,7 @@ public class ValueLob extends Value {
this.compression = compressionAlgorithm != null;
synchronized(handler) {
if(Constants.LOB_FILES_IN_DIRECTORIES) {
objectId = getNewObjectId(handler.getDatabasePath());
objectId = getNewObjectId(handler);
fileName = getFileNamePrefix(handler.getDatabasePath(), objectId) + ".temp.db";
} else {
objectId = handler.allocateObjectId(false, true);
......@@ -377,7 +377,7 @@ public class ValueLob extends Value {
if(linked) {
ValueLob copy = ValueLob.copy(this);
if(Constants.LOB_FILES_IN_DIRECTORIES) {
copy.objectId = getNewObjectId(handler.getDatabasePath());
copy.objectId = getNewObjectId(handler);
} else {
copy.objectId = handler.allocateObjectId(false, true);
}
......
......@@ -8,7 +8,7 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import org.h2.tools.Backup;
import org.h2.tools.Script;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.RunScript;
......@@ -30,9 +30,9 @@ public class Compact {
public static void compact(String dir, String dbName, String user, String password) throws Exception {
String url = "jdbc:h2:" + dir + "/" + dbName;
String script = "data/test.sql";
Backup.execute(url, user, password, script);
String file = "data/test.sql";
Script.execute(url, user, password, file);
DeleteDbFiles.execute(dir, dbName, true);
RunScript.execute(url, user, password, script, null, false);
RunScript.execute(url, user, password, file, null, false);
}
}
......@@ -10,6 +10,8 @@ import java.util.Properties;
import org.h2.server.TcpServer;
import org.h2.test.jdbc.*;
import org.h2.test.jdbc.xa.TestXA;
import org.h2.test.cases.TestBlobDir;
import org.h2.test.cases.TestCalendar;
import org.h2.test.db.*;
import org.h2.test.server.TestNestedLoop;
import org.h2.test.synth.TestBtreeIndex;
......@@ -87,63 +89,78 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
long time = System.currentTimeMillis();
TestAll test = new TestAll();
test.printSystem();
/*
drop table people;
drop table cars;
create table people (family varchar(1) not null, person
varchar(1) not null);
create table cars (family varchar(1) not null, car
varchar(1) not null);
insert into people values(1, 1);
insert into people values(2, 1);
insert into people values(2, 2);
insert into people values(3, 1);
insert into people values(5, 1);
insert into cars values(2, 1);
insert into cars values(2, 2);
insert into cars values(3, 1);
insert into cars values(3, 2);
insert into cars values(3, 3);
insert into cars values(4, 1);
select family, (select count(car) from cars where cars.family = people.family) as x
from people group by family;
*/
// link_table_update.patch.txt
// runscript and script: use 'script' parameter as before
// autocomplete: scroll up on new list
// doc array
// www.inventec.ch/chdh
// www.source-code.biz
// pilar sms
/*
CREATE TABLE T(DATA VARCHAR(10), MYINT INTEGER);
INSERT INTO T (DATA,MYINT) VALUES (null,1);
@META select ifnull(t.data, 0), myint from t;
-- 1 should be VARCHAR
select ifnull(t.data, 0), myint from t;
drop table t;
*/
/*
/*
Pavel Ganelin
Integrate patches www.dullesopen.com/software/h2-database-03-04-07-mod.src.zip
*/
/*
drop all objects;
create table parent(id int primary key, parent int);
insert into parent values(1, null), (2, 1), (3, 1);
create view recursive test_view(id, parent) as
select id, parent from parent
with test_view(id, parent) as
select id, parent from parent where parent is null
union all
select parent.id, parent.parent from test_view, parent
where parent.parent = test_view.id
select * from test_view;
with test_view(id, parent) as
select id, parent from parent where id = 2
union all
select parent.id, parent.parent from test_view, parent
where parent.id = test_view.parent;
where parent.parent = test_view.id
select * from test_view;
drop view test_view;
drop table parent;
*/
// Under certain conditions in client/server mode, obtaining text values
// of CLOBs becomes very slow (e.g., it could take a minimum of 200 ms
// instead of 2 ms). If I change the column type to VARCHAR, the
// performance is very fast again.
//
// Between 2006-12-03 and 2006-12-17, you remarked: "Very large BLOB and
// CLOB data can now be used with the server and the cluster mode. The
// objects will temporarily be buffered on the client side if they are
// larger than some size (currently 64 KB)."
//
// I think the issue is now in Transfer.java, as below:
// 272: writer.flush();
// 273: writeInt(LOB_MAGIC);
//
// I believe the flush() can cause a TCP performance problem in certain
// circumstances. The reason is that when flush() occurs, the blob
// packets are flushed over the network, then a TCP ACK must be exchanged
// from client to server before the separate LOB_MAGIC packet can be sent
// from the server to client. This can cause enough latency to degrade
// application performance noticeably.
//
// I do not know of a good way to solve this, since you cannot avoid
// calling writer.flush(). One "workaround" for some applications is to
// convert CLOB to VARCHAR. However, I am currently trying something
// different. It works so far for me. My "patch" is:
//
// Transfer.java, at line 267:
// java.io.OutputStream out2 = new java.io.FilterOutputStream(out) {
// public void flush() {} };
// Writer writer = new OutputStreamWriter(out2, Constants.UTF8);
//
// James.
@LOOP 10 with test_view(id, parent) as
select id, parent from parent where id = ?
union all
select parent.id, parent.parent from test_view, parent
where parent.parent = test_view.id
select * from test_view;
drop table parent;
*/
/*
create local temporary table abc(id varchar) on commit drop;
insert into abc select * from dual;
create local temporary table abc(id varchar) on commit drop;
insert into abc select * from dual where 1=0;
create local temporary table abc(id varchar) on commit drop;
insert into abc select * from dual;
drop table abc;
*/
// TODO: fix Hibernate dialect bug / Bordea Felix (lost email)
......@@ -169,6 +186,102 @@ drop table parent;
// EXPLAIN SELECT * FROM test WHERE id between 2 and 3 AND flag=true;
// EXPLAIN SELECT * FROM test WHERE id=2 AND flag;
/*
TODO: get FunctionAlias.java from mail
Here are the proposed changes to support function overload for variable number of arguments
Example/Test Case
public class OverloadFunction extends TestCase {
public void testOverload() throws Exception {
Class.forName("org.h2.Driver");
Connection ca = DriverManager.getConnection("jdbc:h2:mem:");
Statement sa = ca.createStatement();
sa.execute("CREATE ALIAS foo FOR \"" + this.getClass().getName() + ".foo\"");
ResultSet rs1 = sa.executeQuery("SELECT foo('a',2)");
rs1.next();
assertEquals(2.0, rs1.getDouble(1));
ResultSet rs2 = sa.executeQuery("SELECT foo('a',2,3,4)");
rs2.next();
assertEquals(9.0, rs2.getDouble(1));
try {
ResultSet rs = sa.executeQuery("SELECT foo()");
fail();
} catch (SQLException e) {
e.printStackTrace();
}
try {
ResultSet rs = sa.executeQuery("SELECT foo('a')");
fail();
} catch (SQLException e) {
e.printStackTrace();
}
try {
ResultSet rs = sa.executeQuery("SELECT foo(2,'a')");
fail();
} catch (SQLException e) {
e.printStackTrace();
}
try {
ResultSet rs = sa.executeQuery("SELECT foo('a',2,3)");
fail();
} catch (SQLException e) {
e.printStackTrace();
}
try {
ResultSet rs = sa.executeQuery("SELECT foo('a',2,3,4,5)");
fail();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static double foo(String s, int i) {
return i;
}
public static double foo(String s, int i, double d1, double d2) {
return i + d1 + d2;
}
}
Changes in the Parser
CODE
private JavaFunction readJavaFunction(String name) throws SQLException {
FunctionAlias functionAlias = database.findFunctionAlias(name);
if (functionAlias == null) {
// TODO compatibility: maybe support 'on the fly java functions' as HSQLDB ( CALL "java.lang.Math.sqrt"(2.0) )
throw Message.getSQLException(Message.FUNCTION_NOT_FOUND_1, name);
}
int paramCount = functionAlias.getParameterCount();
int max = functionAlias.getMaxParameterCount();
ObjectArray list = new ObjectArray(paramCount);
do {
if (functionAlias.isAcceptableParameterCount(list.size())) {
if (readIf(")"))
break;
}
if (list.size() == max) {
read(")"); // force syntax error for extra argument
break;
}
if (list.size() > 0) {
read(",");
}
Expression e = readExpression();
list.add(e);
} while (true);
Expression[] args = new Expression[list.size()];
for (int i = 0; i < args.length; i++) {
args[i] = (Expression) list.get(i);
}
JavaFunction func = new JavaFunction(functionAlias, args);
return func;
}
I also attached FunctionAlias.java file
Pavel
*/
// h2
// update FOO set a = dateadd('second', 4320000, a);
// ms sql server
......@@ -199,6 +312,8 @@ drop table parent;
// -- Oracle, Derby: 10, 11
// -- PostgreSQL, H2, HSQLDB: 1, 2
// auto-upgrade application:
// check if new version is available
// (option: digital signature)
......@@ -428,24 +543,24 @@ drop table parent;
}
void testUnit() {
new TestBitField().runTest(this);
new TestCompress().runTest(this);
new TestDataPage().runTest(this);
new TestExit().runTest(this);
new TestFileLock().runTest(this);
new TestIntArray().runTest(this);
new TestIntIntHashMap().runTest(this);
new TestOverflow().runTest(this);
new TestPattern().runTest(this);
new TestReader().runTest(this);
new TestSampleApps().runTest(this);
new TestScriptReader().runTest(this);
new TestSecurity().runTest(this);
new TestStreams().runTest(this);
new TestStringCache().runTest(this);
new TestStringUtils().runTest(this);
// new TestBitField().runTest(this);
// new TestCompress().runTest(this);
// new TestDataPage().runTest(this);
// new TestExit().runTest(this);
// new TestFileLock().runTest(this);
// new TestIntArray().runTest(this);
// new TestIntIntHashMap().runTest(this);
// new TestOverflow().runTest(this);
// new TestPattern().runTest(this);
// new TestReader().runTest(this);
// new TestSampleApps().runTest(this);
// new TestScriptReader().runTest(this);
// new TestSecurity().runTest(this);
// new TestStreams().runTest(this);
// new TestStringCache().runTest(this);
// new TestStringUtils().runTest(this);
new TestTools().runTest(this);
new TestValueHashMap().runTest(this);
// new TestValueHashMap().runTest(this);
}
void testDatabase() throws Exception {
......@@ -453,59 +568,60 @@ drop table parent;
beforeTest();
// db
new TestScriptSimple().runTest(this);
new TestScript().runTest(this);
new TestAutoRecompile().runTest(this);
new TestBatchUpdates().runTest(this);
new TestBigDb().runTest(this);
new TestBigResult().runTest(this);
new TestCache().runTest(this);
new TestCases().runTest(this);
new TestCheckpoint().runTest(this);
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestFunctions().runTest(this);
new TestIndex().runTest(this);
new TestLinkedTable().runTest(this);
new TestListener().runTest(this);
new TestLob().runTest(this);
new TestLogFile().runTest(this);
new TestMemoryUsage().runTest(this);
new TestMultiConn().runTest(this);
new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this);
new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this);
new TestPowerOff().runTest(this);
new TestReadOnly().runTest(this);
new TestRights().runTest(this);
new TestRunscript().runTest(this);
new TestSQLInjection().runTest(this);
new TestSequence().runTest(this);
new TestSpaceReuse().runTest(this);
new TestSpeed().runTest(this);
new TestTempTables().runTest(this);
new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
// server
new TestNestedLoop().runTest(this);
// jdbc
new TestCancel().runTest(this);
new TestDataSource().runTest(this);
new TestManyJdbcObjects().runTest(this);
new TestMetaData().runTest(this);
new TestNativeSQL().runTest(this);
new TestPreparedStatement().runTest(this);
new TestResultSet().runTest(this);
new TestStatement().runTest(this);
new TestTransactionIsolation().runTest(this);
new TestUpdatableResultSet().runTest(this);
new TestXA().runTest(this);
new TestZloty().runTest(this);
// new TestScriptSimple().runTest(this);
// new TestScript().runTest(this);
// new TestAutoRecompile().runTest(this);
// new TestBackup().runTest(this);
// new TestBatchUpdates().runTest(this);
// new TestBigDb().runTest(this);
// new TestBigResult().runTest(this);
// new TestCache().runTest(this);
// new TestCases().runTest(this);
// new TestCheckpoint().runTest(this);
// new TestCluster().runTest(this);
// new TestCompatibility().runTest(this);
// new TestCsv().runTest(this);
// new TestFunctions().runTest(this);
// new TestIndex().runTest(this);
// new TestLinkedTable().runTest(this);
// new TestListener().runTest(this);
// new TestLob().runTest(this);
// new TestLogFile().runTest(this);
// new TestMemoryUsage().runTest(this);
// new TestMultiConn().runTest(this);
// new TestMultiDimension().runTest(this);
// new TestMultiThread().runTest(this);
// new TestOpenClose().runTest(this);
// new TestOptimizations().runTest(this);
// new TestPowerOff().runTest(this);
// new TestReadOnly().runTest(this);
// new TestRights().runTest(this);
// new TestRunscript().runTest(this);
// new TestSQLInjection().runTest(this);
// new TestSequence().runTest(this);
// new TestSpaceReuse().runTest(this);
// new TestSpeed().runTest(this);
// new TestTempTables().runTest(this);
// new TestTransaction().runTest(this);
// new TestTriggersConstraints().runTest(this);
// new TestTwoPhaseCommit().runTest(this);
//
// // server
// new TestNestedLoop().runTest(this);
//
// // jdbc
// new TestCancel().runTest(this);
// new TestDataSource().runTest(this);
// new TestManyJdbcObjects().runTest(this);
// new TestMetaData().runTest(this);
// new TestNativeSQL().runTest(this);
// new TestPreparedStatement().runTest(this);
// new TestResultSet().runTest(this);
// new TestStatement().runTest(this);
// new TestTransactionIsolation().runTest(this);
// new TestUpdatableResultSet().runTest(this);
// new TestXA().runTest(this);
// new TestZloty().runTest(this);
afterTest();
}
......
......@@ -16,6 +16,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Properties;
import org.h2.jdbc.JdbcConnection;
......@@ -561,4 +562,24 @@ public abstract class TestBase {
}
}
protected void compareDatabases(Statement stat1, Statement stat2) throws Exception {
ResultSet rs1 = stat1.executeQuery("SCRIPT NOPASSWORDS");
ResultSet rs2 = stat2.executeQuery("SCRIPT NOPASSWORDS");
ArrayList list1 = new ArrayList();
ArrayList list2 = new ArrayList();
while(rs1.next()) {
check(rs2.next());
list1.add(rs1.getString(1));
list2.add(rs2.getString(1));
}
for(int i=0; i<list1.size(); i++) {
String s = (String)list1.get(i);
if(!list2.remove(s)) {
error("not found: " + s);
}
}
check(list2.size(), 0);
checkFalse(rs2.next());
}
}
--- special grammar and test cases ---------------------------------------------------------------------------------------------
select (1, 2);
> 1, 2
> ------
> (1, 2)
> rows: 1
select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by id;
> ID NAME
> -- -----
> 1 Hello
> 2 World
> rows (ordered): 2
create table array_test(x array);
> ok
insert into array_test values((1, 2, 3)), ((2, 3, 4));
> update count: 2
select * from array_test where x = (1, 2, 3);
> X
> ---------
> (1, 2, 3)
> rows: 1
drop table array_test;
> ok
select * from (select 1), (select 2);
> 1 2
> - -
> 1 2
> rows: 1
CREATE TABLE TEST(A VARCHAR, B VARCHAR, C VARCHAR AS LOWER(A));
> ok
ALTER TABLE TEST DROP COLUMN B;
> ok
DROP TABLE TEST;
> ok
create table t1(c1 int, c2 int);
> ok
......@@ -92,14 +135,14 @@ CREATE TABLE test (family_name VARCHAR_IGNORECASE(63) NOT NULL);
INSERT INTO test VALUES('Smith'), ('de Smith'), ('el Smith'), ('von Smith');
> update count: 4
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
> FAMILY_NAME
> -----------
> Smith
> de Smith
> rows: 2
SELECT * FROM test WHERE family_name BETWEEN 'D' AND 'T';
SELECT * FROM test WHERE family_name BETWEEN 'D' AND 'T';
> FAMILY_NAME
> -----------
> Smith
......@@ -110,7 +153,7 @@ SELECT * FROM test WHERE family_name BETWEEN 'D' AND 'T';
CREATE INDEX family_name ON test(family_name);
> ok
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
SELECT * FROM test WHERE family_name IN ('de Smith', 'Smith');
> FAMILY_NAME
> -----------
> Smith
......@@ -7430,9 +7473,9 @@ select ifnull(null, '1') x1, ifnull(null, null) xn, ifnull('a', 'b') xa from tes
> rows: 1
select casewhen(null, '1', '2') xn, casewhen(1>0, 'n', 'y') xy, casewhen(0<1, 'a', 'b') xa from test;
> XN XY XA
> ---- -- --
> null n a
> XN XY XA
> -- -- --
> 2 n a
> rows: 1
select x, case when x=0 then 'zero' else 'not zero' end y from system_range(0, 2);
......
create table test(id int primary key check id>1);
drop table test;
create table table1(f1 int not null primary key);
create table table2(f2 int not null references table1(f1) on delete cascade);
drop table table2;
drop table table1;
create table table1(f1 int not null primary key);
create table table2(f2 int not null primary key references table1(f1));
drop table table1;
drop table table2;
select case when 1=null then 1 else 2 end;
> 2;
select case (1) when 1 then 1 else 2 end;
> 1;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论