mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Avoid using unsafe search_path settings during dump and restore.
Historically, pg_dump has "set search_path = foo, pg_catalog" when dumping an object in schema "foo", and has also caused that setting to be used while restoring the object. This is problematic because functions and operators in schema "foo" could capture references meant to refer to pg_catalog entries, both in the queries issued by pg_dump and those issued during the subsequent restore run. That could result in dump/restore misbehavior, or in privilege escalation if a nefarious user installs trojan-horse functions or operators. This patch changes pg_dump so that it does not change the search_path dynamically. The emitted restore script sets the search_path to what was used at dump time, and then leaves it alone thereafter. Created objects are placed in the correct schema, regardless of the active search_path, by dint of schema-qualifying their names in the CREATE commands, as well as in subsequent ALTER and ALTER-like commands. Since this change requires a change in the behavior of pg_restore when processing an archive file made according to this new convention, bump the archive file version number; old versions of pg_restore will therefore refuse to process files made with new versions of pg_dump. Security: CVE-2018-1058
This commit is contained in:
@ -75,15 +75,17 @@
|
|||||||
#define PRETTYINDENT_LIMIT 40 /* wrap limit */
|
#define PRETTYINDENT_LIMIT 40 /* wrap limit */
|
||||||
|
|
||||||
/* Pretty flags */
|
/* Pretty flags */
|
||||||
#define PRETTYFLAG_PAREN 1
|
#define PRETTYFLAG_PAREN 0x0001
|
||||||
#define PRETTYFLAG_INDENT 2
|
#define PRETTYFLAG_INDENT 0x0002
|
||||||
|
#define PRETTYFLAG_SCHEMA 0x0004
|
||||||
|
|
||||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||||
#define WRAP_COLUMN_DEFAULT 0
|
#define WRAP_COLUMN_DEFAULT 0
|
||||||
|
|
||||||
/* macro to test if pretty action needed */
|
/* macros to test if pretty action needed */
|
||||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||||
|
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -456,7 +458,8 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS)
|
|||||||
bool pretty = PG_GETARG_BOOL(1);
|
bool pretty = PG_GETARG_BOOL(1);
|
||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags)));
|
PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +558,8 @@ pg_get_viewdef_ext(PG_FUNCTION_ARGS)
|
|||||||
bool pretty = PG_GETARG_BOOL(1);
|
bool pretty = PG_GETARG_BOOL(1);
|
||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT)));
|
PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +572,8 @@ pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
|
|||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
/* calling this implies we want pretty printing */
|
/* calling this implies we want pretty printing */
|
||||||
prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT;
|
prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, wrap)));
|
PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, wrap)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +606,7 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
|
|||||||
RangeVar *viewrel;
|
RangeVar *viewrel;
|
||||||
Oid viewoid;
|
Oid viewoid;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
/* Look up view name. Can't lock it - we might not have privileges. */
|
/* Look up view name. Can't lock it - we might not have privileges. */
|
||||||
viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
|
viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
|
||||||
@ -805,8 +810,15 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
|
|||||||
appendStringInfo(&buf, " TRUNCATE");
|
appendStringInfo(&buf, " TRUNCATE");
|
||||||
findx++;
|
findx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In non-pretty mode, always schema-qualify the target table name for
|
||||||
|
* safety. In pretty mode, schema-qualify only if not visible.
|
||||||
|
*/
|
||||||
appendStringInfo(&buf, " ON %s ",
|
appendStringInfo(&buf, " ON %s ",
|
||||||
generate_relation_name(trigrec->tgrelid, NIL));
|
pretty ?
|
||||||
|
generate_relation_name(trigrec->tgrelid, NIL) :
|
||||||
|
generate_qualified_relation_name(trigrec->tgrelid));
|
||||||
|
|
||||||
if (OidIsValid(trigrec->tgconstraint))
|
if (OidIsValid(trigrec->tgconstraint))
|
||||||
{
|
{
|
||||||
@ -879,7 +891,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
|
|||||||
context.windowClause = NIL;
|
context.windowClause = NIL;
|
||||||
context.windowTList = NIL;
|
context.windowTList = NIL;
|
||||||
context.varprefix = true;
|
context.varprefix = true;
|
||||||
context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
context.prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
context.wrapColumn = WRAP_COLUMN_DEFAULT;
|
context.wrapColumn = WRAP_COLUMN_DEFAULT;
|
||||||
context.indentLevel = PRETTYINDENT_STD;
|
context.indentLevel = PRETTYINDENT_STD;
|
||||||
|
|
||||||
@ -959,7 +971,8 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
|
|||||||
bool pretty = PG_GETARG_BOOL(2);
|
bool pretty = PG_GETARG_BOOL(2);
|
||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
|
PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
|
||||||
NULL,
|
NULL,
|
||||||
colno != 0,
|
colno != 0,
|
||||||
@ -984,7 +997,8 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty)
|
|||||||
{
|
{
|
||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags);
|
return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1105,7 +1119,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
|
|||||||
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
|
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
|
||||||
idxrec->indisunique ? "UNIQUE " : "",
|
idxrec->indisunique ? "UNIQUE " : "",
|
||||||
quote_identifier(NameStr(idxrelrec->relname)),
|
quote_identifier(NameStr(idxrelrec->relname)),
|
||||||
generate_relation_name(indrelid, NIL),
|
(prettyFlags & PRETTYFLAG_SCHEMA) ?
|
||||||
|
generate_relation_name(indrelid, NIL) :
|
||||||
|
generate_qualified_relation_name(indrelid),
|
||||||
quote_identifier(NameStr(amrec->amname)));
|
quote_identifier(NameStr(amrec->amname)));
|
||||||
else /* currently, must be EXCLUDE constraint */
|
else /* currently, must be EXCLUDE constraint */
|
||||||
appendStringInfo(&buf, "EXCLUDE USING %s (",
|
appendStringInfo(&buf, "EXCLUDE USING %s (",
|
||||||
@ -1297,7 +1313,8 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
|
|||||||
bool pretty = PG_GETARG_BOOL(1);
|
bool pretty = PG_GETARG_BOOL(1);
|
||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
|
PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
|
||||||
false,
|
false,
|
||||||
prettyFlags)));
|
prettyFlags)));
|
||||||
@ -1698,7 +1715,7 @@ pg_get_expr_ext(PG_FUNCTION_ARGS)
|
|||||||
int prettyFlags;
|
int prettyFlags;
|
||||||
char *relname;
|
char *relname;
|
||||||
|
|
||||||
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
|
prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||||
|
|
||||||
if (OidIsValid(relid))
|
if (OidIsValid(relid))
|
||||||
{
|
{
|
||||||
@ -3970,7 +3987,11 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The relation the rule is fired on */
|
/* The relation the rule is fired on */
|
||||||
appendStringInfo(buf, " TO %s", generate_relation_name(ev_class, NIL));
|
appendStringInfo(buf, " TO %s",
|
||||||
|
(prettyFlags & PRETTYFLAG_SCHEMA) ?
|
||||||
|
generate_relation_name(ev_class, NIL) :
|
||||||
|
generate_qualified_relation_name(ev_class));
|
||||||
|
|
||||||
if (ev_attr > 0)
|
if (ev_attr > 0)
|
||||||
appendStringInfo(buf, ".%s",
|
appendStringInfo(buf, ".%s",
|
||||||
quote_identifier(get_relid_attribute_name(ev_class,
|
quote_identifier(get_relid_attribute_name(ev_class,
|
||||||
|
@ -718,6 +718,7 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
|
|||||||
*
|
*
|
||||||
* name: the object name, in the form to use in the commands (already quoted)
|
* name: the object name, in the form to use in the commands (already quoted)
|
||||||
* subname: the sub-object name, if any (already quoted); NULL if none
|
* subname: the sub-object name, if any (already quoted); NULL if none
|
||||||
|
* nspname: the namespace the object is in (NULL if none); not pre-quoted
|
||||||
* type: the object type (as seen in GRANT command: must be one of
|
* type: the object type (as seen in GRANT command: must be one of
|
||||||
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
|
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
|
||||||
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
|
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
|
||||||
@ -737,7 +738,7 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
|
|||||||
* since this routine uses fmtId() internally.
|
* since this routine uses fmtId() internally.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
buildACLCommands(const char *name, const char *subname,
|
buildACLCommands(const char *name, const char *subname, const char *nspname,
|
||||||
const char *type, const char *acls, const char *owner,
|
const char *type, const char *acls, const char *owner,
|
||||||
const char *prefix, int remoteVersion,
|
const char *prefix, int remoteVersion,
|
||||||
PQExpBuffer sql)
|
PQExpBuffer sql)
|
||||||
@ -791,7 +792,10 @@ buildACLCommands(const char *name, const char *subname,
|
|||||||
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
||||||
if (subname)
|
if (subname)
|
||||||
appendPQExpBuffer(firstsql, "(%s)", subname);
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
||||||
appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
|
appendPQExpBuffer(firstsql, " ON %s ", type);
|
||||||
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We still need some hacking though to cover the case where new default
|
* We still need some hacking though to cover the case where new default
|
||||||
@ -839,18 +843,33 @@ buildACLCommands(const char *name, const char *subname,
|
|||||||
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
||||||
if (subname)
|
if (subname)
|
||||||
appendPQExpBuffer(firstsql, "(%s)", subname);
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
||||||
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
|
appendPQExpBuffer(firstsql, " ON %s ", type);
|
||||||
type, name, fmtId(grantee->data));
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(firstsql, "%s FROM %s;\n",
|
||||||
|
name, fmtId(grantee->data));
|
||||||
if (privs->len > 0)
|
if (privs->len > 0)
|
||||||
|
{
|
||||||
appendPQExpBuffer(firstsql,
|
appendPQExpBuffer(firstsql,
|
||||||
"%sGRANT %s ON %s %s TO %s;\n",
|
"%sGRANT %s ON %s ",
|
||||||
prefix, privs->data, type, name,
|
prefix, privs->data, type);
|
||||||
fmtId(grantee->data));
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(firstsql,
|
||||||
|
"%s TO %s;\n",
|
||||||
|
name, fmtId(grantee->data));
|
||||||
|
}
|
||||||
if (privswgo->len > 0)
|
if (privswgo->len > 0)
|
||||||
|
{
|
||||||
appendPQExpBuffer(firstsql,
|
appendPQExpBuffer(firstsql,
|
||||||
"%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
|
"%sGRANT %s ON %s ",
|
||||||
prefix, privswgo->data, type, name,
|
prefix, privswgo->data, type);
|
||||||
fmtId(grantee->data));
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(firstsql,
|
||||||
|
"%s TO %s WITH GRANT OPTION;\n",
|
||||||
|
name, fmtId(grantee->data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -865,8 +884,11 @@ buildACLCommands(const char *name, const char *subname,
|
|||||||
|
|
||||||
if (privs->len > 0)
|
if (privs->len > 0)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
|
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
|
||||||
prefix, privs->data, type, name);
|
prefix, privs->data, type);
|
||||||
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(secondsql, "%s TO ", name);
|
||||||
if (grantee->len == 0)
|
if (grantee->len == 0)
|
||||||
appendPQExpBuffer(secondsql, "PUBLIC;\n");
|
appendPQExpBuffer(secondsql, "PUBLIC;\n");
|
||||||
else if (strncmp(grantee->data, "group ",
|
else if (strncmp(grantee->data, "group ",
|
||||||
@ -878,8 +900,11 @@ buildACLCommands(const char *name, const char *subname,
|
|||||||
}
|
}
|
||||||
if (privswgo->len > 0)
|
if (privswgo->len > 0)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
|
appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
|
||||||
prefix, privswgo->data, type, name);
|
prefix, privswgo->data, type);
|
||||||
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(secondsql, "%s TO ", name);
|
||||||
if (grantee->len == 0)
|
if (grantee->len == 0)
|
||||||
appendPQExpBuffer(secondsql, "PUBLIC");
|
appendPQExpBuffer(secondsql, "PUBLIC");
|
||||||
else if (strncmp(grantee->data, "group ",
|
else if (strncmp(grantee->data, "group ",
|
||||||
@ -906,8 +931,11 @@ buildACLCommands(const char *name, const char *subname,
|
|||||||
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
|
||||||
if (subname)
|
if (subname)
|
||||||
appendPQExpBuffer(firstsql, "(%s)", subname);
|
appendPQExpBuffer(firstsql, "(%s)", subname);
|
||||||
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
|
appendPQExpBuffer(firstsql, " ON %s ", type);
|
||||||
type, name, fmtId(owner));
|
if (nspname && *nspname)
|
||||||
|
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(firstsql, "%s FROM %s;\n",
|
||||||
|
name, fmtId(owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyPQExpBuffer(grantee);
|
destroyPQExpBuffer(grantee);
|
||||||
@ -958,7 +986,7 @@ buildDefaultACLCommands(const char *type, const char *nspname,
|
|||||||
if (nspname)
|
if (nspname)
|
||||||
appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
|
appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
|
||||||
|
|
||||||
result = buildACLCommands("", NULL,
|
result = buildACLCommands("", NULL, NULL,
|
||||||
type, acls, owner,
|
type, acls, owner,
|
||||||
prefix->data, remoteVersion,
|
prefix->data, remoteVersion,
|
||||||
sql);
|
sql);
|
||||||
@ -1412,26 +1440,32 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
|
|||||||
* buildShSecLabelQuery
|
* buildShSecLabelQuery
|
||||||
*
|
*
|
||||||
* Build a query to retrieve security labels for a shared object.
|
* Build a query to retrieve security labels for a shared object.
|
||||||
|
* The object is identified by its OID plus the name of the catalog
|
||||||
|
* it can be found in (e.g., "pg_database" for database names).
|
||||||
|
* The query is appended to "sql". (We don't execute it here so as to
|
||||||
|
* keep this file free of assumptions about how to deal with SQL errors.)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId,
|
buildShSecLabelQuery(PGconn *conn, const char *catalog_name, Oid objectId,
|
||||||
PQExpBuffer sql)
|
PQExpBuffer sql)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(sql,
|
appendPQExpBuffer(sql,
|
||||||
"SELECT provider, label FROM pg_catalog.pg_shseclabel "
|
"SELECT provider, label FROM pg_catalog.pg_shseclabel "
|
||||||
"WHERE classoid = '%s'::pg_catalog.regclass AND "
|
"WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
|
||||||
"objoid = %u", catalog_name, objectId);
|
"AND objoid = '%u'", catalog_name, objectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* emitShSecLabels
|
* emitShSecLabels
|
||||||
*
|
*
|
||||||
* Format security label data retrieved by the query generated in
|
* Construct SECURITY LABEL commands using the data retrieved by the query
|
||||||
* buildShSecLabelQuery.
|
* generated by buildShSecLabelQuery, and append them to "buffer".
|
||||||
|
* Here, the target object is identified by its type name (e.g. "DATABASE")
|
||||||
|
* and its name (not pre-quoted).
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
|
emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
|
||||||
const char *target, const char *objname)
|
const char *objtype, const char *objname)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -1443,7 +1477,7 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
|
|||||||
/* must use fmtId result before calling it again */
|
/* must use fmtId result before calling it again */
|
||||||
appendPQExpBuffer(buffer,
|
appendPQExpBuffer(buffer,
|
||||||
"SECURITY LABEL FOR %s ON %s",
|
"SECURITY LABEL FOR %s ON %s",
|
||||||
fmtId(provider), target);
|
fmtId(provider), objtype);
|
||||||
appendPQExpBuffer(buffer,
|
appendPQExpBuffer(buffer,
|
||||||
" %s IS ",
|
" %s IS ",
|
||||||
fmtId(objname));
|
fmtId(objname));
|
||||||
|
@ -53,7 +53,7 @@ extern void appendShellString(PQExpBuffer buf, const char *str);
|
|||||||
extern void appendConnStrVal(PQExpBuffer buf, const char *str);
|
extern void appendConnStrVal(PQExpBuffer buf, const char *str);
|
||||||
extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
|
extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
|
||||||
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
|
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
|
||||||
extern bool buildACLCommands(const char *name, const char *subname,
|
extern bool buildACLCommands(const char *name, const char *subname, const char *nspname,
|
||||||
const char *type, const char *acls, const char *owner,
|
const char *type, const char *acls, const char *owner,
|
||||||
const char *prefix, int remoteVersion,
|
const char *prefix, int remoteVersion,
|
||||||
PQExpBuffer sql);
|
PQExpBuffer sql);
|
||||||
@ -67,9 +67,9 @@ extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
|
|||||||
const char *schemavar, const char *namevar,
|
const char *schemavar, const char *namevar,
|
||||||
const char *altnamevar, const char *visibilityrule);
|
const char *altnamevar, const char *visibilityrule);
|
||||||
extern void buildShSecLabelQuery(PGconn *conn, const char *catalog_name,
|
extern void buildShSecLabelQuery(PGconn *conn, const char *catalog_name,
|
||||||
uint32 objectId, PQExpBuffer sql);
|
Oid objectId, PQExpBuffer sql);
|
||||||
extern void emitShSecLabels(PGconn *conn, PGresult *res,
|
extern void emitShSecLabels(PGconn *conn, PGresult *res,
|
||||||
PQExpBuffer buffer, const char *target, const char *objname);
|
PQExpBuffer buffer, const char *objtype, const char *objname);
|
||||||
extern void set_dump_section(const char *arg, int *dumpSections);
|
extern void set_dump_section(const char *arg, int *dumpSections);
|
||||||
|
|
||||||
extern void simple_string_list_append(SimpleStringList *list, const char *val);
|
extern void simple_string_list_append(SimpleStringList *list, const char *val);
|
||||||
|
@ -89,6 +89,9 @@ struct Archive
|
|||||||
/* info needed for string escaping */
|
/* info needed for string escaping */
|
||||||
int encoding; /* libpq code for client_encoding */
|
int encoding; /* libpq code for client_encoding */
|
||||||
bool std_strings; /* standard_conforming_strings */
|
bool std_strings; /* standard_conforming_strings */
|
||||||
|
|
||||||
|
/* other important stuff */
|
||||||
|
char *searchpath; /* search_path to set during restore */
|
||||||
char *use_role; /* Issue SET ROLE to this */
|
char *use_role; /* Issue SET ROLE to this */
|
||||||
|
|
||||||
/* error handling */
|
/* error handling */
|
||||||
|
@ -67,6 +67,7 @@ static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
|
|||||||
static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
|
static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
|
||||||
static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
|
static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
|
||||||
static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
|
static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
|
||||||
|
static void processSearchPathEntry(ArchiveHandle *AH, TocEntry *te);
|
||||||
static teReqs _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt);
|
static teReqs _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt);
|
||||||
static RestorePass _tocEntryRestorePass(TocEntry *te);
|
static RestorePass _tocEntryRestorePass(TocEntry *te);
|
||||||
static bool _tocEntryIsACL(TocEntry *te);
|
static bool _tocEntryIsACL(TocEntry *te);
|
||||||
@ -706,7 +707,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
|
|||||||
ahprintf(AH, "TRUNCATE TABLE %s%s;\n\n",
|
ahprintf(AH, "TRUNCATE TABLE %s%s;\n\n",
|
||||||
(PQserverVersion(AH->connection) >= 80400 ?
|
(PQserverVersion(AH->connection) >= 80400 ?
|
||||||
"ONLY " : ""),
|
"ONLY " : ""),
|
||||||
fmtId(te->tag));
|
fmtQualifiedId(PQserverVersion(AH->connection),
|
||||||
|
te->namespace,
|
||||||
|
te->tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -791,10 +794,10 @@ _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *rop
|
|||||||
/*
|
/*
|
||||||
* Disable them.
|
* Disable them.
|
||||||
*/
|
*/
|
||||||
_selectOutputSchema(AH, te->namespace);
|
|
||||||
|
|
||||||
ahprintf(AH, "ALTER TABLE %s DISABLE TRIGGER ALL;\n\n",
|
ahprintf(AH, "ALTER TABLE %s DISABLE TRIGGER ALL;\n\n",
|
||||||
fmtId(te->tag));
|
fmtQualifiedId(PQserverVersion(AH->connection),
|
||||||
|
te->namespace,
|
||||||
|
te->tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -817,10 +820,10 @@ _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt
|
|||||||
/*
|
/*
|
||||||
* Enable them.
|
* Enable them.
|
||||||
*/
|
*/
|
||||||
_selectOutputSchema(AH, te->namespace);
|
|
||||||
|
|
||||||
ahprintf(AH, "ALTER TABLE %s ENABLE TRIGGER ALL;\n\n",
|
ahprintf(AH, "ALTER TABLE %s ENABLE TRIGGER ALL;\n\n",
|
||||||
fmtId(te->tag));
|
fmtQualifiedId(PQserverVersion(AH->connection),
|
||||||
|
te->namespace,
|
||||||
|
te->tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2450,6 +2453,8 @@ ReadToc(ArchiveHandle *AH)
|
|||||||
processEncodingEntry(AH, te);
|
processEncodingEntry(AH, te);
|
||||||
else if (strcmp(te->desc, "STDSTRINGS") == 0)
|
else if (strcmp(te->desc, "STDSTRINGS") == 0)
|
||||||
processStdStringsEntry(AH, te);
|
processStdStringsEntry(AH, te);
|
||||||
|
else if (strcmp(te->desc, "SEARCHPATH") == 0)
|
||||||
|
processSearchPathEntry(AH, te);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2497,14 +2502,25 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
|
|||||||
te->defn);
|
te->defn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
processSearchPathEntry(ArchiveHandle *AH, TocEntry *te)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* te->defn should contain a command to set search_path. We just copy it
|
||||||
|
* verbatim for use later.
|
||||||
|
*/
|
||||||
|
AH->public.searchpath = pg_strdup(te->defn);
|
||||||
|
}
|
||||||
|
|
||||||
static teReqs
|
static teReqs
|
||||||
_tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt)
|
_tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt)
|
||||||
{
|
{
|
||||||
teReqs res = REQ_SCHEMA | REQ_DATA;
|
teReqs res = REQ_SCHEMA | REQ_DATA;
|
||||||
|
|
||||||
/* ENCODING and STDSTRINGS items are treated specially */
|
/* These items are treated specially */
|
||||||
if (strcmp(te->desc, "ENCODING") == 0 ||
|
if (strcmp(te->desc, "ENCODING") == 0 ||
|
||||||
strcmp(te->desc, "STDSTRINGS") == 0)
|
strcmp(te->desc, "STDSTRINGS") == 0 ||
|
||||||
|
strcmp(te->desc, "SEARCHPATH") == 0)
|
||||||
return REQ_SPECIAL;
|
return REQ_SPECIAL;
|
||||||
|
|
||||||
/* If it's an ACL, maybe ignore it */
|
/* If it's an ACL, maybe ignore it */
|
||||||
@ -2708,6 +2724,10 @@ _doSetFixedOutputState(ArchiveHandle *AH)
|
|||||||
if (AH->ropt && AH->ropt->use_role)
|
if (AH->ropt && AH->ropt->use_role)
|
||||||
ahprintf(AH, "SET ROLE %s;\n", fmtId(AH->ropt->use_role));
|
ahprintf(AH, "SET ROLE %s;\n", fmtId(AH->ropt->use_role));
|
||||||
|
|
||||||
|
/* Select the dump-time search_path */
|
||||||
|
if (AH->public.searchpath)
|
||||||
|
ahprintf(AH, "%s", AH->public.searchpath);
|
||||||
|
|
||||||
/* Make sure function checking is disabled */
|
/* Make sure function checking is disabled */
|
||||||
ahprintf(AH, "SET check_function_bodies = false;\n");
|
ahprintf(AH, "SET check_function_bodies = false;\n");
|
||||||
|
|
||||||
@ -2904,6 +2924,15 @@ _selectOutputSchema(ArchiveHandle *AH, const char *schemaName)
|
|||||||
{
|
{
|
||||||
PQExpBuffer qry;
|
PQExpBuffer qry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there was a SEARCHPATH TOC entry, we're supposed to just stay with
|
||||||
|
* that search_path rather than switching to entry-specific paths.
|
||||||
|
* Otherwise, it's an old archive that will not restore correctly unless
|
||||||
|
* we set the search_path as it's expecting.
|
||||||
|
*/
|
||||||
|
if (AH->public.searchpath)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!schemaName || *schemaName == '\0' ||
|
if (!schemaName || *schemaName == '\0' ||
|
||||||
(AH->currSchema && strcmp(AH->currSchema, schemaName) == 0))
|
(AH->currSchema && strcmp(AH->currSchema, schemaName) == 0))
|
||||||
return; /* no need to do anything */
|
return; /* no need to do anything */
|
||||||
|
@ -64,7 +64,7 @@ typedef z_stream *z_streamp;
|
|||||||
|
|
||||||
/* Current archive version number (the format we can output) */
|
/* Current archive version number (the format we can output) */
|
||||||
#define K_VERS_MAJOR 1
|
#define K_VERS_MAJOR 1
|
||||||
#define K_VERS_MINOR 12
|
#define K_VERS_MINOR 13
|
||||||
#define K_VERS_REV 0
|
#define K_VERS_REV 0
|
||||||
|
|
||||||
/* Data block types */
|
/* Data block types */
|
||||||
@ -90,9 +90,11 @@ typedef z_stream *z_streamp;
|
|||||||
* indicator */
|
* indicator */
|
||||||
#define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB
|
#define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB
|
||||||
* entries */
|
* entries */
|
||||||
|
#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* change search_path
|
||||||
|
* behavior */
|
||||||
|
|
||||||
/* Newest format we can read */
|
/* Newest format we can read */
|
||||||
#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0)
|
#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0)
|
||||||
|
|
||||||
|
|
||||||
/* Flags to indicate disposition of offsets stored in files */
|
/* Flags to indicate disposition of offsets stored in files */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -51,9 +51,10 @@ static void dumpDatabases(PGconn *conn);
|
|||||||
static void dumpTimestamp(char *msg);
|
static void dumpTimestamp(char *msg);
|
||||||
|
|
||||||
static int runPgDump(const char *dbname);
|
static int runPgDump(const char *dbname);
|
||||||
static void buildShSecLabels(PGconn *conn, const char *catalog_name,
|
static void buildShSecLabels(PGconn *conn,
|
||||||
uint32 objectId, PQExpBuffer buffer,
|
const char *catalog_name, Oid objectId,
|
||||||
const char *target, const char *objname);
|
const char *objtype, const char *objname,
|
||||||
|
PQExpBuffer buffer);
|
||||||
static PGconn *connectDatabase(const char *dbname, const char *connstr, const char *pghost, const char *pgport,
|
static PGconn *connectDatabase(const char *dbname, const char *connstr, const char *pghost, const char *pgport,
|
||||||
const char *pguser, enum trivalue prompt_password, bool fail_on_error);
|
const char *pguser, enum trivalue prompt_password, bool fail_on_error);
|
||||||
static char *constructConnStr(const char **keywords, const char **values);
|
static char *constructConnStr(const char **keywords, const char **values);
|
||||||
@ -823,7 +824,8 @@ dumpRoles(PGconn *conn)
|
|||||||
|
|
||||||
if (!no_security_labels && server_version >= 90200)
|
if (!no_security_labels && server_version >= 90200)
|
||||||
buildShSecLabels(conn, "pg_authid", auth_oid,
|
buildShSecLabels(conn, "pg_authid", auth_oid,
|
||||||
buf, "ROLE", rolename);
|
"ROLE", rolename,
|
||||||
|
buf);
|
||||||
|
|
||||||
fprintf(OPF, "%s", buf->data);
|
fprintf(OPF, "%s", buf->data);
|
||||||
}
|
}
|
||||||
@ -1056,7 +1058,7 @@ dumpTablespaces(PGconn *conn)
|
|||||||
for (i = 0; i < PQntuples(res); i++)
|
for (i = 0; i < PQntuples(res); i++)
|
||||||
{
|
{
|
||||||
PQExpBuffer buf = createPQExpBuffer();
|
PQExpBuffer buf = createPQExpBuffer();
|
||||||
uint32 spcoid = atooid(PQgetvalue(res, i, 0));
|
Oid spcoid = atooid(PQgetvalue(res, i, 0));
|
||||||
char *spcname = PQgetvalue(res, i, 1);
|
char *spcname = PQgetvalue(res, i, 1);
|
||||||
char *spcowner = PQgetvalue(res, i, 2);
|
char *spcowner = PQgetvalue(res, i, 2);
|
||||||
char *spclocation = PQgetvalue(res, i, 3);
|
char *spclocation = PQgetvalue(res, i, 3);
|
||||||
@ -1080,11 +1082,12 @@ dumpTablespaces(PGconn *conn)
|
|||||||
fspcname, spcoptions);
|
fspcname, spcoptions);
|
||||||
|
|
||||||
if (!skip_acls &&
|
if (!skip_acls &&
|
||||||
!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
|
!buildACLCommands(fspcname, NULL, NULL, "TABLESPACE",
|
||||||
|
spcacl, spcowner,
|
||||||
"", server_version, buf))
|
"", server_version, buf))
|
||||||
{
|
{
|
||||||
fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
|
fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
|
||||||
progname, spcacl, fspcname);
|
progname, spcacl, spcname);
|
||||||
PQfinish(conn);
|
PQfinish(conn);
|
||||||
exit_nicely(1);
|
exit_nicely(1);
|
||||||
}
|
}
|
||||||
@ -1098,7 +1101,8 @@ dumpTablespaces(PGconn *conn)
|
|||||||
|
|
||||||
if (!no_security_labels && server_version >= 90200)
|
if (!no_security_labels && server_version >= 90200)
|
||||||
buildShSecLabels(conn, "pg_tablespace", spcoid,
|
buildShSecLabels(conn, "pg_tablespace", spcoid,
|
||||||
buf, "TABLESPACE", fspcname);
|
"TABLESPACE", spcname,
|
||||||
|
buf);
|
||||||
|
|
||||||
fprintf(OPF, "%s", buf->data);
|
fprintf(OPF, "%s", buf->data);
|
||||||
|
|
||||||
@ -1413,7 +1417,7 @@ dumpCreateDB(PGconn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skip_acls &&
|
if (!skip_acls &&
|
||||||
!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
|
!buildACLCommands(fdbname, NULL, NULL, "DATABASE", dbacl, dbowner,
|
||||||
"", server_version, buf))
|
"", server_version, buf))
|
||||||
{
|
{
|
||||||
fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
|
fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
|
||||||
@ -1727,19 +1731,23 @@ runPgDump(const char *dbname)
|
|||||||
*
|
*
|
||||||
* Build SECURITY LABEL command(s) for an shared object
|
* Build SECURITY LABEL command(s) for an shared object
|
||||||
*
|
*
|
||||||
* The caller has to provide object type and identifier to select security
|
* The caller has to provide object type and identity in two separate formats:
|
||||||
* labels from pg_seclabels system view.
|
* catalog_name (e.g., "pg_database") and object OID, as well as
|
||||||
|
* type name (e.g., "DATABASE") and object name (not pre-quoted).
|
||||||
|
*
|
||||||
|
* The command(s) are appended to "buffer".
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
buildShSecLabels(PGconn *conn, const char *catalog_name, uint32 objectId,
|
buildShSecLabels(PGconn *conn, const char *catalog_name, Oid objectId,
|
||||||
PQExpBuffer buffer, const char *target, const char *objname)
|
const char *objtype, const char *objname,
|
||||||
|
PQExpBuffer buffer)
|
||||||
{
|
{
|
||||||
PQExpBuffer sql = createPQExpBuffer();
|
PQExpBuffer sql = createPQExpBuffer();
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
buildShSecLabelQuery(conn, catalog_name, objectId, sql);
|
buildShSecLabelQuery(conn, catalog_name, objectId, sql);
|
||||||
res = executeQuery(conn, sql->data);
|
res = executeQuery(conn, sql->data);
|
||||||
emitShSecLabels(conn, res, buffer, target, objname);
|
emitShSecLabels(conn, res, buffer, objtype, objname);
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
destroyPQExpBuffer(sql);
|
destroyPQExpBuffer(sql);
|
||||||
|
@ -944,11 +944,11 @@ LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C...
|
|||||||
^
|
^
|
||||||
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
|
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
|
||||||
relname | pg_get_indexdef
|
relname | pg_get_indexdef
|
||||||
--------------------+-----------------------------------------------------------------------------------------------------
|
--------------------+------------------------------------------------------------------------------------------------------------
|
||||||
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b)
|
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON public.collate_test1 USING btree (b)
|
||||||
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "C")
|
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON public.collate_test1 USING btree (b COLLATE "C")
|
||||||
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (b COLLATE "C")
|
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON public.collate_test1 USING btree (b COLLATE "C")
|
||||||
collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
|
collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON public.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
-- schema manipulation commands
|
-- schema manipulation commands
|
||||||
|
@ -573,11 +573,11 @@ LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "P...
|
|||||||
^
|
^
|
||||||
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
|
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
|
||||||
relname | pg_get_indexdef
|
relname | pg_get_indexdef
|
||||||
--------------------+-----------------------------------------------------------------------------------------------------
|
--------------------+-------------------------------------------------------------------------------------------------------------------
|
||||||
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b)
|
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b)
|
||||||
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "POSIX")
|
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "POSIX")
|
||||||
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (b COLLATE "POSIX")
|
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "POSIX")
|
||||||
collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
|
collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
-- foreign keys
|
-- foreign keys
|
||||||
|
@ -2146,105 +2146,105 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
|||||||
SELECT tablename, rulename, definition FROM pg_rules
|
SELECT tablename, rulename, definition FROM pg_rules
|
||||||
ORDER BY tablename, rulename;
|
ORDER BY tablename, rulename;
|
||||||
tablename | rulename | definition
|
tablename | rulename | definition
|
||||||
---------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------+-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
pg_settings | pg_settings_n | CREATE RULE pg_settings_n AS +
|
pg_settings | pg_settings_n | CREATE RULE pg_settings_n AS +
|
||||||
| | ON UPDATE TO pg_settings DO INSTEAD NOTHING;
|
| | ON UPDATE TO pg_catalog.pg_settings DO INSTEAD NOTHING;
|
||||||
pg_settings | pg_settings_u | CREATE RULE pg_settings_u AS +
|
pg_settings | pg_settings_u | CREATE RULE pg_settings_u AS +
|
||||||
| | ON UPDATE TO pg_settings +
|
| | ON UPDATE TO pg_catalog.pg_settings +
|
||||||
| | WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config;
|
| | WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config;
|
||||||
rtest_emp | rtest_emp_del | CREATE RULE rtest_emp_del AS +
|
rtest_emp | rtest_emp_del | CREATE RULE rtest_emp_del AS +
|
||||||
| | ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
| | ON DELETE TO public.rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
||||||
| | VALUES (old.ename, "current_user"(), 'fired'::bpchar, '$0.00'::money, old.salary);
|
| | VALUES (old.ename, "current_user"(), 'fired'::bpchar, '$0.00'::money, old.salary);
|
||||||
rtest_emp | rtest_emp_ins | CREATE RULE rtest_emp_ins AS +
|
rtest_emp | rtest_emp_ins | CREATE RULE rtest_emp_ins AS +
|
||||||
| | ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
| | ON INSERT TO public.rtest_emp DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
||||||
| | VALUES (new.ename, "current_user"(), 'hired'::bpchar, new.salary, '$0.00'::money);
|
| | VALUES (new.ename, "current_user"(), 'hired'::bpchar, new.salary, '$0.00'::money);
|
||||||
rtest_emp | rtest_emp_upd | CREATE RULE rtest_emp_upd AS +
|
rtest_emp | rtest_emp_upd | CREATE RULE rtest_emp_upd AS +
|
||||||
| | ON UPDATE TO rtest_emp +
|
| | ON UPDATE TO public.rtest_emp +
|
||||||
| | WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
| | WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, action, newsal, oldsal) +
|
||||||
| | VALUES (new.ename, "current_user"(), 'honored'::bpchar, new.salary, old.salary);
|
| | VALUES (new.ename, "current_user"(), 'honored'::bpchar, new.salary, old.salary);
|
||||||
rtest_nothn1 | rtest_nothn_r1 | CREATE RULE rtest_nothn_r1 AS +
|
rtest_nothn1 | rtest_nothn_r1 | CREATE RULE rtest_nothn_r1 AS +
|
||||||
| | ON INSERT TO rtest_nothn1 +
|
| | ON INSERT TO public.rtest_nothn1 +
|
||||||
| | WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD NOTHING;
|
| | WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD NOTHING;
|
||||||
rtest_nothn1 | rtest_nothn_r2 | CREATE RULE rtest_nothn_r2 AS +
|
rtest_nothn1 | rtest_nothn_r2 | CREATE RULE rtest_nothn_r2 AS +
|
||||||
| | ON INSERT TO rtest_nothn1 +
|
| | ON INSERT TO public.rtest_nothn1 +
|
||||||
| | WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING;
|
| | WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING;
|
||||||
rtest_nothn2 | rtest_nothn_r3 | CREATE RULE rtest_nothn_r3 AS +
|
rtest_nothn2 | rtest_nothn_r3 | CREATE RULE rtest_nothn_r3 AS +
|
||||||
| | ON INSERT TO rtest_nothn2 +
|
| | ON INSERT TO public.rtest_nothn2 +
|
||||||
| | WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) +
|
| | WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_nothn2 | rtest_nothn_r4 | CREATE RULE rtest_nothn_r4 AS +
|
rtest_nothn2 | rtest_nothn_r4 | CREATE RULE rtest_nothn_r4 AS +
|
||||||
| | ON INSERT TO rtest_nothn2 DO INSTEAD NOTHING;
|
| | ON INSERT TO public.rtest_nothn2 DO INSTEAD NOTHING;
|
||||||
rtest_order1 | rtest_order_r1 | CREATE RULE rtest_order_r1 AS +
|
rtest_order1 | rtest_order_r1 | CREATE RULE rtest_order_r1 AS +
|
||||||
| | ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
| | ON INSERT TO public.rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
||||||
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 1 - this should run 1st'::text);
|
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 1 - this should run 1st'::text);
|
||||||
rtest_order1 | rtest_order_r2 | CREATE RULE rtest_order_r2 AS +
|
rtest_order1 | rtest_order_r2 | CREATE RULE rtest_order_r2 AS +
|
||||||
| | ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) +
|
| | ON INSERT TO public.rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) +
|
||||||
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 2 - this should run 2nd'::text);
|
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 2 - this should run 2nd'::text);
|
||||||
rtest_order1 | rtest_order_r3 | CREATE RULE rtest_order_r3 AS +
|
rtest_order1 | rtest_order_r3 | CREATE RULE rtest_order_r3 AS +
|
||||||
| | ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
| | ON INSERT TO public.rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
||||||
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 3 - this should run 3rd'::text);
|
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 3 - this should run 3rd'::text);
|
||||||
rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS +
|
rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS +
|
||||||
| | ON INSERT TO rtest_order1 +
|
| | ON INSERT TO public.rtest_order1 +
|
||||||
| | WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
| | WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) +
|
||||||
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 4 - this should run 4th'::text);
|
| | VALUES (new.a, nextval('rtest_seq'::regclass), 'rule 4 - this should run 4th'::text);
|
||||||
rtest_person | rtest_pers_del | CREATE RULE rtest_pers_del AS +
|
rtest_person | rtest_pers_del | CREATE RULE rtest_pers_del AS +
|
||||||
| | ON DELETE TO rtest_person DO DELETE FROM rtest_admin +
|
| | ON DELETE TO public.rtest_person DO DELETE FROM rtest_admin +
|
||||||
| | WHERE (rtest_admin.pname = old.pname);
|
| | WHERE (rtest_admin.pname = old.pname);
|
||||||
rtest_person | rtest_pers_upd | CREATE RULE rtest_pers_upd AS +
|
rtest_person | rtest_pers_upd | CREATE RULE rtest_pers_upd AS +
|
||||||
| | ON UPDATE TO rtest_person DO UPDATE rtest_admin SET pname = new.pname +
|
| | ON UPDATE TO public.rtest_person DO UPDATE rtest_admin SET pname = new.pname +
|
||||||
| | WHERE (rtest_admin.pname = old.pname);
|
| | WHERE (rtest_admin.pname = old.pname);
|
||||||
rtest_system | rtest_sys_del | CREATE RULE rtest_sys_del AS +
|
rtest_system | rtest_sys_del | CREATE RULE rtest_sys_del AS +
|
||||||
| | ON DELETE TO rtest_system DO ( DELETE FROM rtest_interface +
|
| | ON DELETE TO public.rtest_system DO ( DELETE FROM rtest_interface +
|
||||||
| | WHERE (rtest_interface.sysname = old.sysname); +
|
| | WHERE (rtest_interface.sysname = old.sysname); +
|
||||||
| | DELETE FROM rtest_admin +
|
| | DELETE FROM rtest_admin +
|
||||||
| | WHERE (rtest_admin.sysname = old.sysname); +
|
| | WHERE (rtest_admin.sysname = old.sysname); +
|
||||||
| | );
|
| | );
|
||||||
rtest_system | rtest_sys_upd | CREATE RULE rtest_sys_upd AS +
|
rtest_system | rtest_sys_upd | CREATE RULE rtest_sys_upd AS +
|
||||||
| | ON UPDATE TO rtest_system DO ( UPDATE rtest_interface SET sysname = new.sysname +
|
| | ON UPDATE TO public.rtest_system DO ( UPDATE rtest_interface SET sysname = new.sysname +
|
||||||
| | WHERE (rtest_interface.sysname = old.sysname); +
|
| | WHERE (rtest_interface.sysname = old.sysname); +
|
||||||
| | UPDATE rtest_admin SET sysname = new.sysname +
|
| | UPDATE rtest_admin SET sysname = new.sysname +
|
||||||
| | WHERE (rtest_admin.sysname = old.sysname); +
|
| | WHERE (rtest_admin.sysname = old.sysname); +
|
||||||
| | );
|
| | );
|
||||||
rtest_t4 | rtest_t4_ins1 | CREATE RULE rtest_t4_ins1 AS +
|
rtest_t4 | rtest_t4_ins1 | CREATE RULE rtest_t4_ins1 AS +
|
||||||
| | ON INSERT TO rtest_t4 +
|
| | ON INSERT TO public.rtest_t4 +
|
||||||
| | WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD INSERT INTO rtest_t5 (a, b) +
|
| | WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD INSERT INTO rtest_t5 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_t4 | rtest_t4_ins2 | CREATE RULE rtest_t4_ins2 AS +
|
rtest_t4 | rtest_t4_ins2 | CREATE RULE rtest_t4_ins2 AS +
|
||||||
| | ON INSERT TO rtest_t4 +
|
| | ON INSERT TO public.rtest_t4 +
|
||||||
| | WHERE ((new.a >= 20) AND (new.a < 30)) DO INSERT INTO rtest_t6 (a, b) +
|
| | WHERE ((new.a >= 20) AND (new.a < 30)) DO INSERT INTO rtest_t6 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_t5 | rtest_t5_ins | CREATE RULE rtest_t5_ins AS +
|
rtest_t5 | rtest_t5_ins | CREATE RULE rtest_t5_ins AS +
|
||||||
| | ON INSERT TO rtest_t5 +
|
| | ON INSERT TO public.rtest_t5 +
|
||||||
| | WHERE (new.a > 15) DO INSERT INTO rtest_t7 (a, b) +
|
| | WHERE (new.a > 15) DO INSERT INTO rtest_t7 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_t6 | rtest_t6_ins | CREATE RULE rtest_t6_ins AS +
|
rtest_t6 | rtest_t6_ins | CREATE RULE rtest_t6_ins AS +
|
||||||
| | ON INSERT TO rtest_t6 +
|
| | ON INSERT TO public.rtest_t6 +
|
||||||
| | WHERE (new.a > 25) DO INSTEAD INSERT INTO rtest_t8 (a, b) +
|
| | WHERE (new.a > 25) DO INSTEAD INSERT INTO rtest_t8 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_v1 | rtest_v1_del | CREATE RULE rtest_v1_del AS +
|
rtest_v1 | rtest_v1_del | CREATE RULE rtest_v1_del AS +
|
||||||
| | ON DELETE TO rtest_v1 DO INSTEAD DELETE FROM rtest_t1 +
|
| | ON DELETE TO public.rtest_v1 DO INSTEAD DELETE FROM rtest_t1 +
|
||||||
| | WHERE (rtest_t1.a = old.a);
|
| | WHERE (rtest_t1.a = old.a);
|
||||||
rtest_v1 | rtest_v1_ins | CREATE RULE rtest_v1_ins AS +
|
rtest_v1 | rtest_v1_ins | CREATE RULE rtest_v1_ins AS +
|
||||||
| | ON INSERT TO rtest_v1 DO INSTEAD INSERT INTO rtest_t1 (a, b) +
|
| | ON INSERT TO public.rtest_v1 DO INSTEAD INSERT INTO rtest_t1 (a, b) +
|
||||||
| | VALUES (new.a, new.b);
|
| | VALUES (new.a, new.b);
|
||||||
rtest_v1 | rtest_v1_upd | CREATE RULE rtest_v1_upd AS +
|
rtest_v1 | rtest_v1_upd | CREATE RULE rtest_v1_upd AS +
|
||||||
| | ON UPDATE TO rtest_v1 DO INSTEAD UPDATE rtest_t1 SET a = new.a, b = new.b +
|
| | ON UPDATE TO public.rtest_v1 DO INSTEAD UPDATE rtest_t1 SET a = new.a, b = new.b +
|
||||||
| | WHERE (rtest_t1.a = old.a);
|
| | WHERE (rtest_t1.a = old.a);
|
||||||
shoelace | shoelace_del | CREATE RULE shoelace_del AS +
|
shoelace | shoelace_del | CREATE RULE shoelace_del AS +
|
||||||
| | ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data +
|
| | ON DELETE TO public.shoelace DO INSTEAD DELETE FROM shoelace_data +
|
||||||
| | WHERE (shoelace_data.sl_name = old.sl_name);
|
| | WHERE (shoelace_data.sl_name = old.sl_name);
|
||||||
shoelace | shoelace_ins | CREATE RULE shoelace_ins AS +
|
shoelace | shoelace_ins | CREATE RULE shoelace_ins AS +
|
||||||
| | ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data (sl_name, sl_avail, sl_color, sl_len, sl_unit) +
|
| | ON INSERT TO public.shoelace DO INSTEAD INSERT INTO shoelace_data (sl_name, sl_avail, sl_color, sl_len, sl_unit) +
|
||||||
| | VALUES (new.sl_name, new.sl_avail, new.sl_color, new.sl_len, new.sl_unit);
|
| | VALUES (new.sl_name, new.sl_avail, new.sl_color, new.sl_len, new.sl_unit);
|
||||||
shoelace | shoelace_upd | CREATE RULE shoelace_upd AS +
|
shoelace | shoelace_upd | CREATE RULE shoelace_upd AS +
|
||||||
| | ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = new.sl_name, sl_avail = new.sl_avail, sl_color = new.sl_color, sl_len = new.sl_len, sl_unit = new.sl_unit+
|
| | ON UPDATE TO public.shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = new.sl_name, sl_avail = new.sl_avail, sl_color = new.sl_color, sl_len = new.sl_len, sl_unit = new.sl_unit+
|
||||||
| | WHERE (shoelace_data.sl_name = old.sl_name);
|
| | WHERE (shoelace_data.sl_name = old.sl_name);
|
||||||
shoelace_data | log_shoelace | CREATE RULE log_shoelace AS +
|
shoelace_data | log_shoelace | CREATE RULE log_shoelace AS +
|
||||||
| | ON UPDATE TO shoelace_data +
|
| | ON UPDATE TO public.shoelace_data +
|
||||||
| | WHERE (new.sl_avail <> old.sl_avail) DO INSERT INTO shoelace_log (sl_name, sl_avail, log_who, log_when) +
|
| | WHERE (new.sl_avail <> old.sl_avail) DO INSERT INTO shoelace_log (sl_name, sl_avail, log_who, log_when) +
|
||||||
| | VALUES (new.sl_name, new.sl_avail, 'Al Bundy'::name, 'Thu Jan 01 00:00:00 1970'::timestamp without time zone);
|
| | VALUES (new.sl_name, new.sl_avail, 'Al Bundy'::name, 'Thu Jan 01 00:00:00 1970'::timestamp without time zone);
|
||||||
shoelace_ok | shoelace_ok_ins | CREATE RULE shoelace_ok_ins AS +
|
shoelace_ok | shoelace_ok_ins | CREATE RULE shoelace_ok_ins AS +
|
||||||
| | ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) +
|
| | ON INSERT TO public.shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = (shoelace.sl_avail + new.ok_quant) +
|
||||||
| | WHERE (shoelace.sl_name = new.ok_name);
|
| | WHERE (shoelace.sl_name = new.ok_name);
|
||||||
(29 rows)
|
(29 rows)
|
||||||
|
|
||||||
|
@ -382,8 +382,8 @@ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'
|
|||||||
|
|
||||||
SELECT pg_get_triggerdef(oid, false) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a';
|
SELECT pg_get_triggerdef(oid, false) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a';
|
||||||
pg_get_triggerdef
|
pg_get_triggerdef
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table FOR EACH ROW WHEN ((old.a <> new.a)) EXECUTE PROCEDURE trigger_func('modified_a')
|
CREATE TRIGGER modified_a BEFORE UPDATE OF a ON public.main_table FOR EACH ROW WHEN ((old.a <> new.a)) EXECUTE PROCEDURE trigger_func('modified_a')
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any';
|
SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any';
|
||||||
@ -412,8 +412,8 @@ CREATE TRIGGER after_upd_b_stmt_trig AFTER UPDATE OF b ON main_table
|
|||||||
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_upd_b_stmt');
|
FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_upd_b_stmt');
|
||||||
SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
|
SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
|
||||||
pg_get_triggerdef
|
pg_get_triggerdef
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row')
|
CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON public.main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row')
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
UPDATE main_table SET a = 50;
|
UPDATE main_table SET a = 50;
|
||||||
|
Reference in New Issue
Block a user