extension-inline.v0.patch
text/x-patch
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v0
| File | + | − |
|---|---|---|
| doc/src/sgml/catalogs.sgml | 7 | 0 |
| src/backend/commands/extension.c | 205 | 13 |
| src/backend/parser/gram.y | 97 | 3 |
| src/include/catalog/pg_extension.h | 5 | 3 |
| src/include/commands/extension.h | 4 | 3 |
| src/include/nodes/parsenodes.h | 4 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index be4bbc7..4d1c18c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3020,6 +3020,13 @@
</row>
<row>
+ <entry><structfield>extinline</structfield></entry>
+ <entry><type>bool</type></entry>
+ <entry></entry>
+ <entry>True if extension has been created inline</entry>
+ </row>
+
+ <row>
<entry><structfield>extversion</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 7192c45..7157bc3 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -73,6 +73,8 @@ typedef struct ExtensionControlFile
bool superuser; /* must be superuser to install? */
int encoding; /* encoding of the script file, or -1 */
List *requires; /* names of prerequisite extensions */
+ bool is_inline; /* create extension inline */
+ char *script; /* script when extension is inline */
} ExtensionControlFile;
/*
@@ -590,6 +592,7 @@ read_extension_control_file(const char *extname)
control->relocatable = false;
control->superuser = true;
control->encoding = -1;
+ control->is_inline = false;
/*
* Parse the primary control file.
@@ -800,7 +803,10 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
errhint("Must be superuser to update this extension.")));
}
- filename = get_extension_script_filename(control, from_version, version);
+ if (!control->is_inline)
+ filename = get_extension_script_filename(control, from_version, version);
+ else
+ filename = "INLINE"; /* make compiler happy */
/*
* Force client_min_messages and log_min_messages to be at least WARNING,
@@ -856,11 +862,13 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
CurrentExtensionObject = extensionOid;
PG_TRY();
{
- char *c_sql = read_extension_script_file(control, filename);
Datum t_sql;
+ if (!control->is_inline)
+ control->script = read_extension_script_file(control, filename);
+
/* We use various functions that want to operate on text datums */
- t_sql = CStringGetTextDatum(c_sql);
+ t_sql = CStringGetTextDatum(control->script);
/*
* Reduce any lines beginning with "\echo" to empty. This allows
@@ -904,9 +912,9 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
}
/* And now back to C string */
- c_sql = text_to_cstring(DatumGetTextPP(t_sql));
+ control->script = text_to_cstring(DatumGetTextPP(t_sql));
- execute_sql_string(c_sql, filename);
+ execute_sql_string(control->script, filename);
}
PG_CATCH();
{
@@ -1178,6 +1186,11 @@ CreateExtension(CreateExtensionStmt *stmt)
DefElem *d_schema = NULL;
DefElem *d_new_version = NULL;
DefElem *d_old_version = NULL;
+ DefElem *d_relocatable = NULL;
+ DefElem *d_comment = NULL;
+ DefElem *d_module_pathname = NULL;
+ DefElem *d_requires = NULL;
+ DefElem *d_superuser = NULL;
char *schemaName;
Oid schemaOid;
char *versionName;
@@ -1231,7 +1244,18 @@ CreateExtension(CreateExtensionStmt *stmt)
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
- pcontrol = read_extension_control_file(stmt->extname);
+ if (!stmt->is_inline)
+ pcontrol = read_extension_control_file(stmt->extname);
+ else
+ {
+ /* we still need to initialize a default empty primary control file */
+ pcontrol = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
+ pcontrol->name = pstrdup(stmt->extname);
+ pcontrol->relocatable = false;
+ pcontrol->superuser = true;
+ pcontrol->encoding = -1;
+ pcontrol->is_inline = true;
+ }
/*
* Read the statement option list
@@ -1264,6 +1288,46 @@ CreateExtension(CreateExtensionStmt *stmt)
errmsg("conflicting or redundant options")));
d_old_version = defel;
}
+ else if (strcmp(defel->defname, "relocatable") == 0)
+ {
+ if (d_relocatable || !stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_relocatable = defel;
+ }
+ else if (strcmp(defel->defname, "comment") == 0)
+ {
+ if (d_comment || !stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_comment = defel;
+ }
+ else if (strcmp(defel->defname, "module_pathname") == 0)
+ {
+ if (d_module_pathname || !stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_module_pathname = defel;
+ }
+ else if (strcmp(defel->defname, "requires") == 0)
+ {
+ if (d_requires || !stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_requires = defel;
+ }
+ else if (strcmp(defel->defname, "superuser") == 0)
+ {
+ if (d_superuser || !stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_superuser = defel;
+ }
else
elog(ERROR, "unrecognized option: %s", defel->defname);
}
@@ -1332,7 +1396,55 @@ CreateExtension(CreateExtensionStmt *stmt)
/*
* Fetch control parameters for installation target version
*/
- control = read_extension_aux_control_file(pcontrol, versionName);
+ if (!stmt->is_inline)
+ control = read_extension_aux_control_file(pcontrol, versionName);
+ else
+ {
+ /*
+ * When the extension is inline we take the control parameters from the
+ * statement
+ */
+ control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
+ control->name = pstrdup(stmt->extname);
+ control->encoding = -1;
+ control->is_inline = true;
+ control->script = pstrdup(stmt->script);
+
+ if (d_relocatable)
+ control->relocatable = intVal(d_relocatable) != 0;
+
+ if (d_superuser)
+ control->superuser = intVal(d_superuser) != 0;
+
+ if (d_comment && d_comment->arg)
+ control->comment = strVal(d_comment->arg);
+
+ if (d_requires && d_requires->arg)
+ {
+ char *rawnames = pstrdup(strVal(d_requires->arg));
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawnames, ',', &pcontrol->requires))
+ {
+ /* syntax error in name list */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("requires must be a list of extension names")));
+ }
+ }
+
+ if (d_schema && d_schema->arg)
+ {
+ /* CREATE EXTENSION ... INLINE WITH SCHEMA ... NOT RELOCATABLE
+ *
+ * In the case of an inline extension we need to consider the
+ * schema given by the command to be the same one as the one we
+ * otherwise find in the control file.
+ */
+ control->schema = strVal(d_schema->arg);
+ d_schema = NULL;
+ }
+ }
/*
* Determine the target schema to install the extension into
@@ -1442,6 +1554,7 @@ CreateExtension(CreateExtensionStmt *stmt)
*/
extensionOid = InsertExtensionTuple(control->name, extowner,
schemaOid, control->relocatable,
+ control->is_inline,
versionName,
PointerGetDatum(NULL),
PointerGetDatum(NULL),
@@ -1484,7 +1597,8 @@ CreateExtension(CreateExtensionStmt *stmt)
*/
Oid
InsertExtensionTuple(const char *extName, Oid extOwner,
- Oid schemaOid, bool relocatable, const char *extVersion,
+ Oid schemaOid, bool relocatable, bool is_inline,
+ const char *extVersion,
Datum extConfig, Datum extCondition,
List *requiredExtensions)
{
@@ -1510,6 +1624,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
+ values[Anum_pg_extension_extinline - 1] = BoolGetDatum(is_inline);
values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
if (extConfig == PointerGetDatum(NULL))
@@ -2351,6 +2466,7 @@ void
ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
{
DefElem *d_new_version = NULL;
+ DefElem *d_old_version = NULL;
char *versionName;
char *oldVersionName;
ExtensionControlFile *control;
@@ -2397,6 +2513,26 @@ ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
extensionOid = HeapTupleGetOid(extTup);
/*
+ * We can only update inline an inline extension
+ */
+ datum = heap_getattr(extTup, Anum_pg_extension_extinline,
+ RelationGetDescr(extRel), &isnull);
+ if (isnull)
+ elog(ERROR, "extversion is null");
+
+ if (DatumGetBool(datum) != stmt->is_inline)
+ {
+ if (stmt->is_inline)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("inline update applies only to inline extension")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("inline extension can only be updated inline")));
+ }
+
+ /*
* Determine the existing version we are updating from
*/
datum = heap_getattr(extTup, Anum_pg_extension_extversion,
@@ -2419,7 +2555,23 @@ ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
- control = read_extension_control_file(stmt->extname);
+ if (!stmt->is_inline)
+ control = read_extension_control_file(stmt->extname);
+ else
+ {
+ /*
+ * When the extension is inline we take the control parameters from the
+ * statement
+ */
+ control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
+ control->name = pstrdup(stmt->extname);
+ control->encoding = -1;
+ control->is_inline = true;
+ control->script = pstrdup(stmt->script);
+
+ /* TODO: add support for all other control properties in the statement */
+ control->relocatable = false;
+ }
/*
* Read the statement option list
@@ -2436,11 +2588,35 @@ ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
errmsg("conflicting or redundant options")));
d_new_version = defel;
}
+ else if (strcmp(defel->defname, "old_version") == 0)
+ {
+ if (d_old_version)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_old_version = defel;
+ }
else
elog(ERROR, "unrecognized option: %s", defel->defname);
}
/*
+ * Check that we've been asked to upgrade from the current version
+ */
+ if (control->is_inline && !d_old_version)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("missing \"FROM\" option"),
+ errdetail("Inline update of extension requires the FROM option")));
+
+ if (d_old_version && d_old_version->arg)
+ if (strcmp(oldVersionName, strVal(d_old_version->arg)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("FROM version does not match current version, \"%s\"",
+ oldVersionName)));
+
+ /*
* Determine the version to update to
*/
if (d_new_version && d_new_version->arg)
@@ -2470,9 +2646,23 @@ ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
/*
* Identify the series of update script files we need to execute
*/
- updateVersions = identify_update_path(control,
- oldVersionName,
- versionName);
+ if (!control->is_inline)
+ {
+ updateVersions = identify_update_path(control,
+ oldVersionName,
+ versionName);
+ }
+ else
+ {
+ /*
+ * In case of an inline update the script and control parameters are
+ * all taken direcly from the statement itself. The origin version is
+ * checked against the installed one.
+ *
+ * TODO: process all control properties from the statement.
+ */
+ updateVersions = list_make1(versionName);
+ }
/*
* Update the pg_extension row and execute the update scripts, one at a
@@ -2521,7 +2711,9 @@ ApplyExtensionUpdates(Oid extensionOid,
/*
* Fetch parameters for specific version (pcontrol is not changed)
*/
- control = read_extension_aux_control_file(pcontrol, versionName);
+ control = pcontrol; /* make compiler happy */
+ if (!control->is_inline)
+ control = read_extension_aux_control_file(pcontrol, versionName);
/* Find the pg_extension tuple */
extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8943c5b..81f5f2b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -242,8 +242,9 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
%type <list> createdb_opt_list alterdb_opt_list copy_opt_list
transaction_mode_list
create_extension_opt_list alter_extension_opt_list
+ extension_feature_list create_extension_full_opt_list
%type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
- transaction_mode_item
+ transaction_mode_item create_extension_full_opt_item
create_extension_opt_item alter_extension_opt_item
%type <ival> opt_lock lock_type cast_context
@@ -546,7 +547,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
- RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+ RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA REQUIRES
RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK
ROW ROWS RULE
@@ -3306,6 +3307,7 @@ CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
{
CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
n->extname = $3;
+ n->is_inline = false;
n->if_not_exists = false;
n->options = $5;
$$ = (Node *) n;
@@ -3314,10 +3316,36 @@ CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
{
CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
n->extname = $6;
+ n->is_inline = false;
n->if_not_exists = true;
n->options = $8;
$$ = (Node *) n;
}
+ /* we avoid shift/reduce using INLINE, the other way around
+ * would be to accept the same option list for control file
+ * based extension definitions and inline ones, and sort it out
+ * in extension.c
+ */
+ | CREATE EXTENSION name INLINE_P opt_with create_extension_full_opt_list
+ AS Sconst
+ {
+ CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ n->extname = $3;
+ n->is_inline = true;
+ n->if_not_exists = false;
+ n->options = $6;
+ n->script = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+create_extension_full_opt_list:
+ create_extension_full_opt_list create_extension_opt_item
+ { $$ = lappend($1, $2); }
+ | create_extension_full_opt_list create_extension_full_opt_item
+ { $$ = lappend($1, $2); }
+ | /* EMPTY */
+ { $$ = NIL; }
;
create_extension_opt_list:
@@ -3342,6 +3370,57 @@ create_extension_opt_item:
}
;
+create_extension_full_opt_item:
+ COMMENT Sconst
+ {
+ $$ = makeDefElem("comment", (Node *)makeString($2));
+ }
+ | REQUIRES extension_feature_list
+ {
+ $$ = makeDefElem("requires", (Node *)$2);
+ }
+ /*
+ * We handle identifiers that aren't parser keywords with
+ * the following special-case codes, to avoid bloating the
+ * size of the main parser.
+ */
+ | IDENT
+ {
+ if (strcasecmp($1, "superuser") == 0)
+ $$ = makeDefElem("superuser", (Node *)makeInteger(TRUE));
+ else if (strcasecmp($1, "relocatable") == 0)
+ $$ = makeDefElem("relocatable", (Node *)makeInteger(TRUE));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized extension parameter \"%s\"", $1),
+ parser_errposition(@1)));
+ }
+ | NOT IDENT
+ {
+ if (strcasecmp($2, "superuser") == 0)
+ $$ = makeDefElem("superuser", (Node *)makeInteger(FALSE));
+ else if (strcasecmp($2, "relocatable") == 0)
+ $$ = makeDefElem("relocatable", (Node *)makeInteger(FALSE));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized extension parameter \"%s\"", $1),
+ parser_errposition(@1)));
+ }
+ ;
+
+extension_feature_list:
+ Sconst
+ {
+ $$ = list_make1($1);
+ }
+ | extension_feature_list ',' Sconst
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -3353,6 +3432,16 @@ AlterExtensionStmt: ALTER EXTENSION name UPDATE alter_extension_opt_list
AlterExtensionStmt *n = makeNode(AlterExtensionStmt);
n->extname = $3;
n->options = $5;
+ n->is_inline = false;
+ $$ = (Node *) n;
+ }
+ | ALTER EXTENSION name UPDATE alter_extension_opt_list WITH Sconst
+ {
+ AlterExtensionStmt *n = makeNode(AlterExtensionStmt);
+ n->extname = $3;
+ n->options = $5;
+ n->is_inline = true;
+ n->script = $7;
$$ = (Node *) n;
}
;
@@ -3365,7 +3454,11 @@ alter_extension_opt_list:
;
alter_extension_opt_item:
- TO ColId_or_Sconst
+ FROM ColId_or_Sconst
+ {
+ $$ = makeDefElem("old_version", (Node *)makeString($2));
+ }
+ | TO ColId_or_Sconst
{
$$ = makeDefElem("new_version", (Node *)makeString($2));
}
@@ -12058,6 +12151,7 @@ unreserved_keyword:
| REPEATABLE
| REPLACE
| REPLICA
+ | REQUIRES
| RESET
| RESTART
| RESTRICT
diff --git a/src/include/catalog/pg_extension.h b/src/include/catalog/pg_extension.h
index 064b999..d28de9a 100644
--- a/src/include/catalog/pg_extension.h
+++ b/src/include/catalog/pg_extension.h
@@ -34,6 +34,7 @@ CATALOG(pg_extension,3079)
Oid extowner; /* extension owner */
Oid extnamespace; /* namespace of contained objects */
bool extrelocatable; /* if true, allow ALTER EXTENSION SET SCHEMA */
+ bool extinline; /* if true, there's no control and script files */
/*
* VARIABLE LENGTH FIELDS start here.
@@ -62,9 +63,10 @@ typedef FormData_pg_extension *Form_pg_extension;
#define Anum_pg_extension_extowner 2
#define Anum_pg_extension_extnamespace 3
#define Anum_pg_extension_extrelocatable 4
-#define Anum_pg_extension_extversion 5
-#define Anum_pg_extension_extconfig 6
-#define Anum_pg_extension_extcondition 7
+#define Anum_pg_extension_extinline 5
+#define Anum_pg_extension_extversion 6
+#define Anum_pg_extension_extconfig 7
+#define Anum_pg_extension_extcondition 8
/* ----------------
* pg_extension has no initial contents
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index f22ac80..ac27a60 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -32,9 +32,10 @@ extern void CreateExtension(CreateExtensionStmt *stmt);
extern void RemoveExtensionById(Oid extId);
extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
- Oid schemaOid, bool relocatable, const char *extVersion,
- Datum extConfig, Datum extCondition,
- List *requiredExtensions);
+ Oid schemaOid, bool relocatable, bool is_inline,
+ const char *extVersion,
+ Datum extConfig, Datum extCondition,
+ List *requiredExtensions);
extern void ExecAlterExtensionStmt(AlterExtensionStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6e8b110..24165ae 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1593,8 +1593,10 @@ typedef struct CreateExtensionStmt
{
NodeTag type;
char *extname;
+ bool is_inline; /* no files, SQL only extension */
bool if_not_exists; /* just do nothing if it already exists? */
List *options; /* List of DefElem nodes */
+ char *script; /* inline extensions stuff the script here */
} CreateExtensionStmt;
/* Only used for ALTER EXTENSION UPDATE; later might need an action field */
@@ -1603,6 +1605,8 @@ typedef struct AlterExtensionStmt
NodeTag type;
char *extname;
List *options; /* List of DefElem nodes */
+ bool is_inline; /* no files, SQL only extension */
+ char *script; /* inline extensions stuff the script here */
} AlterExtensionStmt;
typedef struct AlterExtensionContentsStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49aefed..4d64881 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -305,6 +305,7 @@ PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD)
PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD)
PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD)
+PG_KEYWORD("requires", REQUIRES, UNRESERVED_KEYWORD)
PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD)
PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD)
PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD)