Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Greenplum
Gpdb
提交
c4096c76
G
Gpdb
项目概览
Greenplum
/
Gpdb
通知
7
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
Gpdb
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
c4096c76
编写于
8月 05, 2011
作者:
R
Robert Haas
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Allow per-column foreign data wrapper options.
Shigeru Hanada, with fairly minor editing by me.
上级
68cbb9f4
变更
23
隐藏空白更改
内联
并排
Showing
23 changed file
with
407 addition
and
47 deletion
+407
-47
doc/src/sgml/catalogs.sgml
doc/src/sgml/catalogs.sgml
+9
-0
doc/src/sgml/information_schema.sgml
doc/src/sgml/information_schema.sgml
+63
-0
doc/src/sgml/ref/alter_foreign_table.sgml
doc/src/sgml/ref/alter_foreign_table.sgml
+6
-4
doc/src/sgml/ref/create_foreign_table.sgml
doc/src/sgml/ref/create_foreign_table.sgml
+5
-3
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/ref/psql-ref.sgml
+6
-0
src/backend/access/common/tupdesc.c
src/backend/access/common/tupdesc.c
+2
-2
src/backend/catalog/genbki.pl
src/backend/catalog/genbki.pl
+3
-1
src/backend/catalog/heap.c
src/backend/catalog/heap.c
+2
-1
src/backend/catalog/information_schema.sql
src/backend/catalog/information_schema.sql
+33
-0
src/backend/commands/tablecmds.c
src/backend/commands/tablecmds.c
+106
-0
src/backend/nodes/copyfuncs.c
src/backend/nodes/copyfuncs.c
+1
-0
src/backend/nodes/outfuncs.c
src/backend/nodes/outfuncs.c
+1
-0
src/backend/parser/gram.y
src/backend/parser/gram.y
+12
-2
src/backend/parser/parse_utilcmd.c
src/backend/parser/parse_utilcmd.c
+25
-0
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.c
+54
-6
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump.h
+1
-0
src/bin/psql/describe.c
src/bin/psql/describe.c
+20
-2
src/include/catalog/catversion.h
src/include/catalog/catversion.h
+1
-1
src/include/catalog/pg_attribute.h
src/include/catalog/pg_attribute.h
+5
-1
src/include/catalog/pg_class.h
src/include/catalog/pg_class.h
+1
-1
src/include/nodes/parsenodes.h
src/include/nodes/parsenodes.h
+2
-0
src/test/regress/expected/foreign_data.out
src/test/regress/expected/foreign_data.out
+41
-20
src/test/regress/sql/foreign_data.sql
src/test/regress/sql/foreign_data.sql
+8
-3
未找到文件。
doc/src/sgml/catalogs.sgml
浏览文件 @
c4096c76
...
...
@@ -1157,6 +1157,15 @@
</entry>
</row>
<row>
<entry><structfield>attfdwoptions</structfield></entry>
<entry><type>text[]</type></entry>
<entry></entry>
<entry>
Attribute-level foreign data wrapper options, as <quote>keyword=value</> strings
</entry>
</row>
</tbody>
</tgroup>
</table>
...
...
doc/src/sgml/information_schema.sgml
浏览文件 @
c4096c76
...
...
@@ -1018,6 +1018,69 @@
</table>
</sect1>
<sect1 id="infoschema-column-options">
<title><literal>column_options</literal></title>
<para>
The view <literal>column_options</literal> contains all the
options defined for foreign table columns in the current database. Only
those foreign table columns are shown that the current user has access to
(by way of being the owner or having some privilege).
</para>
<table>
<title><literal>column_options</literal> Columns</title>
<tgroup cols="3">
<thead>
<row>
<entry>Name</entry>
<entry>Data Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>table_catalog</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the database that contains the foreign table (always the current database)</entry>
</row>
<row>
<entry><literal>table_schema</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the schema that contains the foreign table</entry>
</row>
<row>
<entry><literal>table_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the foreign table</entry>
</row>
<row>
<entry><literal>column_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the column</entry>
</row>
<row>
<entry><literal>option_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of an option</entry>
</row>
<row>
<entry><literal>option_value</literal></entry>
<entry><type>character_data</type></entry>
<entry>Value of the option</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="infoschema-column-privileges">
<title><literal>column_privileges</literal></title>
...
...
doc/src/sgml/ref/alter_foreign_table.sgml
浏览文件 @
c4096c76
...
...
@@ -36,6 +36,7 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
</synopsis>
...
...
@@ -125,12 +126,13 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
<term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
<listitem>
<para>
Change options for the foreign table.
Change options for the foreign table
or one of its columns
.
<literal>ADD</>, <literal>SET</>, and <literal>DROP</>
specify the action to be performed. <literal>ADD</> is assumed
if no operation is explicitly specified. Option names must be
unique; names and values are also validated using the foreign
data wrapper library.
if no operation is explicitly specified. Duplicate option names are not
allowed (although it's OK for a table option and a column option to have
the same name). Option names and values are also validated using the
foreign data wrapper library.
</para>
</listitem>
</varlistentry>
...
...
doc/src/sgml/ref/create_foreign_table.sgml
浏览文件 @
c4096c76
...
...
@@ -19,7 +19,7 @@
<refsynopsisdiv>
<synopsis>
CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ NULL | NOT NULL ] }
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [
OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] [
NULL | NOT NULL ] }
[, ... ]
] )
SERVER <replaceable class="parameter">server_name</replaceable>
...
...
@@ -138,10 +138,12 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
<term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
<listitem>
<para>
Options to be associated with the new foreign table.
Options to be associated with the new foreign table or one of its
columns.
The allowed option names and values are specific to each foreign
data wrapper and are validated using the foreign-data wrapper's
validator function. Option names must be unique.
validator function. Duplicate option names are not allowed (although
it's OK for a table option and a column option to have the same name).
</para>
</listitem>
</varlistentry>
...
...
doc/src/sgml/ref/psql-ref.sgml
浏览文件 @
c4096c76
...
...
@@ -891,6 +891,12 @@ testdb=>
below.)
</para>
<para>
For some types of relation, <literal>\d</> shows additional information
for each column: column values for sequences, indexed expression for
indexes and per-column foreign data wrapper options for foreign tables.
</para>
<para>
The command form <literal>\d+</literal> is identical, except that
more information is displayed: any comments associated with the
...
...
src/backend/access/common/tupdesc.c
浏览文件 @
c4096c76
...
...
@@ -363,7 +363,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return
false
;
if
(
attr1
->
attcollation
!=
attr2
->
attcollation
)
return
false
;
/* attacl
and att
options are not even present... */
/* attacl
, attoptions and attfdw
options are not even present... */
}
if
(
tupdesc1
->
constr
!=
NULL
)
...
...
@@ -483,7 +483,7 @@ TupleDescInitEntry(TupleDesc desc,
att
->
attisdropped
=
false
;
att
->
attislocal
=
true
;
att
->
attinhcount
=
0
;
/* attacl
and att
options are not present in tupledescs */
/* attacl
, attoptions and attfdw
options are not present in tupledescs */
tuple
=
SearchSysCache1
(
TYPEOID
,
ObjectIdGetDatum
(
oidtypeid
));
if
(
!
HeapTupleIsValid
(
tuple
))
...
...
src/backend/catalog/genbki.pl
浏览文件 @
c4096c76
...
...
@@ -369,7 +369,8 @@ sub emit_pgattr_row
attislocal
=>
'
t
',
attinhcount
=>
'
0
',
attacl
=>
'
_null_
',
attoptions
=>
'
_null_
'
attoptions
=>
'
_null_
',
attfdwoptions
=>
'
_null_
'
);
return
{
%PGATTR_DEFAULTS
,
%row
};
}
...
...
@@ -400,6 +401,7 @@ sub emit_schemapg_row
# Only the fixed-size portions of the descriptors are ever used.
delete
$row
->
{
attacl
};
delete
$row
->
{
attoptions
};
delete
$row
->
{
attfdwoptions
};
# Expand booleans from 'f'/'t' to 'false'/'true'.
# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
...
...
src/backend/catalog/heap.c
浏览文件 @
c4096c76
...
...
@@ -126,7 +126,7 @@ static List *insert_ordered_unique_oid(List *list, Oid datum);
*/
/*
* The initializers below do not include t
he attoptions or attacl
fields,
* The initializers below do not include t
railing variable length
fields,
* but that's OK - we're never going to reference anything beyond the
* fixed-size portion of the structure anyway.
*/
...
...
@@ -620,6 +620,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
/* start out with empty permissions and empty options */
nulls
[
Anum_pg_attribute_attacl
-
1
]
=
true
;
nulls
[
Anum_pg_attribute_attoptions
-
1
]
=
true
;
nulls
[
Anum_pg_attribute_attfdwoptions
-
1
]
=
true
;
tup
=
heap_form_tuple
(
RelationGetDescr
(
pg_attribute_rel
),
values
,
nulls
);
...
...
src/backend/catalog/information_schema.sql
浏览文件 @
c4096c76
...
...
@@ -2534,6 +2534,39 @@ GRANT SELECT ON element_types TO PUBLIC;
-- SQL/MED views; these use section numbers from part 9 of the standard.
/* Base view for foreign table columns */
CREATE
VIEW
_pg_foreign_table_columns
AS
SELECT
n
.
nspname
,
c
.
relname
,
a
.
attname
,
a
.
attfdwoptions
FROM
pg_foreign_table
t
,
pg_authid
u
,
pg_namespace
n
,
pg_class
c
,
pg_attribute
a
WHERE
u
.
oid
=
c
.
relowner
AND
(
pg_has_role
(
c
.
relowner
,
'USAGE'
)
OR
has_column_privilege
(
c
.
oid
,
a
.
attnum
,
'SELECT, INSERT, UPDATE, REFERENCES'
))
AND
n
.
oid
=
c
.
relnamespace
AND
c
.
oid
=
t
.
ftrelid
AND
c
.
relkind
=
'f'
AND
a
.
attrelid
=
c
.
oid
AND
a
.
attnum
>
0
;
/*
* 24.2
* COLUMN_OPTIONS view
*/
CREATE
VIEW
column_options
AS
SELECT
CAST
(
current_database
()
AS
sql_identifier
)
AS
table_catalog
,
c
.
nspname
AS
table_schema
,
c
.
relname
AS
table_name
,
c
.
attname
AS
column_name
,
CAST
((
pg_options_to_table
(
c
.
attfdwoptions
)).
option_name
AS
sql_identifier
)
AS
option_name
,
CAST
((
pg_options_to_table
(
c
.
attfdwoptions
)).
option_value
AS
character_data
)
AS
option_value
FROM
_pg_foreign_table_columns
c
;
GRANT
SELECT
ON
column_options
TO
PUBLIC
;
/* Base view for foreign-data wrappers */
CREATE
VIEW
_pg_foreign_data_wrappers
AS
SELECT
w
.
oid
,
...
...
src/backend/commands/tablecmds.c
浏览文件 @
c4096c76
...
...
@@ -346,6 +346,8 @@ static void ATPrepAlterColumnType(List **wqueue,
static
bool
ATColumnChangeRequiresRewrite
(
Node
*
expr
,
AttrNumber
varattno
);
static
void
ATExecAlterColumnType
(
AlteredTableInfo
*
tab
,
Relation
rel
,
AlterTableCmd
*
cmd
,
LOCKMODE
lockmode
);
static
void
ATExecAlterColumnGenericOptions
(
Relation
rel
,
const
char
*
colName
,
List
*
options
,
LOCKMODE
lockmode
);
static
void
ATPostAlterTypeCleanup
(
List
**
wqueue
,
AlteredTableInfo
*
tab
,
LOCKMODE
lockmode
);
static
void
ATPostAlterTypeParse
(
Oid
oldId
,
char
*
cmd
,
List
**
wqueue
,
LOCKMODE
lockmode
,
bool
rewrite
);
...
...
@@ -2648,6 +2650,7 @@ AlterTableGetLockLevel(List *cmds)
case
AT_DropNotNull
:
/* may change some SQL plans */
case
AT_SetNotNull
:
case
AT_GenericOptions
:
case
AT_AlterColumnGenericOptions
:
cmd_lockmode
=
AccessExclusiveLock
;
break
;
...
...
@@ -2925,6 +2928,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepAlterColumnType
(
wqueue
,
tab
,
rel
,
recurse
,
recursing
,
cmd
,
lockmode
);
pass
=
AT_PASS_ALTER_TYPE
;
break
;
case
AT_AlterColumnGenericOptions
:
ATSimplePermissions
(
rel
,
ATT_FOREIGN_TABLE
);
/* This command never recurses */
/* No command-specific prep needed */
pass
=
AT_PASS_MISC
;
break
;
case
AT_ChangeOwner
:
/* ALTER OWNER */
/* This command never recurses */
/* No command-specific prep needed */
...
...
@@ -3169,6 +3178,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case
AT_AlterColumnType
:
/* ALTER COLUMN TYPE */
ATExecAlterColumnType
(
tab
,
rel
,
cmd
,
lockmode
);
break
;
case
AT_AlterColumnGenericOptions
:
/* ALTER COLUMN OPTIONS */
ATExecAlterColumnGenericOptions
(
rel
,
cmd
->
name
,
(
List
*
)
cmd
->
def
,
lockmode
);
break
;
case
AT_ChangeOwner
:
/* ALTER OWNER */
ATExecChangeOwner
(
RelationGetRelid
(
rel
),
get_role_oid
(
cmd
->
name
,
false
),
...
...
@@ -7397,6 +7409,100 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
heap_freetuple
(
heapTup
);
}
static
void
ATExecAlterColumnGenericOptions
(
Relation
rel
,
const
char
*
colName
,
List
*
options
,
LOCKMODE
lockmode
)
{
Relation
ftrel
;
Relation
attrel
;
ForeignServer
*
server
;
ForeignDataWrapper
*
fdw
;
HeapTuple
tuple
;
HeapTuple
newtuple
;
bool
isnull
;
Datum
repl_val
[
Natts_pg_attribute
];
bool
repl_null
[
Natts_pg_attribute
];
bool
repl_repl
[
Natts_pg_attribute
];
Datum
datum
;
Form_pg_foreign_table
fttableform
;
Form_pg_attribute
atttableform
;
if
(
options
==
NIL
)
return
;
/* First, determine FDW validator associated to the foreign table. */
ftrel
=
heap_open
(
ForeignTableRelationId
,
AccessShareLock
);
tuple
=
SearchSysCache1
(
FOREIGNTABLEREL
,
rel
->
rd_id
);
if
(
!
HeapTupleIsValid
(
tuple
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_OBJECT
),
errmsg
(
"foreign table
\"
%s
\"
does not exist"
,
RelationGetRelationName
(
rel
))));
fttableform
=
(
Form_pg_foreign_table
)
GETSTRUCT
(
tuple
);
server
=
GetForeignServer
(
fttableform
->
ftserver
);
fdw
=
GetForeignDataWrapper
(
server
->
fdwid
);
heap_close
(
ftrel
,
AccessShareLock
);
ReleaseSysCache
(
tuple
);
attrel
=
heap_open
(
AttributeRelationId
,
RowExclusiveLock
);
tuple
=
SearchSysCacheAttName
(
RelationGetRelid
(
rel
),
colName
);
if
(
!
HeapTupleIsValid
(
tuple
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"column
\"
%s
\"
of relation
\"
%s
\"
does not exist"
,
colName
,
RelationGetRelationName
(
rel
))));
/* Prevent them from altering a system attribute */
atttableform
=
(
Form_pg_attribute
)
GETSTRUCT
(
tuple
);
if
(
atttableform
->
attnum
<=
0
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"cannot alter system column
\"
%s
\"
"
,
colName
)));
/* Initialize buffers for new tuple values */
memset
(
repl_val
,
0
,
sizeof
(
repl_val
));
memset
(
repl_null
,
false
,
sizeof
(
repl_null
));
memset
(
repl_repl
,
false
,
sizeof
(
repl_repl
));
/* Extract the current options */
datum
=
SysCacheGetAttr
(
ATTNAME
,
tuple
,
Anum_pg_attribute_attfdwoptions
,
&
isnull
);
if
(
isnull
)
datum
=
PointerGetDatum
(
NULL
);
/* Transform the options */
datum
=
transformGenericOptions
(
AttributeRelationId
,
datum
,
options
,
fdw
->
fdwvalidator
);
if
(
PointerIsValid
(
DatumGetPointer
(
datum
)))
repl_val
[
Anum_pg_attribute_attfdwoptions
-
1
]
=
datum
;
else
repl_null
[
Anum_pg_attribute_attfdwoptions
-
1
]
=
true
;
repl_repl
[
Anum_pg_attribute_attfdwoptions
-
1
]
=
true
;
/* Everything looks good - update the tuple */
newtuple
=
heap_modify_tuple
(
tuple
,
RelationGetDescr
(
attrel
),
repl_val
,
repl_null
,
repl_repl
);
ReleaseSysCache
(
tuple
);
simple_heap_update
(
attrel
,
&
newtuple
->
t_self
,
newtuple
);
CatalogUpdateIndexes
(
attrel
,
newtuple
);
heap_close
(
attrel
,
RowExclusiveLock
);
heap_freetuple
(
newtuple
);
}
/*
* Cleanup after we've finished all the ALTER TYPE operations for a
* particular relation. We have to drop and recreate all the indexes
...
...
src/backend/nodes/copyfuncs.c
浏览文件 @
c4096c76
...
...
@@ -2312,6 +2312,7 @@ _copyColumnDef(ColumnDef *from)
COPY_NODE_FIELD
(
collClause
);
COPY_SCALAR_FIELD
(
collOid
);
COPY_NODE_FIELD
(
constraints
);
COPY_NODE_FIELD
(
fdwoptions
);
return
newnode
;
}
...
...
src/backend/nodes/outfuncs.c
浏览文件 @
c4096c76
...
...
@@ -2102,6 +2102,7 @@ _outColumnDef(StringInfo str, ColumnDef *node)
WRITE_NODE_FIELD
(
collClause
);
WRITE_OID_FIELD
(
collOid
);
WRITE_NODE_FIELD
(
constraints
);
WRITE_NODE_FIELD
(
fdwoptions
);
}
static
void
...
...
src/backend/parser/gram.y
浏览文件 @
c4096c76
...
...
@@ -1769,6 +1769,15 @@ alter_table_cmd:
def->raw_default = $8;
$$ = (Node *)n;
}
/* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> OPTIONS */
| ALTER opt_column ColId alter_generic_options
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AlterColumnGenericOptions;
n->name = $3;
n->def = (Node *) $4;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ADD CONSTRAINT ... */
| ADD_P TableConstraint
{
...
...
@@ -2497,7 +2506,7 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
columnDef: ColId Typename ColQualList
columnDef: ColId Typename
create_generic_options
ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
...
...
@@ -2510,7 +2519,8 @@ columnDef: ColId Typename ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
SplitColQualList($3, &n->constraints, &n->collClause,
n->fdwoptions = $3;
SplitColQualList($4, &n->constraints, &n->collClause,
yyscanner);
$$ = (Node *)n;
}
...
...
src/backend/parser/parse_utilcmd.c
浏览文件 @
c4096c76
...
...
@@ -559,6 +559,31 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
break
;
}
}
/*
* Generate ALTER FOREIGN TABLE ALTER COLUMN statement which adds
* per-column foreign data wrapper options for this column.
*/
if
(
column
->
fdwoptions
!=
NIL
)
{
AlterTableStmt
*
stmt
;
AlterTableCmd
*
cmd
;
cmd
=
makeNode
(
AlterTableCmd
);
cmd
->
subtype
=
AT_AlterColumnGenericOptions
;
cmd
->
name
=
column
->
colname
;
cmd
->
def
=
(
Node
*
)
column
->
fdwoptions
;
cmd
->
behavior
=
DROP_RESTRICT
;
cmd
->
missing_ok
=
false
;
stmt
=
makeNode
(
AlterTableStmt
);
stmt
->
relation
=
cxt
->
relation
;
stmt
->
cmds
=
NIL
;
stmt
->
relkind
=
OBJECT_FOREIGN_TABLE
;
stmt
->
cmds
=
lappend
(
stmt
->
cmds
,
cmd
);
cxt
->
alist
=
lappend
(
cxt
->
alist
,
stmt
);
}
}
/*
...
...
src/bin/pg_dump/pg_dump.c
浏览文件 @
c4096c76
...
...
@@ -5574,6 +5574,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int
i_attislocal
;
int
i_attoptions
;
int
i_attcollation
;
int
i_attfdwoptions
;
PGresult
*
res
;
int
ntups
;
bool
hasdefaults
;
...
...
@@ -5611,7 +5612,31 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
resetPQExpBuffer
(
q
);
if
(
g_fout
->
remoteVersion
>=
90100
)
if
(
g_fout
->
remoteVersion
>=
90200
)
{
/*
* attfdwoptions is new in 9.2.
*/
appendPQExpBuffer
(
q
,
"SELECT a.attnum, a.attname, a.atttypmod, "
"a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
"array_to_string(ARRAY("
" SELECT option_name || ' ' || quote_literal(option_value) "
" FROM pg_options_to_table(attfdwoptions)), ', ') "
" AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attrelid, a.attnum"
,
tbinfo
->
dobj
.
catId
.
oid
);
}
else
if
(
g_fout
->
remoteVersion
>=
90100
)
{
/*
* attcollation is new in 9.1. Since we only want to dump COLLATE
...
...
@@ -5626,7 +5651,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
...
...
@@ -5643,7 +5669,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation "
"0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
...
...
@@ -5659,7 +5686,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
...
...
@@ -5680,7 +5708,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"false AS attisdropped, a.attlen, "
"a.attalign, false AS attislocal, "
"format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_attribute a LEFT JOIN pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::oid "
...
...
@@ -5698,7 +5727,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"attlen, attalign, "
"false AS attislocal, "
"(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_attribute a "
"WHERE attrelid = '%u'::oid "
"AND attnum > 0::int2 "
...
...
@@ -5726,6 +5756,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attislocal
=
PQfnumber
(
res
,
"attislocal"
);
i_attoptions
=
PQfnumber
(
res
,
"attoptions"
);
i_attcollation
=
PQfnumber
(
res
,
"attcollation"
);
i_attfdwoptions
=
PQfnumber
(
res
,
"attfdwoptions"
);
tbinfo
->
numatts
=
ntups
;
tbinfo
->
attnames
=
(
char
**
)
malloc
(
ntups
*
sizeof
(
char
*
));
...
...
@@ -5742,6 +5773,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo
->
attrdefs
=
(
AttrDefInfo
**
)
malloc
(
ntups
*
sizeof
(
AttrDefInfo
*
));
tbinfo
->
attoptions
=
(
char
**
)
malloc
(
ntups
*
sizeof
(
char
*
));
tbinfo
->
attcollation
=
(
Oid
*
)
malloc
(
ntups
*
sizeof
(
Oid
));
tbinfo
->
attfdwoptions
=
(
char
**
)
malloc
(
ntups
*
sizeof
(
char
*
));
tbinfo
->
inhAttrs
=
(
bool
*
)
malloc
(
ntups
*
sizeof
(
bool
));
tbinfo
->
inhAttrDef
=
(
bool
*
)
malloc
(
ntups
*
sizeof
(
bool
));
tbinfo
->
inhNotNull
=
(
bool
*
)
malloc
(
ntups
*
sizeof
(
bool
));
...
...
@@ -5768,6 +5800,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo
->
notnull
[
j
]
=
(
PQgetvalue
(
res
,
j
,
i_attnotnull
)[
0
]
==
't'
);
tbinfo
->
attoptions
[
j
]
=
strdup
(
PQgetvalue
(
res
,
j
,
i_attoptions
));
tbinfo
->
attcollation
[
j
]
=
atooid
(
PQgetvalue
(
res
,
j
,
i_attcollation
));
tbinfo
->
attfdwoptions
[
j
]
=
strdup
(
PQgetvalue
(
res
,
j
,
i_attfdwoptions
));
tbinfo
->
attrdefs
[
j
]
=
NULL
;
/* fix below */
if
(
PQgetvalue
(
res
,
j
,
i_atthasdef
)[
0
]
==
't'
)
hasdefaults
=
true
;
...
...
@@ -12469,6 +12502,21 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
appendPQExpBuffer
(
q
,
"SET (%s);
\n
"
,
tbinfo
->
attoptions
[
j
]);
}
/*
* Dump per-column fdw options.
*/
if
(
tbinfo
->
relkind
==
RELKIND_FOREIGN_TABLE
&&
tbinfo
->
attfdwoptions
[
j
]
&&
tbinfo
->
attfdwoptions
[
j
][
0
]
!=
'\0'
)
{
appendPQExpBuffer
(
q
,
"ALTER FOREIGN TABLE %s "
,
fmtId
(
tbinfo
->
dobj
.
name
));
appendPQExpBuffer
(
q
,
"ALTER COLUMN %s "
,
fmtId
(
tbinfo
->
attnames
[
j
]));
appendPQExpBuffer
(
q
,
"OPTIONS (%s);
\n
"
,
tbinfo
->
attfdwoptions
[
j
]);
}
}
}
...
...
src/bin/pg_dump/pg_dump.h
浏览文件 @
c4096c76
...
...
@@ -275,6 +275,7 @@ typedef struct _tableInfo
bool
*
attislocal
;
/* true if attr has local definition */
char
**
attoptions
;
/* per-attribute options */
Oid
*
attcollation
;
/* per-attribute collation selection */
char
**
attfdwoptions
;
/* per-attribute fdw options */
/*
* Note: we need to store per-attribute notnull, default, and constraint
...
...
src/bin/psql/describe.c
浏览文件 @
c4096c76
...
...
@@ -1281,7 +1281,12 @@ describeOneTableDetails(const char *schemaname,
res
=
NULL
;
}
/* Get column info */
/*
* Get column info
*
* You need to modify value of "firstvcol" which willbe defined below if
* you are adding column(s) preceding to verbose-only columns.
*/
printfPQExpBuffer
(
&
buf
,
"SELECT a.attname,"
);
appendPQExpBuffer
(
&
buf
,
"
\n
pg_catalog.format_type(a.atttypid, a.atttypmod),"
"
\n
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
...
...
@@ -1295,6 +1300,12 @@ describeOneTableDetails(const char *schemaname,
appendPQExpBuffer
(
&
buf
,
"
\n
NULL AS attcollation"
);
if
(
tableinfo
.
relkind
==
'i'
)
appendPQExpBuffer
(
&
buf
,
",
\n
pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef"
);
else
appendPQExpBuffer
(
&
buf
,
",
\n
NULL AS indexdef"
);
if
(
tableinfo
.
relkind
==
'f'
&&
pset
.
sversion
>=
90200
)
appendPQExpBuffer
(
&
buf
,
",
\n
a.attfdwoptions"
);
else
appendPQExpBuffer
(
&
buf
,
",
\n
NULL AS attfdwoptions"
);
if
(
verbose
)
{
appendPQExpBuffer
(
&
buf
,
",
\n
a.attstorage"
);
...
...
@@ -1386,6 +1397,9 @@ describeOneTableDetails(const char *schemaname,
if
(
tableinfo
.
relkind
==
'i'
)
headers
[
cols
++
]
=
gettext_noop
(
"Definition"
);
if
(
tableinfo
.
relkind
==
'f'
&&
pset
.
sversion
>=
90200
)
headers
[
cols
++
]
=
gettext_noop
(
"Options"
);
if
(
verbose
)
{
headers
[
cols
++
]
=
gettext_noop
(
"Storage"
);
...
...
@@ -1471,10 +1485,14 @@ describeOneTableDetails(const char *schemaname,
if
(
tableinfo
.
relkind
==
'i'
)
printTableAddCell
(
&
cont
,
PQgetvalue
(
res
,
i
,
6
),
false
,
false
);
/* FDW options for foreign table column, only for 9.2 or later */
if
(
tableinfo
.
relkind
==
'f'
&&
pset
.
sversion
>=
90200
)
printTableAddCell
(
&
cont
,
PQgetvalue
(
res
,
i
,
7
),
false
,
false
);
/* Storage and Description */
if
(
verbose
)
{
int
firstvcol
=
(
tableinfo
.
relkind
==
'i'
?
7
:
6
)
;
int
firstvcol
=
8
;
char
*
storage
=
PQgetvalue
(
res
,
i
,
firstvcol
);
/* these strings are literal in our syntax, so not translated. */
...
...
src/include/catalog/catversion.h
浏览文件 @
c4096c76
...
...
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 20110
720
1
#define CATALOG_VERSION_NO 20110
805
1
#endif
src/include/catalog/pg_attribute.h
浏览文件 @
c4096c76
...
...
@@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* Column-level options */
text
attoptions
[
1
];
/* Column-level FDW options */
text
attfdwoptions
[
1
];
}
FormData_pg_attribute
;
/*
...
...
@@ -179,7 +182,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
#define Natts_pg_attribute 2
0
#define Natts_pg_attribute 2
1
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
...
...
@@ -200,6 +203,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_attcollation 18
#define Anum_pg_attribute_attacl 19
#define Anum_pg_attribute_attoptions 20
#define Anum_pg_attribute_attfdwoptions 21
/* ----------------
...
...
src/include/catalog/pg_class.h
浏览文件 @
c4096c76
...
...
@@ -132,7 +132,7 @@ typedef FormData_pg_class *Form_pg_class;
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
DATA
(
insert
OID
=
1247
(
pg_type
PGNSP
71
0
PGUID
0
0
0
0
0
0
0
f
f
p
r
29
0
t
f
f
f
f
3
_null_
_null_
));
DESCR
(
""
);
DATA
(
insert
OID
=
1249
(
pg_attribute
PGNSP
75
0
PGUID
0
0
0
0
0
0
0
f
f
p
r
2
0
0
f
f
f
f
f
3
_null_
_null_
));
DATA
(
insert
OID
=
1249
(
pg_attribute
PGNSP
75
0
PGUID
0
0
0
0
0
0
0
f
f
p
r
2
1
0
f
f
f
f
f
3
_null_
_null_
));
DESCR
(
""
);
DATA
(
insert
OID
=
1255
(
pg_proc
PGNSP
81
0
PGUID
0
0
0
0
0
0
0
f
f
p
r
26
0
t
f
f
f
f
3
_null_
_null_
));
DESCR
(
""
);
...
...
src/include/nodes/parsenodes.h
浏览文件 @
c4096c76
...
...
@@ -500,6 +500,7 @@ typedef struct ColumnDef
CollateClause
*
collClause
;
/* untransformed COLLATE spec, if any */
Oid
collOid
;
/* collation OID (InvalidOid if not set) */
List
*
constraints
;
/* other constraints on column */
List
*
fdwoptions
;
/* per-column FDW options */
}
ColumnDef
;
/*
...
...
@@ -1197,6 +1198,7 @@ typedef enum AlterTableType
AT_DropConstraint
,
/* drop constraint */
AT_DropConstraintRecurse
,
/* internal to commands/tablecmds.c */
AT_AlterColumnType
,
/* alter column type */
AT_AlterColumnGenericOptions
,
/* alter column OPTIONS (...) */
AT_ChangeOwner
,
/* change owner */
AT_ClusterOn
,
/* CLUSTER ON */
AT_DropCluster
,
/* SET WITHOUT CLUSTER */
...
...
src/test/regress/expected/foreign_data.out
浏览文件 @
c4096c76
...
...
@@ -646,19 +646,19 @@ ERROR: syntax error at or near "WITH OIDS"
LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
^
CREATE FOREIGN TABLE ft1 (
c1 integer NOT NULL,
c2 text,
c1 integer
OPTIONS (param1 'val1')
NOT NULL,
c2 text
OPTIONS (param2 'val2', param3 'val3')
,
c3 date
) SERVER sc OPTIONS (delimiter ',', quote '"');
COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
\d+ ft1
Foreign table "public.ft1"
Column | Type | Modifiers | Storage | Description
--------+---------+-----------+----------+-------------
c1 | integer | not null | plain | ft1.c1
c2 | text | | extended |
c3 | date | | plain |
Foreign table "public.ft1"
Column | Type | Modifiers |
Options |
Storage | Description
--------+---------+-----------+----------
-----------------+----------
+-------------
c1 | integer | not null |
{param1=val1} |
plain | ft1.c1
c2 | text | |
{param2=val2,param3=val3} |
extended |
c3 | date | |
|
plain |
Server: sc
Has OIDs: no
...
...
@@ -687,7 +687,7 @@ ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer
OPTIONS (p1 'v1')
;
ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
ERROR: "ft1" is not a table or view
ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
...
...
@@ -698,6 +698,27 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR
ERROR: "ft1" is not a table
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
ALTER FOREIGN TABLE ft1 ALTER COLUMN xmin OPTIONS (ADD p1 'v1'); -- ERROR
ERROR: cannot alter system column "xmin"
ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'),
ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2');
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET p2 'V2', DROP p1);
\d+ ft1
Foreign table "public.ft1"
Column | Type | Modifiers | Options | Storage | Description
--------+---------+-----------+---------------------------+----------+-------------
c1 | integer | not null | {param1=val1} | plain |
c2 | text | | {param2=val2,param3=val3} | extended |
c3 | date | | | plain |
c4 | integer | | | plain |
c6 | integer | not null | | plain |
c7 | integer | | {p1=v1,p2=v2} | plain |
c8 | text | | {p2=V2} | extended |
c9 | integer | | | plain |
c10 | integer | | {p1=v1} | plain |
Server: sc
Has OIDs: no
-- can't change the column type if it's used elsewhere
CREATE TABLE use_ft1_column_type (x ft1);
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR
...
...
@@ -726,17 +747,17 @@ ERROR: relation "ft1" does not exist
ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
\d foreign_schema.foreign_table_1
Foreign table "foreign_schema.foreign_table_1"
Column | Type | Modifiers
------------------+---------+-----------
foreign_column_1 | integer | not null
c2 | text |
c3 | date |
c4 | integer |
c6 | integer | not null
c7 | integer |
c8 | text |
c10 | integer |
Foreign table "foreign_schema.foreign_table_1"
Column | Type | Modifiers
| Options
------------------+---------+-----------
+---------------------------
foreign_column_1 | integer | not null
| {param1=val1}
c2 | text |
| {param2=val2,param3=val3}
c3 | date |
|
c4 | integer |
|
c6 | integer | not null
|
c7 | integer |
| {p1=v1,p2=v2}
c8 | text |
| {p2=V2}
c10 | integer |
| {p1=v1}
Server: sc
-- Information schema
...
...
src/test/regress/sql/foreign_data.sql
浏览文件 @
c4096c76
...
...
@@ -264,8 +264,8 @@ CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
CREATE
FOREIGN
TABLE
ft1
(
c1
serial
)
SERVER
sc
;
-- ERROR
CREATE
FOREIGN
TABLE
ft1
()
SERVER
sc
WITH
OIDS
;
-- ERROR
CREATE
FOREIGN
TABLE
ft1
(
c1
integer
NOT
NULL
,
c2
text
,
c1
integer
OPTIONS
(
param1
'val1'
)
NOT
NULL
,
c2
text
OPTIONS
(
param2
'val2'
,
param3
'val3'
)
,
c3
date
)
SERVER
sc
OPTIONS
(
delimiter
','
,
quote
'"'
);
COMMENT
ON
FOREIGN
TABLE
ft1
IS
'ft1'
;
...
...
@@ -288,7 +288,7 @@ ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
ALTER
FOREIGN
TABLE
ft1
ADD
COLUMN
c7
integer
NOT
NULL
;
ALTER
FOREIGN
TABLE
ft1
ADD
COLUMN
c8
integer
;
ALTER
FOREIGN
TABLE
ft1
ADD
COLUMN
c9
integer
;
ALTER
FOREIGN
TABLE
ft1
ADD
COLUMN
c10
integer
;
ALTER
FOREIGN
TABLE
ft1
ADD
COLUMN
c10
integer
OPTIONS
(
p1
'v1'
)
;
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c4
SET
DEFAULT
0
;
-- ERROR
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c5
DROP
DEFAULT
;
-- ERROR
...
...
@@ -297,6 +297,11 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c8
TYPE
char
(
10
)
USING
'0'
;
-- ERROR
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c8
TYPE
char
(
10
);
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c8
SET
DATA
TYPE
text
;
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
xmin
OPTIONS
(
ADD
p1
'v1'
);
-- ERROR
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c7
OPTIONS
(
ADD
p1
'v1'
,
ADD
p2
'v2'
),
ALTER
COLUMN
c8
OPTIONS
(
ADD
p1
'v1'
,
ADD
p2
'v2'
);
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c8
OPTIONS
(
SET
p2
'V2'
,
DROP
p1
);
\
d
+
ft1
-- can't change the column type if it's used elsewhere
CREATE
TABLE
use_ft1_column_type
(
x
ft1
);
ALTER
FOREIGN
TABLE
ft1
ALTER
COLUMN
c8
SET
DATA
TYPE
integer
;
-- ERROR
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录