Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
a5472db7
提交
a5472db7
authored
12月 21, 2018
作者:
Evgenij Ryazanov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add explicit table query
上级
c6933120
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
238 行增加
和
63 行删除
+238
-63
help.csv
h2/src/docsrc/help/help.csv
+13
-0
advanced.html
h2/src/docsrc/html/advanced.html
+2
-2
Parser.java
h2/src/main/org/h2/command/Parser.java
+107
-38
FullText.java
h2/src/main/org/h2/fulltext/FullText.java
+5
-5
FullTextLucene.java
h2/src/main/org/h2/fulltext/FullTextLucene.java
+5
-5
JdbcDatabaseMetaData.java
h2/src/main/org/h2/jdbc/JdbcDatabaseMetaData.java
+2
-2
LobStorageBackend.java
h2/src/main/org/h2/store/LobStorageBackend.java
+5
-5
ParserUtil.java
h2/src/main/org/h2/util/ParserUtil.java
+9
-2
TestScript.java
h2/src/test/org/h2/test/scripts/TestScript.java
+1
-1
table.sql
h2/src/test/org/h2/test/scripts/dml/table.sql
+55
-0
with.sql
h2/src/test/org/h2/test/scripts/dml/with.sql
+19
-1
table.sql
h2/src/test/org/h2/test/scripts/functions/system/table.sql
+15
-2
没有找到文件。
h2/src/docsrc/help/help.csv
浏览文件 @
a5472db7
...
@@ -238,6 +238,19 @@ Lists the schemas, tables, or the columns of a table.
...
@@ -238,6 +238,19 @@ Lists the schemas, tables, or the columns of a table.
SHOW TABLES
SHOW TABLES
"
"
"Commands (DML)","Explicit TABLE","
TABLE [schemaName.]tableName
","
Selects data from a table.
This command is an equivalent to SELECT * FROM TABLE.
FROM, WHERE, GROUP BY, HAVING, and WINDOW clauses from the SELECT command are not allowed.
","
TABLE TEST;
TABLE TEST ORDER BY ID FETCH FIRST ROW ONLY;
"
"Commands (DML)","WITH","
"Commands (DML)","WITH","
WITH [ RECURSIVE ] { name [( columnName [,...] )] AS ( select ) [,...] }
WITH [ RECURSIVE ] { name [( columnName [,...] )] AS ( select ) [,...] }
{ select | insert | update | merge | delete | createTable }
{ select | insert | update | merge | delete | createTable }
...
...
h2/src/docsrc/html/advanced.html
浏览文件 @
a5472db7
...
@@ -481,8 +481,8 @@ EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING,
...
@@ -481,8 +481,8 @@ EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING,
IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
SYSTIME, SYSTIMESTAMP, T
ODAY, TOP, TRUE, UNION, UNIQUE, VALUES
,
SYSTIME, SYSTIMESTAMP, T
ABLE, TODAY, TOP, TRUE, UNION, UNIQUE
,
WHERE, WINDOW, WITH
VALUES,
WHERE, WINDOW, WITH
</code>
</code>
</p><p>
</p><p>
Certain words of this list are keywords because they are functions that can be used without '()',
Certain words of this list are keywords because they are functions that can be used without '()',
...
...
h2/src/main/org/h2/command/Parser.java
浏览文件 @
a5472db7
...
@@ -52,6 +52,7 @@ import static org.h2.util.ParserUtil.PRIMARY;
...
@@ -52,6 +52,7 @@ import static org.h2.util.ParserUtil.PRIMARY;
import
static
org
.
h2
.
util
.
ParserUtil
.
ROW
;
import
static
org
.
h2
.
util
.
ParserUtil
.
ROW
;
import
static
org
.
h2
.
util
.
ParserUtil
.
ROWNUM
;
import
static
org
.
h2
.
util
.
ParserUtil
.
ROWNUM
;
import
static
org
.
h2
.
util
.
ParserUtil
.
SELECT
;
import
static
org
.
h2
.
util
.
ParserUtil
.
SELECT
;
import
static
org
.
h2
.
util
.
ParserUtil
.
TABLE
;
import
static
org
.
h2
.
util
.
ParserUtil
.
TRUE
;
import
static
org
.
h2
.
util
.
ParserUtil
.
TRUE
;
import
static
org
.
h2
.
util
.
ParserUtil
.
UNION
;
import
static
org
.
h2
.
util
.
ParserUtil
.
UNION
;
import
static
org
.
h2
.
util
.
ParserUtil
.
UNIQUE
;
import
static
org
.
h2
.
util
.
ParserUtil
.
UNIQUE
;
...
@@ -251,7 +252,7 @@ import org.h2.value.ValueTimestampTimeZone;
...
@@ -251,7 +252,7 @@ import org.h2.value.ValueTimestampTimeZone;
public
class
Parser
{
public
class
Parser
{
private
static
final
String
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS
=
private
static
final
String
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS
=
"WITH statement supports only SELECT, CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements"
;
"WITH statement supports only SELECT,
TABLE,
CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements"
;
// used during the tokenizer phase
// used during the tokenizer phase
private
static
final
int
CHAR_END
=
1
,
CHAR_VALUE
=
2
,
CHAR_QUOTED
=
3
;
private
static
final
int
CHAR_END
=
1
,
CHAR_VALUE
=
2
,
CHAR_QUOTED
=
3
;
...
@@ -510,6 +511,8 @@ public class Parser {
...
@@ -510,6 +511,8 @@ public class Parser {
"ROWNUM"
,
"ROWNUM"
,
// SELECT
// SELECT
"SELECT"
,
"SELECT"
,
// TABLE
"TABLE"
,
// TRUE
// TRUE
"TRUE"
,
"TRUE"
,
// UNION
// UNION
...
@@ -745,6 +748,7 @@ public class Parser {
...
@@ -745,6 +748,7 @@ public class Parser {
case
OPEN_PAREN:
case
OPEN_PAREN:
case
FROM:
case
FROM:
case
SELECT:
case
SELECT:
case
TABLE:
c
=
parseSelect
();
c
=
parseSelect
();
break
;
break
;
case
VALUES:
case
VALUES:
...
@@ -944,7 +948,7 @@ public class Parser {
...
@@ -944,7 +948,7 @@ public class Parser {
private
Prepared
parseAnalyze
()
{
private
Prepared
parseAnalyze
()
{
Analyze
command
=
new
Analyze
(
session
);
Analyze
command
=
new
Analyze
(
session
);
if
(
readIf
(
"TABLE"
))
{
if
(
readIf
(
TABLE
))
{
Table
table
=
readTableOrView
();
Table
table
=
readTableOrView
();
command
.
setTable
(
table
);
command
.
setTable
(
table
);
}
}
...
@@ -1436,7 +1440,20 @@ public class Parser {
...
@@ -1436,7 +1440,20 @@ public class Parser {
// need to read ahead, it could be a nested union:
// need to read ahead, it could be a nested union:
// ((select 1) union (select 1))
// ((select 1) union (select 1))
}
}
boolean
select
=
isToken
(
SELECT
)
||
isToken
(
FROM
)
||
isToken
(
WITH
);
boolean
select
;
switch
(
currentTokenType
)
{
case
FROM:
case
SELECT:
case
WITH:
select
=
true
;
break
;
case
TABLE:
read
();
select
=
!
readIf
(
OPEN_PAREN
);
break
;
default
:
select
=
false
;
}
parseIndex
=
start
;
parseIndex
=
start
;
read
();
read
();
return
select
;
return
select
;
...
@@ -1761,6 +1778,9 @@ public class Parser {
...
@@ -1761,6 +1778,9 @@ public class Parser {
}
}
}
else
if
(
readIf
(
VALUES
))
{
}
else
if
(
readIf
(
VALUES
))
{
table
=
parseValuesTable
(
0
).
getTable
();
table
=
parseValuesTable
(
0
).
getTable
();
}
else
if
(
readIf
(
TABLE
))
{
read
(
OPEN_PAREN
);
table
=
readTableFunction
(
"TABLE"
,
null
,
database
.
getSchema
(
Constants
.
SCHEMA_MAIN
));
}
else
{
}
else
{
String
tableName
=
readIdentifierWithSchema
(
null
);
String
tableName
=
readIdentifierWithSchema
(
null
);
Schema
schema
;
Schema
schema
;
...
@@ -1801,15 +1821,7 @@ public class Parser {
...
@@ -1801,15 +1821,7 @@ public class Parser {
table
=
new
RangeTable
(
mainSchema
,
min
,
max
,
false
);
table
=
new
RangeTable
(
mainSchema
,
min
,
max
,
false
);
}
}
}
else
{
}
else
{
Expression
expr
=
readFunction
(
schema
,
tableName
);
table
=
readTableFunction
(
tableName
,
schema
,
mainSchema
);
if
(!(
expr
instanceof
FunctionCall
))
{
throw
getSyntaxError
();
}
FunctionCall
call
=
(
FunctionCall
)
expr
;
if
(!
call
.
isDeterministic
())
{
recompileAlways
=
true
;
}
table
=
new
FunctionTable
(
mainSchema
,
session
,
expr
,
call
);
}
}
}
else
{
}
else
{
table
=
readTableOrView
(
tableName
);
table
=
readTableOrView
(
tableName
);
...
@@ -1853,6 +1865,18 @@ public class Parser {
...
@@ -1853,6 +1865,18 @@ public class Parser {
return
filter
;
return
filter
;
}
}
private
Table
readTableFunction
(
String
tableName
,
Schema
schema
,
Schema
mainSchema
)
{
Expression
expr
=
readFunction
(
schema
,
tableName
);
if
(!(
expr
instanceof
FunctionCall
))
{
throw
getSyntaxError
();
}
FunctionCall
call
=
(
FunctionCall
)
expr
;
if
(!
call
.
isDeterministic
())
{
recompileAlways
=
true
;
}
return
new
FunctionTable
(
mainSchema
,
session
,
expr
,
call
);
}
private
IndexHints
parseIndexHints
(
Table
table
)
{
private
IndexHints
parseIndexHints
(
Table
table
)
{
read
(
OPEN_PAREN
);
read
(
OPEN_PAREN
);
LinkedHashSet
<
String
>
indexNames
=
new
LinkedHashSet
<>();
LinkedHashSet
<
String
>
indexNames
=
new
LinkedHashSet
<>();
...
@@ -1919,7 +1943,7 @@ public class Parser {
...
@@ -1919,7 +1943,7 @@ public class Parser {
}
}
private
Prepared
parseTruncate
()
{
private
Prepared
parseTruncate
()
{
read
(
"TABLE"
);
read
(
TABLE
);
Table
table
=
readTableOrView
();
Table
table
=
readTableOrView
();
boolean
restart
;
boolean
restart
;
if
(
readIf
(
"CONTINUE"
))
{
if
(
readIf
(
"CONTINUE"
))
{
...
@@ -1949,7 +1973,7 @@ public class Parser {
...
@@ -1949,7 +1973,7 @@ public class Parser {
int
type
=
0
;
int
type
=
0
;
read
(
ON
);
read
(
ON
);
boolean
column
=
false
;
boolean
column
=
false
;
if
(
readIf
(
"TABLE"
)
||
readIf
(
"VIEW"
))
{
if
(
readIf
(
TABLE
)
||
readIf
(
"VIEW"
))
{
type
=
DbObject
.
TABLE_OR_VIEW
;
type
=
DbObject
.
TABLE_OR_VIEW
;
}
else
if
(
readIf
(
"COLUMN"
))
{
}
else
if
(
readIf
(
"COLUMN"
))
{
column
=
true
;
column
=
true
;
...
@@ -2016,7 +2040,7 @@ public class Parser {
...
@@ -2016,7 +2040,7 @@ public class Parser {
}
}
private
Prepared
parseDrop
()
{
private
Prepared
parseDrop
()
{
if
(
readIf
(
"TABLE"
))
{
if
(
readIf
(
TABLE
))
{
boolean
ifExists
=
readIfExists
(
false
);
boolean
ifExists
=
readIfExists
(
false
);
String
tableName
=
readIdentifierWithSchema
();
String
tableName
=
readIdentifierWithSchema
();
DropTable
command
=
new
DropTable
(
session
,
getSchema
());
DropTable
command
=
new
DropTable
(
session
,
getSchema
());
...
@@ -2316,20 +2340,28 @@ public class Parser {
...
@@ -2316,20 +2340,28 @@ public class Parser {
readIf
(
FOR
);
readIf
(
FOR
);
}
}
}
}
if
(
isToken
(
SELECT
)
||
isToken
(
FROM
)
||
isToken
(
OPEN_PAREN
)
||
isToken
(
WITH
))
{
switch
(
currentTokenType
)
{
case
FROM:
case
SELECT:
case
TABLE:
case
WITH:
case
OPEN_PAREN:
Query
query
=
parseSelect
();
Query
query
=
parseSelect
();
query
.
setNeverLazy
(
true
);
query
.
setNeverLazy
(
true
);
command
.
setCommand
(
query
);
command
.
setCommand
(
query
);
}
else
if
(
readIf
(
"DELETE"
))
{
break
;
command
.
setCommand
(
parseDelete
());
default
:
}
else
if
(
readIf
(
"UPDATE"
))
{
if
(
readIf
(
"DELETE"
))
{
command
.
setCommand
(
parseUpdate
());
command
.
setCommand
(
parseDelete
());
}
else
if
(
readIf
(
"INSERT"
))
{
}
else
if
(
readIf
(
"UPDATE"
))
{
command
.
setCommand
(
parseInsert
());
command
.
setCommand
(
parseUpdate
());
}
else
if
(
readIf
(
"MERGE"
))
{
}
else
if
(
readIf
(
"INSERT"
))
{
command
.
setCommand
(
parseMerge
());
command
.
setCommand
(
parseInsert
());
}
else
{
}
else
if
(
readIf
(
"MERGE"
))
{
throw
getSyntaxError
();
command
.
setCommand
(
parseMerge
());
}
else
{
throw
getSyntaxError
();
}
}
}
return
command
;
return
command
;
}
}
...
@@ -2647,6 +2679,18 @@ public class Parser {
...
@@ -2647,6 +2679,18 @@ public class Parser {
fromFirst
=
false
;
fromFirst
=
false
;
}
else
if
(
readIf
(
FROM
))
{
}
else
if
(
readIf
(
FROM
))
{
fromFirst
=
true
;
fromFirst
=
true
;
}
else
if
(
readIf
(
TABLE
))
{
int
start
=
lastParseIndex
;
Table
table
=
readTableOrView
();
Select
command
=
new
Select
(
session
);
TableFilter
filter
=
new
TableFilter
(
session
,
table
,
null
,
rightsChecked
,
command
,
orderInFrom
++,
null
);
command
.
addTableFilter
(
filter
,
true
);
ArrayList
<
Expression
>
expressions
=
new
ArrayList
<>();
expressions
.
add
(
new
Wildcard
(
null
,
null
));
command
.
setExpressions
(
expressions
);
setSQL
(
command
,
"SELECT"
,
start
);
return
command
;
}
else
{
}
else
{
throw
getSyntaxError
();
throw
getSyntaxError
();
}
}
...
@@ -3760,6 +3804,17 @@ public class Parser {
...
@@ -3760,6 +3804,17 @@ public class Parser {
case
WITH:
case
WITH:
r
=
new
Subquery
(
parseSelect
());
r
=
new
Subquery
(
parseSelect
());
break
;
break
;
case
TABLE:
int
index
=
lastParseIndex
;
read
();
if
(
readIf
(
OPEN_PAREN
))
{
r
=
readFunction
(
null
,
"TABLE"
);
}
else
{
parseIndex
=
index
;
read
();
r
=
new
Subquery
(
parseSelect
());
}
break
;
case
IDENTIFIER:
case
IDENTIFIER:
String
name
=
currentToken
;
String
name
=
currentToken
;
if
(
currentTokenQuoted
)
{
if
(
currentTokenQuoted
)
{
...
@@ -5592,22 +5647,22 @@ public class Parser {
...
@@ -5592,22 +5647,22 @@ public class Parser {
if
(
readIf
(
"LINKED"
))
{
if
(
readIf
(
"LINKED"
))
{
return
parseCreateLinkedTable
(
true
,
false
,
force
);
return
parseCreateLinkedTable
(
true
,
false
,
force
);
}
}
read
(
"TABLE"
);
read
(
TABLE
);
return
parseCreateTable
(
true
,
false
,
cached
);
return
parseCreateTable
(
true
,
false
,
cached
);
}
else
if
(
readIf
(
"GLOBAL"
))
{
}
else
if
(
readIf
(
"GLOBAL"
))
{
read
(
"TEMPORARY"
);
read
(
"TEMPORARY"
);
if
(
readIf
(
"LINKED"
))
{
if
(
readIf
(
"LINKED"
))
{
return
parseCreateLinkedTable
(
true
,
true
,
force
);
return
parseCreateLinkedTable
(
true
,
true
,
force
);
}
}
read
(
"TABLE"
);
read
(
TABLE
);
return
parseCreateTable
(
true
,
true
,
cached
);
return
parseCreateTable
(
true
,
true
,
cached
);
}
else
if
(
readIf
(
"TEMP"
)
||
readIf
(
"TEMPORARY"
))
{
}
else
if
(
readIf
(
"TEMP"
)
||
readIf
(
"TEMPORARY"
))
{
if
(
readIf
(
"LINKED"
))
{
if
(
readIf
(
"LINKED"
))
{
return
parseCreateLinkedTable
(
true
,
true
,
force
);
return
parseCreateLinkedTable
(
true
,
true
,
force
);
}
}
read
(
"TABLE"
);
read
(
TABLE
);
return
parseCreateTable
(
true
,
true
,
cached
);
return
parseCreateTable
(
true
,
true
,
cached
);
}
else
if
(
readIf
(
"TABLE"
))
{
}
else
if
(
readIf
(
TABLE
))
{
if
(!
cached
&&
!
memory
)
{
if
(!
cached
&&
!
memory
)
{
cached
=
database
.
getDefaultTableType
()
==
Table
.
TYPE_CACHED
;
cached
=
database
.
getDefaultTableType
()
==
Table
.
TYPE_CACHED
;
}
}
...
@@ -6110,10 +6165,17 @@ public class Parser {
...
@@ -6110,10 +6165,17 @@ public class Parser {
parentheses
++;
parentheses
++;
}
}
if
(
isToken
(
SELECT
))
{
if
(
isToken
(
SELECT
))
{
Query
query
=
parseSelectUnion
();
p
=
parseWithQuery
();
query
.
setPrepareAlways
(
true
);
}
else
if
(
isToken
(
TABLE
))
{
query
.
setNeverLazy
(
true
);
int
index
=
lastParseIndex
;
p
=
query
;
read
();
if
(!
isToken
(
OPEN_PAREN
))
{
parseIndex
=
index
;
read
();
p
=
parseWithQuery
();
}
else
{
throw
DbException
.
get
(
ErrorCode
.
SYNTAX_ERROR_1
,
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS
);
}
}
else
if
(
readIf
(
"INSERT"
))
{
}
else
if
(
readIf
(
"INSERT"
))
{
p
=
parseInsert
();
p
=
parseInsert
();
p
.
setPrepareAlways
(
true
);
p
.
setPrepareAlways
(
true
);
...
@@ -6127,7 +6189,7 @@ public class Parser {
...
@@ -6127,7 +6189,7 @@ public class Parser {
p
=
parseDelete
();
p
=
parseDelete
();
p
.
setPrepareAlways
(
true
);
p
.
setPrepareAlways
(
true
);
}
else
if
(
readIf
(
"CREATE"
))
{
}
else
if
(
readIf
(
"CREATE"
))
{
if
(!
isToken
(
"TABLE"
))
{
if
(!
isToken
(
TABLE
))
{
throw
DbException
.
get
(
ErrorCode
.
SYNTAX_ERROR_1
,
throw
DbException
.
get
(
ErrorCode
.
SYNTAX_ERROR_1
,
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS
);
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS
);
...
@@ -6150,6 +6212,13 @@ public class Parser {
...
@@ -6150,6 +6212,13 @@ public class Parser {
return
p
;
return
p
;
}
}
private
Prepared
parseWithQuery
()
{
Query
query
=
parseSelectUnion
();
query
.
setPrepareAlways
(
true
);
query
.
setNeverLazy
(
true
);
return
query
;
}
private
TableView
parseSingleCommonTableExpression
(
boolean
isTemporary
)
{
private
TableView
parseSingleCommonTableExpression
(
boolean
isTemporary
)
{
String
cteViewName
=
readIdentifierWithSchema
();
String
cteViewName
=
readIdentifierWithSchema
();
Schema
schema
=
getSchema
();
Schema
schema
=
getSchema
();
...
@@ -6328,7 +6397,7 @@ public class Parser {
...
@@ -6328,7 +6397,7 @@ public class Parser {
}
}
private
Prepared
parseAlter
()
{
private
Prepared
parseAlter
()
{
if
(
readIf
(
"TABLE"
))
{
if
(
readIf
(
TABLE
))
{
return
parseAlterTable
();
return
parseAlterTable
();
}
else
if
(
readIf
(
"USER"
))
{
}
else
if
(
readIf
(
"USER"
))
{
return
parseAlterUser
();
return
parseAlterUser
();
...
@@ -6864,7 +6933,7 @@ public class Parser {
...
@@ -6864,7 +6933,7 @@ public class Parser {
schemaNames
.
add
(
readUniqueIdentifier
());
schemaNames
.
add
(
readUniqueIdentifier
());
}
while
(
readIf
(
COMMA
));
}
while
(
readIf
(
COMMA
));
command
.
setSchemaNames
(
schemaNames
);
command
.
setSchemaNames
(
schemaNames
);
}
else
if
(
readIf
(
"TABLE"
))
{
}
else
if
(
readIf
(
TABLE
))
{
ArrayList
<
Table
>
tables
=
Utils
.
newSmallArrayList
();
ArrayList
<
Table
>
tables
=
Utils
.
newSmallArrayList
();
do
{
do
{
tables
.
add
(
readTableOrView
());
tables
.
add
(
readTableOrView
());
...
@@ -7506,7 +7575,7 @@ public class Parser {
...
@@ -7506,7 +7575,7 @@ public class Parser {
private
CreateLinkedTable
parseCreateLinkedTable
(
boolean
temp
,
private
CreateLinkedTable
parseCreateLinkedTable
(
boolean
temp
,
boolean
globalTemp
,
boolean
force
)
{
boolean
globalTemp
,
boolean
force
)
{
read
(
"TABLE"
);
read
(
TABLE
);
boolean
ifNotExists
=
readIfNotExists
();
boolean
ifNotExists
=
readIfNotExists
();
String
tableName
=
readIdentifierWithSchema
();
String
tableName
=
readIdentifierWithSchema
();
CreateLinkedTable
command
=
new
CreateLinkedTable
(
session
,
getSchema
());
CreateLinkedTable
command
=
new
CreateLinkedTable
(
session
,
getSchema
());
...
...
h2/src/main/org/h2/fulltext/FullText.java
浏览文件 @
a5472db7
...
@@ -110,8 +110,8 @@ public class FullText {
...
@@ -110,8 +110,8 @@ public class FullText {
stat
.
execute
(
"CREATE SCHEMA IF NOT EXISTS "
+
SCHEMA
);
stat
.
execute
(
"CREATE SCHEMA IF NOT EXISTS "
+
SCHEMA
);
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, "
+
".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, "
+
"SCHEMA VARCHAR,
TABLE
VARCHAR, COLUMNS VARCHAR, "
+
"SCHEMA VARCHAR,
\"TABLE\"
VARCHAR, COLUMNS VARCHAR, "
+
"UNIQUE(SCHEMA,
TABLE
))"
);
"UNIQUE(SCHEMA,
\"TABLE\"
))"
);
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, "
+
".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, "
+
"NAME VARCHAR, UNIQUE(NAME))"
);
"NAME VARCHAR, UNIQUE(NAME))"
);
...
@@ -176,7 +176,7 @@ public class FullText {
...
@@ -176,7 +176,7 @@ public class FullText {
String
table
,
String
columnList
)
throws
SQLException
{
String
table
,
String
columnList
)
throws
SQLException
{
init
(
conn
);
init
(
conn
);
PreparedStatement
prep
=
conn
.
prepareStatement
(
"INSERT INTO "
+
SCHEMA
PreparedStatement
prep
=
conn
.
prepareStatement
(
"INSERT INTO "
+
SCHEMA
+
".INDEXES(SCHEMA,
TABLE
, COLUMNS) VALUES(?, ?, ?)"
);
+
".INDEXES(SCHEMA,
\"TABLE\"
, COLUMNS) VALUES(?, ?, ?)"
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
3
,
columnList
);
prep
.
setString
(
3
,
columnList
);
...
@@ -221,7 +221,7 @@ public class FullText {
...
@@ -221,7 +221,7 @@ public class FullText {
throws
SQLException
{
throws
SQLException
{
init
(
conn
);
init
(
conn
);
PreparedStatement
prep
=
conn
.
prepareStatement
(
"SELECT ID FROM "
+
SCHEMA
PreparedStatement
prep
=
conn
.
prepareStatement
(
"SELECT ID FROM "
+
SCHEMA
+
".INDEXES WHERE SCHEMA=? AND
TABLE
=?"
);
+
".INDEXES WHERE SCHEMA=? AND
\"TABLE\"
=?"
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
2
,
table
);
ResultSet
rs
=
prep
.
executeQuery
();
ResultSet
rs
=
prep
.
executeQuery
();
...
@@ -929,7 +929,7 @@ public class FullText {
...
@@ -929,7 +929,7 @@ public class FullText {
ArrayList
<
String
>
indexList
=
Utils
.
newSmallArrayList
();
ArrayList
<
String
>
indexList
=
Utils
.
newSmallArrayList
();
PreparedStatement
prep
=
conn
.
prepareStatement
(
PreparedStatement
prep
=
conn
.
prepareStatement
(
"SELECT ID, COLUMNS FROM "
+
SCHEMA
+
".INDEXES"
+
"SELECT ID, COLUMNS FROM "
+
SCHEMA
+
".INDEXES"
+
" WHERE SCHEMA=? AND
TABLE
=?"
);
" WHERE SCHEMA=? AND
\"TABLE\"
=?"
);
prep
.
setString
(
1
,
schemaName
);
prep
.
setString
(
1
,
schemaName
);
prep
.
setString
(
2
,
tableName
);
prep
.
setString
(
2
,
tableName
);
rs
=
prep
.
executeQuery
();
rs
=
prep
.
executeQuery
();
...
...
h2/src/main/org/h2/fulltext/FullTextLucene.java
浏览文件 @
a5472db7
...
@@ -114,8 +114,8 @@ public class FullTextLucene extends FullText {
...
@@ -114,8 +114,8 @@ public class FullTextLucene extends FullText {
try
(
Statement
stat
=
conn
.
createStatement
())
{
try
(
Statement
stat
=
conn
.
createStatement
())
{
stat
.
execute
(
"CREATE SCHEMA IF NOT EXISTS "
+
SCHEMA
);
stat
.
execute
(
"CREATE SCHEMA IF NOT EXISTS "
+
SCHEMA
);
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
stat
.
execute
(
"CREATE TABLE IF NOT EXISTS "
+
SCHEMA
+
".INDEXES(SCHEMA VARCHAR,
TABLE
VARCHAR, "
+
".INDEXES(SCHEMA VARCHAR,
\"TABLE\"
VARCHAR, "
+
"COLUMNS VARCHAR, PRIMARY KEY(SCHEMA,
TABLE
))"
);
"COLUMNS VARCHAR, PRIMARY KEY(SCHEMA,
\"TABLE\"
))"
);
stat
.
execute
(
"CREATE ALIAS IF NOT EXISTS FTL_CREATE_INDEX FOR \""
+
stat
.
execute
(
"CREATE ALIAS IF NOT EXISTS FTL_CREATE_INDEX FOR \""
+
FullTextLucene
.
class
.
getName
()
+
".createIndex\""
);
FullTextLucene
.
class
.
getName
()
+
".createIndex\""
);
stat
.
execute
(
"CREATE ALIAS IF NOT EXISTS FTL_DROP_INDEX FOR \""
+
stat
.
execute
(
"CREATE ALIAS IF NOT EXISTS FTL_DROP_INDEX FOR \""
+
...
@@ -144,7 +144,7 @@ public class FullTextLucene extends FullText {
...
@@ -144,7 +144,7 @@ public class FullTextLucene extends FullText {
String
table
,
String
columnList
)
throws
SQLException
{
String
table
,
String
columnList
)
throws
SQLException
{
init
(
conn
);
init
(
conn
);
PreparedStatement
prep
=
conn
.
prepareStatement
(
"INSERT INTO "
+
SCHEMA
PreparedStatement
prep
=
conn
.
prepareStatement
(
"INSERT INTO "
+
SCHEMA
+
".INDEXES(SCHEMA,
TABLE
, COLUMNS) VALUES(?, ?, ?)"
);
+
".INDEXES(SCHEMA,
\"TABLE\"
, COLUMNS) VALUES(?, ?, ?)"
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
3
,
columnList
);
prep
.
setString
(
3
,
columnList
);
...
@@ -166,7 +166,7 @@ public class FullTextLucene extends FullText {
...
@@ -166,7 +166,7 @@ public class FullTextLucene extends FullText {
init
(
conn
);
init
(
conn
);
PreparedStatement
prep
=
conn
.
prepareStatement
(
"DELETE FROM "
+
SCHEMA
PreparedStatement
prep
=
conn
.
prepareStatement
(
"DELETE FROM "
+
SCHEMA
+
".INDEXES WHERE SCHEMA=? AND
TABLE
=?"
);
+
".INDEXES WHERE SCHEMA=? AND
\"TABLE\"
=?"
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
1
,
schema
);
prep
.
setString
(
2
,
table
);
prep
.
setString
(
2
,
table
);
int
rowCount
=
prep
.
executeUpdate
();
int
rowCount
=
prep
.
executeUpdate
();
...
@@ -548,7 +548,7 @@ public class FullTextLucene extends FullText {
...
@@ -548,7 +548,7 @@ public class FullTextLucene extends FullText {
ArrayList
<
String
>
indexList
=
Utils
.
newSmallArrayList
();
ArrayList
<
String
>
indexList
=
Utils
.
newSmallArrayList
();
PreparedStatement
prep
=
conn
.
prepareStatement
(
PreparedStatement
prep
=
conn
.
prepareStatement
(
"SELECT COLUMNS FROM "
+
SCHEMA
"SELECT COLUMNS FROM "
+
SCHEMA
+
".INDEXES WHERE SCHEMA=? AND
TABLE
=?"
);
+
".INDEXES WHERE SCHEMA=? AND
\"TABLE\"
=?"
);
prep
.
setString
(
1
,
schemaName
);
prep
.
setString
(
1
,
schemaName
);
prep
.
setString
(
2
,
tableName
);
prep
.
setString
(
2
,
tableName
);
rs
=
prep
.
executeQuery
();
rs
=
prep
.
executeQuery
();
...
...
h2/src/main/org/h2/jdbc/JdbcDatabaseMetaData.java
浏览文件 @
a5472db7
...
@@ -1553,8 +1553,8 @@ public class JdbcDatabaseMetaData extends TraceObject implements
...
@@ -1553,8 +1553,8 @@ public class JdbcDatabaseMetaData extends TraceObject implements
* IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
* IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
* LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
* LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
* OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
* OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
* SYSTIME, SYSTIMESTAMP, T
ODAY, TOP, TRUE, UNION, UNIQUE, VALUES
,
* SYSTIME, SYSTIMESTAMP, T
ABLE, TODAY, TOP, TRUE, UNION, UNIQUE
,
* WHERE, WINDOW, WITH
*
VALUES,
WHERE, WINDOW, WITH
* </pre>
* </pre>
*
*
* @return a list of additional the keywords
* @return a list of additional the keywords
...
...
h2/src/main/org/h2/store/LobStorageBackend.java
浏览文件 @
a5472db7
...
@@ -139,10 +139,10 @@ public class LobStorageBackend implements LobStorageInterface {
...
@@ -139,10 +139,10 @@ public class LobStorageBackend implements LobStorageInterface {
}
}
if
(
create
)
{
if
(
create
)
{
stat
.
execute
(
"CREATE CACHED TABLE IF NOT EXISTS "
+
LOBS
+
stat
.
execute
(
"CREATE CACHED TABLE IF NOT EXISTS "
+
LOBS
+
"(ID BIGINT PRIMARY KEY, BYTE_COUNT BIGINT,
TABLE
INT) HIDDEN"
);
"(ID BIGINT PRIMARY KEY, BYTE_COUNT BIGINT,
\"TABLE\"
INT) HIDDEN"
);
stat
.
execute
(
"CREATE INDEX IF NOT EXISTS "
+
stat
.
execute
(
"CREATE INDEX IF NOT EXISTS "
+
"INFORMATION_SCHEMA.INDEX_LOB_TABLE ON "
+
"INFORMATION_SCHEMA.INDEX_LOB_TABLE ON "
+
LOBS
+
"(
TABLE
)"
);
LOBS
+
"(
\"TABLE\"
)"
);
stat
.
execute
(
"CREATE CACHED TABLE IF NOT EXISTS "
+
LOB_MAP
+
stat
.
execute
(
"CREATE CACHED TABLE IF NOT EXISTS "
+
LOB_MAP
+
"(LOB BIGINT, SEQ INT, POS BIGINT, HASH INT, "
+
"(LOB BIGINT, SEQ INT, POS BIGINT, HASH INT, "
+
"BLOCK BIGINT, PRIMARY KEY(LOB, SEQ)) HIDDEN"
);
"BLOCK BIGINT, PRIMARY KEY(LOB, SEQ)) HIDDEN"
);
...
@@ -191,7 +191,7 @@ public class LobStorageBackend implements LobStorageInterface {
...
@@ -191,7 +191,7 @@ public class LobStorageBackend implements LobStorageInterface {
public
void
removeAllForTable
(
int
tableId
)
{
public
void
removeAllForTable
(
int
tableId
)
{
init
();
init
();
try
{
try
{
String
sql
=
"SELECT ID FROM "
+
LOBS
+
" WHERE
TABLE
= ?"
;
String
sql
=
"SELECT ID FROM "
+
LOBS
+
" WHERE
\"TABLE\"
= ?"
;
PreparedStatement
prep
=
prepare
(
sql
);
PreparedStatement
prep
=
prepare
(
sql
);
prep
.
setInt
(
1
,
tableId
);
prep
.
setInt
(
1
,
tableId
);
ResultSet
rs
=
prep
.
executeQuery
();
ResultSet
rs
=
prep
.
executeQuery
();
...
@@ -414,7 +414,7 @@ public class LobStorageBackend implements LobStorageInterface {
...
@@ -414,7 +414,7 @@ public class LobStorageBackend implements LobStorageInterface {
synchronized
(
database
)
{
synchronized
(
database
)
{
synchronized
(
conn
.
getSession
())
{
synchronized
(
conn
.
getSession
())
{
String
sql
=
"INSERT INTO "
+
LOBS
+
String
sql
=
"INSERT INTO "
+
LOBS
+
"(ID, BYTE_COUNT,
TABLE
) VALUES(?, ?, ?)"
;
"(ID, BYTE_COUNT,
\"TABLE\"
) VALUES(?, ?, ?)"
;
PreparedStatement
prep
=
prepare
(
sql
);
PreparedStatement
prep
=
prepare
(
sql
);
prep
.
setLong
(
1
,
lobId
);
prep
.
setLong
(
1
,
lobId
);
prep
.
setLong
(
2
,
byteCount
);
prep
.
setLong
(
2
,
byteCount
);
...
@@ -456,7 +456,7 @@ public class LobStorageBackend implements LobStorageInterface {
...
@@ -456,7 +456,7 @@ public class LobStorageBackend implements LobStorageInterface {
reuse
(
sql
,
prep
);
reuse
(
sql
,
prep
);
sql
=
"INSERT INTO "
+
LOBS
+
sql
=
"INSERT INTO "
+
LOBS
+
"(ID, BYTE_COUNT,
TABLE
) "
+
"(ID, BYTE_COUNT,
\"TABLE\"
) "
+
"SELECT ?, BYTE_COUNT, ? FROM "
+
LOBS
+
"SELECT ?, BYTE_COUNT, ? FROM "
+
LOBS
+
" WHERE ID = ?"
;
" WHERE ID = ?"
;
prep
=
prepare
(
sql
);
prep
=
prepare
(
sql
);
...
...
h2/src/main/org/h2/util/ParserUtil.java
浏览文件 @
a5472db7
...
@@ -232,10 +232,15 @@ public class ParserUtil {
...
@@ -232,10 +232,15 @@ public class ParserUtil {
*/
*/
public
static
final
int
SELECT
=
ROWNUM
+
1
;
public
static
final
int
SELECT
=
ROWNUM
+
1
;
/**
* The token "TABLE".
*/
public
static
final
int
TABLE
=
SELECT
+
1
;
/**
/**
* The token "TRUE".
* The token "TRUE".
*/
*/
public
static
final
int
TRUE
=
SELECT
+
1
;
public
static
final
int
TRUE
=
TABLE
+
1
;
/**
/**
* The token "UNION".
* The token "UNION".
...
@@ -490,7 +495,9 @@ public class ParserUtil {
...
@@ -490,7 +495,9 @@ public class ParserUtil {
}
}
return
IDENTIFIER
;
return
IDENTIFIER
;
case
'T'
:
case
'T'
:
if
(
eq
(
"TRUE"
,
s
,
ignoreCase
,
start
,
end
))
{
if
(
eq
(
"TABLE"
,
s
,
ignoreCase
,
start
,
end
))
{
return
TABLE
;
}
else
if
(
eq
(
"TRUE"
,
s
,
ignoreCase
,
start
,
end
))
{
return
TRUE
;
return
TRUE
;
}
}
if
(
additionalKeywords
)
{
if
(
additionalKeywords
)
{
...
...
h2/src/test/org/h2/test/scripts/TestScript.java
浏览文件 @
a5472db7
...
@@ -155,7 +155,7 @@ public class TestScript extends TestDb {
...
@@ -155,7 +155,7 @@ public class TestScript extends TestDb {
testScript
(
"ddl/"
+
s
+
".sql"
);
testScript
(
"ddl/"
+
s
+
".sql"
);
}
}
for
(
String
s
:
new
String
[]
{
"delete"
,
"error_reporting"
,
"insertIgnore"
,
"merge"
,
"mergeUsing"
,
"replace"
,
for
(
String
s
:
new
String
[]
{
"delete"
,
"error_reporting"
,
"insertIgnore"
,
"merge"
,
"mergeUsing"
,
"replace"
,
"script"
,
"select"
,
"show"
,
"with"
})
{
"script"
,
"select"
,
"show"
,
"
table"
,
"
with"
})
{
testScript
(
"dml/"
+
s
+
".sql"
);
testScript
(
"dml/"
+
s
+
".sql"
);
}
}
for
(
String
s
:
new
String
[]
{
"help"
})
{
for
(
String
s
:
new
String
[]
{
"help"
})
{
...
...
h2/src/test/org/h2/test/scripts/dml/table.sql
0 → 100644
浏览文件 @
a5472db7
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE
TABLE
TEST
(
A
INT
,
B
INT
,
C
INT
);
>
ok
INSERT
INTO
TEST
VALUES
(
1
,
1
,
1
),
(
1
,
1
,
2
),
(
1
,
1
,
3
),
(
1
,
2
,
1
),
(
1
,
2
,
2
),
(
1
,
2
,
3
),
(
2
,
1
,
1
),
(
2
,
1
,
2
),
(
2
,
1
,
3
),
(
2
,
2
,
1
),
(
2
,
2
,
2
),
(
2
,
2
,
3
);
>
update
count
:
12
TABLE
TEST
ORDER
BY
A
,
B
;
>
A
B
C
>
-
-
-
>
1
1
1
>
1
1
2
>
1
1
3
>
1
2
1
>
1
2
2
>
1
2
3
>
2
1
1
>
2
1
2
>
2
1
3
>
2
2
1
>
2
2
2
>
2
2
3
>
rows
(
partially
ordered
):
12
TABLE
TEST
ORDER
BY
A
,
B
,
C
FETCH
FIRST
4
ROWS
ONLY
;
>
A
B
C
>
-
-
-
>
1
1
1
>
1
1
2
>
1
1
3
>
1
2
1
>
rows
(
ordered
):
4
SELECT
*
FROM
(
TABLE
TEST
)
ORDER
BY
A
,
B
,
C
FETCH
FIRST
ROW
ONLY
;
>
A
B
C
>
-
-
-
>
1
1
1
>
rows
(
ordered
):
1
SELECT
(
1
,
2
,
3
)
IN
(
TABLE
TEST
);
>>
TRUE
SELECT
(
TABLE
TEST
FETCH
FIRST
ROW
ONLY
)
"ROW"
;
>
ROW
>
-------------
>
ROW
(
1
,
1
,
1
)
>
rows
:
1
DROP
TABLE
TEST
;
>
ok
h2/src/test/org/h2/test/scripts/dml/with.sql
浏览文件 @
a5472db7
...
@@ -129,4 +129,22 @@ WITH CTE_TEST AS (SELECT 1, 2) ((SELECT * FROM CTE_TEST));
...
@@ -129,4 +129,22 @@ WITH CTE_TEST AS (SELECT 1, 2) ((SELECT * FROM CTE_TEST));
>
1
2
>
1
2
>
-
-
>
-
-
>
1
2
>
1
2
>
rows
:
1
>
rows
:
1
\ No newline at end of file
CREATE
TABLE
TEST
(
A
INT
,
B
INT
)
AS
SELECT
1
,
2
;
>
ok
WITH
CTE_TEST
AS
(
TABLE
TEST
)
((
SELECT
*
FROM
CTE_TEST
));
>
A
B
>
-
-
>
1
2
>
rows
:
1
WITH
CTE_TEST
AS
(
TABLE
TEST
)
((
TABLE
CTE_TEST
));
>
A
B
>
-
-
>
1
2
>
rows
:
1
DROP
TABLE
TEST
;
>
ok
h2/src/test/org/h2/test/scripts/functions/system/table.sql
浏览文件 @
a5472db7
...
@@ -31,11 +31,17 @@ SELECT * FROM (SELECT * FROM TEST) x ORDER BY id;
...
@@ -31,11 +31,17 @@ SELECT * FROM (SELECT * FROM TEST) x ORDER BY id;
drop
table
test
;
drop
table
test
;
>
ok
>
ok
call
table
(
id
int
=
(
1
));
>
ID
>
--
>
1
>
rows
:
1
explain
select
*
from
table
(
id
int
=
(
1
,
2
),
name
varchar
=
(
'Hello'
,
'World'
));
explain
select
*
from
table
(
id
int
=
(
1
,
2
),
name
varchar
=
(
'Hello'
,
'World'
));
>>
SELECT
TABLE
.
ID
,
TABLE
.
NAME
FROM
TABLE
(
ID
INT
=
ROW
(
1
,
2
),
NAME
VARCHAR
=
ROW
(
'Hello'
,
'World'
))
/* function */
>>
SELECT
"TABLE"
.
ID
,
"TABLE"
.
NAME
FROM
TABLE
(
ID
INT
=
ROW
(
1
,
2
),
NAME
VARCHAR
=
ROW
(
'Hello'
,
'World'
))
/* function */
explain
select
*
from
table
(
id
int
=
ARRAY
[
1
,
2
],
name
varchar
=
ARRAY
[
'Hello'
,
'World'
]);
explain
select
*
from
table
(
id
int
=
ARRAY
[
1
,
2
],
name
varchar
=
ARRAY
[
'Hello'
,
'World'
]);
>>
SELECT
TABLE
.
ID
,
TABLE
.
NAME
FROM
TABLE
(
ID
INT
=
ARRAY
[
1
,
2
],
NAME
VARCHAR
=
ARRAY
[
'Hello'
,
'World'
])
/* function */
>>
SELECT
"TABLE"
.
ID
,
"TABLE"
.
NAME
FROM
TABLE
(
ID
INT
=
ARRAY
[
1
,
2
],
NAME
VARCHAR
=
ARRAY
[
'Hello'
,
'World'
])
/* function */
select
*
from
table
(
id
int
=
(
1
,
2
),
name
varchar
=
(
'Hello'
,
'World'
))
x
order
by
id
;
select
*
from
table
(
id
int
=
(
1
,
2
),
name
varchar
=
(
'Hello'
,
'World'
))
x
order
by
id
;
>
ID
NAME
>
ID
NAME
...
@@ -43,3 +49,10 @@ select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by i
...
@@ -43,3 +49,10 @@ select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by i
>
1
Hello
>
1
Hello
>
2
World
>
2
World
>
rows
(
ordered
):
2
>
rows
(
ordered
):
2
SELECT
*
FROM
(
TABLE
(
ID
INT
=
(
1
,
2
)));
>
ID
>
--
>
1
>
2
>
rows
:
2
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论