Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
b6ee16fa
提交
b6ee16fa
authored
5月 19, 2008
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
--no commit message
--no commit message
上级
0677dc95
隐藏空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
268 行增加
和
76 行删除
+268
-76
FullTextSettings.java
h2/src/main/org/h2/fulltext/FullTextSettings.java
+6
-6
BtreeHead.java
h2/src/main/org/h2/index/BtreeHead.java
+2
-2
BtreeIndex.java
h2/src/main/org/h2/index/BtreeIndex.java
+3
-3
BtreeNode.java
h2/src/main/org/h2/index/BtreeNode.java
+1
-1
IndexCondition.java
h2/src/main/org/h2/index/IndexCondition.java
+50
-0
IndexType.java
h2/src/main/org/h2/index/IndexType.java
+71
-1
LinkedIndex.java
h2/src/main/org/h2/index/LinkedIndex.java
+8
-0
ScanIndex.java
h2/src/main/org/h2/index/ScanIndex.java
+7
-0
TreeIndex.java
h2/src/main/org/h2/index/TreeIndex.java
+1
-1
help.csv
h2/src/main/org/h2/res/help.csv
+28
-0
FileLock.java
h2/src/main/org/h2/store/FileLock.java
+66
-24
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+5
-32
test.in.txt
h2/src/test/org/h2/test/test.in.txt
+12
-6
testSimple.in.txt
h2/src/test/org/h2/test/testSimple.in.txt
+8
-0
没有找到文件。
h2/src/main/org/h2/fulltext/FullTextSettings.java
浏览文件 @
b6ee16fa
...
...
@@ -36,7 +36,7 @@ public class FullTextSettings {
return
ignoreList
;
}
public
HashMap
getWordList
()
{
HashMap
getWordList
()
{
return
words
;
}
...
...
@@ -48,7 +48,7 @@ public class FullTextSettings {
indexes
.
put
(
ObjectUtils
.
getLong
(
index
.
id
),
index
);
}
public
String
convertWord
(
String
word
)
{
String
convertWord
(
String
word
)
{
// TODO this is locale specific, document
word
=
word
.
toUpperCase
();
if
(
ignoreList
.
contains
(
word
))
{
...
...
@@ -79,19 +79,19 @@ public class FullTextSettings {
return
path
;
}
public
PreparedStatement
getPrepSelectMapByWordId
()
{
PreparedStatement
getPrepSelectMapByWordId
()
{
return
prepSelectMapByWordId
;
}
public
void
setPrepSelectMapByWordId
(
PreparedStatement
prepSelectMapByWordId
)
{
void
setPrepSelectMapByWordId
(
PreparedStatement
prepSelectMapByWordId
)
{
this
.
prepSelectMapByWordId
=
prepSelectMapByWordId
;
}
public
PreparedStatement
getPrepSelectRowById
()
{
PreparedStatement
getPrepSelectRowById
()
{
return
prepSelectRowById
;
}
public
void
setPrepSelectRowById
(
PreparedStatement
prepSelectRowById
)
{
void
setPrepSelectRowById
(
PreparedStatement
prepSelectRowById
)
{
this
.
prepSelectRowById
=
prepSelectRowById
;
}
...
...
h2/src/main/org/h2/index/BtreeHead.java
浏览文件 @
b6ee16fa
...
...
@@ -31,11 +31,11 @@ public class BtreeHead extends Record {
consistent
=
s
.
readInt
()
==
1
;
}
public
boolean
getConsistent
()
{
boolean
getConsistent
()
{
return
consistent
;
}
public
void
setConsistent
(
boolean
b
)
{
void
setConsistent
(
boolean
b
)
{
this
.
consistent
=
b
;
}
...
...
h2/src/main/org/h2/index/BtreeIndex.java
浏览文件 @
b6ee16fa
...
...
@@ -138,7 +138,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
storage
.
addRecord
(
session
,
p
,
Storage
.
ALLOCATE_POS
);
}
public
BtreePage
getPage
(
Session
session
,
int
i
)
throws
SQLException
{
BtreePage
getPage
(
Session
session
,
int
i
)
throws
SQLException
{
return
(
BtreePage
)
storage
.
getRecord
(
session
,
i
);
}
...
...
@@ -280,7 +280,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return
rows
;
}
public
Row
getRow
(
Session
session
,
int
pos
)
throws
SQLException
{
Row
getRow
(
Session
session
,
int
pos
)
throws
SQLException
{
return
tableData
.
getRow
(
session
,
pos
);
}
...
...
@@ -316,7 +316,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return
needRebuild
;
}
public
int
getRecordOverhead
()
{
int
getRecordOverhead
()
{
return
storage
.
getRecordOverhead
();
}
...
...
h2/src/main/org/h2/index/BtreeNode.java
浏览文件 @
b6ee16fa
...
...
@@ -60,7 +60,7 @@ public class BtreeNode extends BtreePage {
this
.
pageData
=
pageData
;
}
protected
SearchRow
getData
(
int
i
)
throws
SQLException
{
SearchRow
getData
(
int
i
)
throws
SQLException
{
SearchRow
r
=
(
SearchRow
)
pageData
.
get
(
i
);
if
(
r
==
null
)
{
int
p
=
pageChildren
.
get
(
i
+
1
);
...
...
h2/src/main/org/h2/index/IndexCondition.java
浏览文件 @
b6ee16fa
...
...
@@ -53,16 +53,34 @@ public class IndexCondition {
private
Expression
expression
;
private
int
compareType
;
/**
* Create an index condition with the given parameters.
*
* @param compareType the comparison type
* @param column the column
* @param expression the expression
*/
public
IndexCondition
(
int
compareType
,
ExpressionColumn
column
,
Expression
expression
)
{
this
.
compareType
=
compareType
;
this
.
column
=
column
==
null
?
null
:
column
.
getColumn
();
this
.
expression
=
expression
;
}
/**
* Get the current value of the expression.
*
* @param session the session
* @return the value
*/
public
Value
getCurrentValue
(
Session
session
)
throws
SQLException
{
return
expression
.
getValue
(
session
);
}
/**
* Get the SQL snippet of this comparison.
*
* @return the SQL snippet
*/
public
String
getSQL
()
{
if
(
compareType
==
Comparison
.
FALSE
)
{
return
"FALSE"
;
...
...
@@ -92,6 +110,11 @@ public class IndexCondition {
return
buff
.
toString
();
}
/**
* Get the comparison bit mask.
*
* @return the mask
*/
public
int
getMask
()
{
switch
(
compareType
)
{
case
Comparison
.
FALSE
:
...
...
@@ -109,10 +132,21 @@ public class IndexCondition {
}
}
/**
* Check if the result is always false.
*
* @return true if the result will always be false
*/
public
boolean
isAlwaysFalse
()
{
return
compareType
==
Comparison
.
FALSE
;
}
/**
* Check if this index condition is of the type column larger or equal to
* value.
*
* @return true if this is a start condition
*/
public
boolean
isStart
()
{
switch
(
compareType
)
{
case
Comparison
.
EQUAL
:
...
...
@@ -124,6 +158,12 @@ public class IndexCondition {
}
}
/**
* Check if this index condition is of the type column smaller or equal to
* value.
*
* @return true if this is a end condition
*/
public
boolean
isEnd
()
{
switch
(
compareType
)
{
case
Comparison
.
EQUAL
:
...
...
@@ -135,10 +175,20 @@ public class IndexCondition {
}
}
/**
* Get the referenced column.
*
* @return the column
*/
public
Column
getColumn
()
{
return
column
;
}
/**
* Check if the expression can be evaluated.
*
* @return true if it can be evaluated
*/
public
boolean
isEvaluatable
()
{
return
expression
.
isEverything
(
ExpressionVisitor
.
EVALUATABLE
);
}
...
...
h2/src/main/org/h2/index/IndexType.java
浏览文件 @
b6ee16fa
...
...
@@ -13,6 +13,13 @@ public class IndexType {
private
boolean
isPrimaryKey
,
isPersistent
,
isUnique
,
isHash
,
isScan
;
private
boolean
belongsToConstraint
;
/**
* Create a primary key index.
*
* @param persistent if the index is persistent
* @param hash if a hash index should be used
* @return the index type
*/
public
static
IndexType
createPrimaryKey
(
boolean
persistent
,
boolean
hash
)
{
IndexType
type
=
new
IndexType
();
type
.
isPrimaryKey
=
true
;
...
...
@@ -22,6 +29,13 @@ public class IndexType {
return
type
;
}
/**
* Create a unique index.
*
* @param persistent if the index is persistent
* @param hash if a hash index should be used
* @return the index type
*/
public
static
IndexType
createUnique
(
boolean
persistent
,
boolean
hash
)
{
IndexType
type
=
new
IndexType
();
type
.
isUnique
=
true
;
...
...
@@ -30,12 +44,24 @@ public class IndexType {
return
type
;
}
/**
* Create a non-unique index.
*
* @param persistent if the index is persistent
* @return the index type
*/
public
static
IndexType
createNonUnique
(
boolean
persistent
)
{
IndexType
type
=
new
IndexType
();
type
.
isPersistent
=
persistent
;
return
type
;
}
/**
* Create a scan pseudo-index.
*
* @param persistent if the index is persistent
* @return the index type
*/
public
static
IndexType
createScan
(
boolean
persistent
)
{
IndexType
type
=
new
IndexType
();
type
.
isPersistent
=
persistent
;
...
...
@@ -43,27 +69,66 @@ public class IndexType {
return
type
;
}
/**
* Sets if this index belongs to a constraint.
*
* @param belongsToConstraint if the index belongs to a constraint
*/
public
void
setBelongsToConstraint
(
boolean
belongsToConstraint
)
{
this
.
belongsToConstraint
=
belongsToConstraint
;
}
/**
* If the index is created because of a constraint. Such indexes are to be
* dropped once the constraint is dropped.
*
* @return if the index belongs to a constraint
*/
public
boolean
belongsToConstraint
()
{
return
belongsToConstraint
;
}
/**
* Is this a hash index?
*
* @return true if it is a hash index
*/
public
boolean
isHash
()
{
return
isHash
;
}
/**
* Is this index persistent?
*
* @return true if it is persistent
*/
public
boolean
isPersistent
()
{
return
isPersistent
;
}
/**
* Does this index belong to a primary key constraint?
*
* @return true if it references a primary key constraint
*/
public
boolean
isPrimaryKey
()
{
return
isPrimaryKey
;
}
/**
* Is this a unique index?
*
* @return true if it is
*/
public
boolean
isUnique
()
{
return
isUnique
;
}
/**
* Get the SQL snipped to create such an index.
*
* @return the SQL snipped
*/
public
String
getSQL
()
{
StringBuffer
buff
=
new
StringBuffer
();
if
(
isPrimaryKey
)
{
...
...
@@ -83,6 +148,11 @@ public class IndexType {
return
buff
.
toString
();
}
/**
* Is this a table scan pseudo-index?
*
* @return true if it is
*/
public
boolean
isScan
()
{
return
isScan
;
}
...
...
h2/src/main/org/h2/index/LinkedIndex.java
浏览文件 @
b6ee16fa
...
...
@@ -193,6 +193,14 @@ public class LinkedIndex extends BaseIndex {
}
}
/**
* Update a row using a UPDATE statement. This method is to be called if the
* emit updates option is enabled.
*
* @param session the session
* @param oldRow the old data
* @param newRow the new data
*/
public
void
update
(
Session
session
,
Row
oldRow
,
Row
newRow
)
throws
SQLException
{
StringBuffer
buff
=
new
StringBuffer
(
"UPDATE "
);
buff
.
append
(
targetTableName
).
append
(
" SET "
);
...
...
h2/src/main/org/h2/index/ScanIndex.java
浏览文件 @
b6ee16fa
...
...
@@ -92,6 +92,13 @@ public class ScanIndex extends BaseIndex {
}
}
/**
* Get the row at the given position.
*
* @param session the session
* @param key the position
* @return the row
*/
public
Row
getRow
(
Session
session
,
int
key
)
throws
SQLException
{
if
(
storage
!=
null
)
{
return
(
Row
)
storage
.
getRecord
(
session
,
key
);
...
...
h2/src/main/org/h2/index/TreeIndex.java
浏览文件 @
b6ee16fa
...
...
@@ -329,7 +329,7 @@ public class TreeIndex extends BaseIndex {
return
x
;
}
public
TreeNode
previous
(
TreeNode
x
)
{
TreeNode
previous
(
TreeNode
x
)
{
if
(
x
==
null
)
{
return
null
;
}
...
...
h2/src/main/org/h2/res/help.csv
浏览文件 @
b6ee16fa
...
...
@@ -1761,6 +1761,25 @@ Aggregates are only allowed in select statements.
","
AVG(X)
"
"Functions (Aggregate)","BOOL_AND","
BOOL_AND(boolean): boolean
","
Returns true if all expressions are true.
Aggregates are only allowed in select statements.
","
BOOL_AND(ID>10)
"
"Functions (Aggregate)","BOOL_OR","
BOOL_OR(boolean): boolean
","
Returns true if any expression is true.
Aggregates are only allowed in select statements.
","
BOOL_OR(NAME LIKE 'W%')
"
"Functions (Aggregate)","COUNT","
COUNT(*) | COUNT([DISTINCT] expression): int
","
...
...
@@ -1769,6 +1788,7 @@ Aggregates are only allowed in select statements.
","
COUNT(*)
"
"Functions (Aggregate)","GROUP_CONCAT","
GROUP_CONCAT([DISTINCT] string [ORDER BY {expression [ASC|DESC]}[,...]] [SEPARATOR expression]): string
","
...
...
@@ -1777,6 +1797,7 @@ Aggregates are only allowed in select statements.
","
GROUP_CONCAT(NAME ORDER BY ID SEPARATOR ', ')
"
"Functions (Aggregate)","MAX","
MAX(value): value
","
...
...
@@ -1785,6 +1806,7 @@ Aggregates are only allowed in select statements.
","
MAX(NAME)
"
"Functions (Aggregate)","MIN","
MIN(value): value
","
...
...
@@ -1793,6 +1815,7 @@ Aggregates are only allowed in select statements.
","
MIN(NAME)
"
"Functions (Aggregate)","SUM","
SUM([DISTINCT] {int | long | decimal | double}): value
","
...
...
@@ -1801,6 +1824,7 @@ Aggregates are only allowed in select statements.
","
SUM(X)
"
"Functions (Aggregate)","SELECTIVITY","
SELECTIVITY(value): int
","
...
...
@@ -1812,6 +1836,7 @@ Aggregates are only allowed in select statements.
","
SELECT SELECTIVITY(FIRSTNAME), SELECTIVITY(NAME) FROM TEST WHERE ROWNUM()<100000
"
"Functions (Aggregate)","STDDEV_POP","
STDDEV_POP([DISTINCT] double): double
","
...
...
@@ -1820,6 +1845,7 @@ Aggregates are only allowed in select statements.
","
STDDEV_POP(X)
"
"Functions (Aggregate)","STDDEV_SAMP","
STDDEV_SAMP([DISTINCT] double): double
","
...
...
@@ -1828,6 +1854,7 @@ Aggregates are only allowed in select statements.
","
STDDEV(X)
"
"Functions (Aggregate)","VAR_POP","
VAR_POP([DISTINCT] double): double
","
...
...
@@ -1836,6 +1863,7 @@ Aggregates are only allowed in select statements.
","
VAR_POP(X)
"
"Functions (Aggregate)","VAR_SAMP","
VAR_SAMP([DISTINCT] double): double
","
...
...
h2/src/main/org/h2/store/FileLock.java
浏览文件 @
b6ee16fa
...
...
@@ -31,15 +31,32 @@ import org.h2.util.SortedProperties;
/**
* The file lock is used to lock a database so that only one process can write
* to it.
Usually a .lock.db file is used, but locking by creating a socket
is
* supported as well.
* to it.
It uses a cooperative locking protocol. Usually a .lock.db file
is
*
used, but locking by creating a socket is
supported as well.
*/
public
class
FileLock
{
/**
* This locking method means no locking is used at all.
*/
public
static
final
int
LOCK_NO
=
0
;
public
static
final
int
LOCK_NO
=
0
,
LOCK_FILE
=
1
,
LOCK_SOCKET
=
2
;
/**
* This locking method means the cooperative file locking protocol should be
* used.
*/
public
static
final
int
LOCK_FILE
=
1
;
/**
* This locking method means a socket is created on the given machine.
*/
public
static
final
int
LOCK_SOCKET
=
2
;
// TODO lock: maybe not so secure! what if tread does not have chance to run?
// TODO lock: implement locking method using java 1.4 FileLock
// TODO log / messages: use translatable messages
// private java.nio.channels.FileLock fileLock;
private
static
final
String
MAGIC
=
"FileLock"
;
private
static
final
String
FILE
=
"file"
,
SOCKET
=
"socket"
;
private
static
final
int
RANDOM_BYTES
=
16
;
...
...
@@ -56,13 +73,25 @@ public class FileLock {
private
boolean
locked
;
private
Trace
trace
;
// private java.nio.channels.FileLock fileLock;
/**
* Create a new file locking object.
*
* @param traceSystem the trace system to use
* @param sleep the number of milliseconds to sleep
*/
public
FileLock
(
TraceSystem
traceSystem
,
int
sleep
)
{
this
.
trace
=
traceSystem
.
getTrace
(
Trace
.
FILE_LOCK
);
this
.
sleep
=
sleep
;
}
/**
* Lock the file if possible. A file may only be locked once.
*
* @param fileName the name of the properties file to use
* @param allowSocket if the socket locking protocol should be used if
* possible
* @throws SQLException if locking was not successful
*/
public
synchronized
void
lock
(
String
fileName
,
boolean
allowSocket
)
throws
SQLException
{
this
.
fs
=
FileSystem
.
getInstance
(
fileName
);
this
.
fileName
=
fileName
;
...
...
@@ -76,24 +105,11 @@ public class FileLock {
}
locked
=
true
;
}
protected
void
finalize
()
{
if
(!
SysProperties
.
runFinalize
)
{
return
;
}
if
(
locked
)
{
unlock
();
}
}
// void kill() {
// socket = null;
// file = null;
// locked = false;
// trace("killed", null);
// }
// TODO log / messages: use translatable messages!
/**
* Unlock the file. The watchdog thread is stopped. This method does nothing
* if the file is already unlocked.
*/
public
synchronized
void
unlock
()
{
if
(!
locked
)
{
return
;
...
...
@@ -115,7 +131,26 @@ public class FileLock {
locked
=
false
;
}
void
save
()
throws
SQLException
{
/**
* This finalizer unlocks the file if necessary.
*/
protected
void
finalize
()
{
if
(!
SysProperties
.
runFinalize
)
{
return
;
}
if
(
locked
)
{
unlock
();
}
}
// void kill() {
// socket = null;
// file = null;
// locked = false;
// trace("killed", null);
// }
private
void
save
()
throws
SQLException
{
try
{
OutputStream
out
=
fs
.
openFileOutputStream
(
fileName
,
false
);
try
{
...
...
@@ -317,6 +352,13 @@ public class FileLock {
return
Message
.
getSQLException
(
ErrorCode
.
DATABASE_ALREADY_OPEN_1
,
reason
);
}
/**
* Get the file locking method type given a method name.
*
* @param method the method name
* @return the method type
* @throws SQLException if the method name is unknown
*/
public
static
int
getFileLockMethod
(
String
method
)
throws
SQLException
{
if
(
method
==
null
||
method
.
equalsIgnoreCase
(
"FILE"
))
{
return
FileLock
.
LOCK_FILE
;
...
...
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
b6ee16fa
...
...
@@ -160,46 +160,15 @@ java org.h2.test.TestAll timer
/*
create table test(d number(1, -84));
MySQL:
CREATE TABLE user (
id int(25) NOT NULL auto_increment,
name varchar(25) NOT NULL,
PRIMARY KEY (id,name)
)
I've created a linked table to an oracle database, and tried:
create table person as select * from ora_person
This gives me:
Invalid value -127 for parameter scale [90008-71] 90008/90008
Doing a column-by-column select, I get the exception when I try to
create table as select one of the NUMBER fields with unspecified
lengths.
Here's a simpler example:
create table t as select sysadmin from ora_person;
(sysadmin is a NUMBER column)
This works:
create table t as select cast(sysadmin as int) sysadmin from
ora_person
But, it's clunky. Is this something that can be fixed in a future
version?
download PostgreSQL docs
BIT_AND, BIT_OR, BIT_XOR
SOME, EVERY: HSQLDB compatibility
add tests for SOME, EVERY (SOME is used in Parser.java
for something else; test this; maybe use BOOL_AND / BOOL_OR)
document SOME, EVERY
in help.csv, use complete examples for functions; add a test case
upload and test javadoc/index.html
improve javadocs
write complete page right after checkpoint
option to
write complete page right after checkpoint
upload jazoon
...
...
@@ -254,6 +223,10 @@ History:
Roadmap:
Negative scale values for DECIMAL or NUMBER columns are not supported
in regular tables and in linked tables.
MySQL compatibility: auto_increment column are no longer automatically
converted to primary key columns.
PostgreSQL compatibility: support for BOOL_OR and BOOL_AND
aggregate functions.
*/
if
(
args
.
length
>
0
)
{
...
...
h2/src/test/org/h2/test/test.in.txt
浏览文件 @
b6ee16fa
--- special grammar and test cases ---------------------------------------------------------------------------------------------
CREATE TABLE test (id int(25) NOT NULL auto_increment, name varchar NOT NULL, PRIMARY KEY (id,name));
> ok
drop table test;
> ok
CREATE MEMORY TABLE TEST(ID INT, D DOUBLE, F FLOAT);
> ok
...
...
@@ -6734,9 +6740,9 @@ select * from s;
> rows: 1
select some(y>10), every(y>10), min(y), max(y) from t;
>
SOME(Y > 10.0) EVERY
(Y > 10.0) MIN(Y) MAX(Y)
> --------------
--------------- ------ ------
> null
null
null null
>
BOOL_OR(Y > 10.0) BOOL_AND
(Y > 10.0) MIN(Y) MAX(Y)
> --------------
--- ---
--------------- ------ ------
> null
null
null null
> rows: 1
insert into t values(1000000004, 4);
...
...
@@ -6792,9 +6798,9 @@ stddev_pop(distinct y) s_py, stddev_samp(distinct y) s_sy, var_pop(distinct y) v
> rows: 1
select some(y>10), every(y>10), min(y), max(y) from t;
>
SOME(Y > 10.0) EVERY
(Y > 10.0) MIN(Y) MAX(Y)
> --------------
--------------- ------ ------
> TRUE
FALSE
4.0 16.0
>
BOOL_OR(Y > 10.0) BOOL_AND
(Y > 10.0) MIN(Y) MAX(Y)
> --------------
--- ---
--------------- ------ ------
> TRUE
FALSE
4.0 16.0
> rows: 1
drop view s;
...
...
h2/src/test/org/h2/test/testSimple.in.txt
浏览文件 @
b6ee16fa
SELECT SOME(X>4) FROM SYSTEM_RANGE(1,6);
> TRUE;
SELECT EVERY(X>4) FROM SYSTEM_RANGE(1,6);
> FALSE;
SELECT BOOL_OR(X>4) FROM SYSTEM_RANGE(1,6);
> TRUE;
SELECT BOOL_AND(X>4) FROM SYSTEM_RANGE(1,6);
> FALSE;
CREATE TABLE TEST(ID IDENTITY);
ALTER TABLE TEST ALTER COLUMN ID RESTART WITH ? {1:10};
INSERT INTO TEST VALUES(NULL);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论