当前位置:   article > 正文

PostgreSQL源码分析——CREATE EXTENSION

PostgreSQL源码分析——CREATE EXTENSION

本文分析一下CREATE EXTENSION的源码实现。顺带学习一下PG中的插件相关的原理。

语法解析层面

gram.y中的语法定义如下:

/************************************************************************
 *
 *		QUERY:
 *             CREATE EXTENSION extension
 *             [ WITH ] [ SCHEMA schema ] [ VERSION version ]
 *
 ***************************************************************************/
CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
				{
					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
					n->extname = $3;
					n->if_not_exists = false;
					n->options = $5;
					$$ = (Node *) n;
				}
				| CREATE EXTENSION IF_P NOT EXISTS name opt_with create_extension_opt_list
				{
					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
					n->extname = $6;
					n->if_not_exists = true;
					n->options = $8;
					$$ = (Node *) n;
				}
		;

create_extension_opt_list:
			create_extension_opt_list create_extension_opt_item
				{ $$ = lappend($1, $2); }
			| /* EMPTY */
				{ $$ = NIL; }
		;

create_extension_opt_item:
			SCHEMA name
				{
					$$ = makeDefElem("schema", (Node *)makeString($2), @1);
				}
			| VERSION_P NonReservedWord_or_Sconst
				{
					$$ = makeDefElem("new_version", (Node *)makeString($2), @1);
				}
			| FROM NonReservedWord_or_Sconst
				{
					ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("CREATE EXTENSION ... FROM is no longer supported"),parser_errposition(@1));
				}
			| CASCADE
				{
					$$ = makeDefElem("cascade", (Node *)makeInteger(true), @1);
				}
		;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

其核心就是要记录插件名字,有哪些扩展选项:

typedef struct CreateExtensionStmt
{
	NodeTag		type;
	char	   *extname;
	bool		if_not_exists;	/* just do nothing if it already exists? */
	List	   *options;		/* List of DefElem nodes */
} CreateExtensionStmt;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

很明显这是一个UTILITY命令,不需要经过查询优化器进行优化,直接简单封装为查询树继而封装为执行计划树后进入执行器执行。

执行层面

生成执行计划树后,进入到执行器中进行处理,Utility语句会调用standard_ProcessUtility进行处理,对应的创建插件由CreateExtension函数实现。

/* CREATE EXTENSION*/
ObjectAddress CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
{
	DefElem    *d_schema = NULL;
	DefElem    *d_new_version = NULL;
	DefElem    *d_cascade = NULL;
	char	   *schemaName = NULL;
	char	   *versionName = NULL;
	bool		cascade = false;
	ListCell   *lc;

	/* Check extension name validity before any filesystem access */
	check_valid_extension_name(stmt->extname);  // 检查插件名称是否有效

	/* Check for duplicate extension name.  The unique index on
	 * pg_extension.extname would catch this anyway, and serves as a backstop
	 * in case of race conditions; but this is a friendlier error message, and
	 * besides we need a check to support IF NOT EXISTS. */
	if (get_extension_oid(stmt->extname, true) != InvalidOid) // 从pg_extension系统表中查看是否有冲突
	{
		if (stmt->if_not_exists)
		{
			ereport(NOTICE,(errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("extension \"%s\" already exists, skipping",stmt->extname)));
			return InvalidObjectAddress;
		}
		else
			ereport(ERROR,(errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("extension \"%s\" already exists",stmt->extname)));
	}

	/* We use global variables to track the extension being created, so we can create only one extension at the same time. */
	if (creating_extension)
		ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("nested CREATE EXTENSION is not supported")));

	/* Deconstruct the statement option list */  // 解析options
	foreach(lc, stmt->options)
	{
		DefElem    *defel = (DefElem *) lfirst(lc);

		if (strcmp(defel->defname, "schema") == 0)
		{
			if (d_schema)
				ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR),errmsg("conflicting or redundant options"), parser_errposition(pstate, defel->location)));
			d_schema = defel;
			schemaName = defGetString(d_schema);
		}
		else if (strcmp(defel->defname, "new_version") == 0)
		{
			if (d_new_version)
				ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"), parser_errposition(pstate, defel->location)));
			d_new_version = defel;
			versionName = defGetString(d_new_version);
		}
		else if (strcmp(defel->defname, "cascade") == 0)
		{
			if (d_cascade)
				ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"), parser_errposition(pstate, defel->location)));
			d_cascade = defel;
			cascade = defGetBoolean(d_cascade);
		}
		else
			elog(ERROR, "unrecognized option: %s", defel->defname);
	}

	/* Call CreateExtensionInternal to do the real work. */  // 实际创建是在这里实现的
	return CreateExtensionInternal(stmt->extname, schemaName, versionName, cascade, NIL, true);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

我们接下来分析一下关键函数CreateExtensionInternal,源码如下:

/* CREATE EXTENSION worker
 *
 * When CASCADE is specified, CreateExtensionInternal() recurses if required
 * extensions need to be installed.  To sanely handle cyclic dependencies,
 * the "parents" list contains a list of names of extensions already being
 * installed, allowing us to error out if we recurse to one of those.  */
static ObjectAddress CreateExtensionInternal(char *extensionName,char *schemaName,const char *versionName,bool cascade,List *parents,bool is_create)
{
	char	   *origSchemaName = schemaName;
	Oid			schemaOid = InvalidOid;
	Oid			extowner = GetUserId();
	ExtensionControlFile *pcontrol;
	ExtensionControlFile *control;
	char	   *filename;
	struct stat fst;
	List	   *updateVersions;
	List	   *requiredExtensions;
	List	   *requiredSchemas;
	Oid			extensionOid;
	ObjectAddress address;
	ListCell   *lc;

	/* Read the primary control file. */
	pcontrol = read_extension_control_file(extensionName);  // 读*.control文件。在插件的目录里。

	/* Determine the version to install */
	if (versionName == NULL)
	{
		if (pcontrol->default_version)
			versionName = pcontrol->default_version;
		else
			ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),errmsg("version to install must be specified")));
	}
	check_valid_version_name(versionName);

	/* Figure out which script(s) we need to run to install the desired
	 * version of the extension.  If we do not have a script that directly
	 * does what is needed, we try to find a sequence of update scripts that will get us there. */
	filename = get_extension_script_filename(pcontrol, NULL, versionName);
	if (stat(filename, &fst) == 0)
		updateVersions = NIL;		/* Easy, no extra scripts */
	else
	{
		/* Look for best way to install this version */
		List	   *evi_list;
		ExtensionVersionInfo *evi_start;
		ExtensionVersionInfo *evi_target;

		/* Extract the version update graph from the script directory */
		evi_list = get_ext_ver_list(pcontrol);

		/* Identify the target version */
		evi_target = get_ext_ver_info(versionName, &evi_list);

		/* Identify best path to reach target */
		evi_start = find_install_path(evi_list, evi_target, &updateVersions);

		/* Fail if no path ... */
		if (evi_start == NULL)
			ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",pcontrol->name, versionName)));

		versionName = evi_start->name;		/* Otherwise, install best starting point and then upgrade */
	}

	/* Fetch control parameters for installation target version */
	control = read_extension_aux_control_file(pcontrol, versionName);

	/* Determine the target schema to install the extension into*/
	if (schemaName)
		schemaOid = get_namespace_oid(schemaName, false);		/* If the user is giving us the schema name, it must exist already. */

	if (control->schema != NULL)
	{
		/* The extension is not relocatable and the author gave us a schema for it.
		 * Unless CASCADE parameter was given, it's an error to give a schema
		 * different from control->schema if control->schema is specified. */
		if (schemaName && strcmp(control->schema, schemaName) != 0 && !cascade)
			ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("extension \"%s\" must be installed in schema \"%s\"",control->name,	control->schema)));

		schemaName = control->schema;		/* Always use the schema from control file for current extension. */
		schemaOid = get_namespace_oid(schemaName, true);		/* Find or create the schema in case it does not exist. */
		if (!OidIsValid(schemaOid))
		{
			CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);

			csstmt->schemaname = schemaName;
			csstmt->authrole = NULL;	/* will be created by current user */
			csstmt->schemaElts = NIL;
			csstmt->if_not_exists = false;
			CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",-1, -1);

			/* CreateSchemaCommand includes CommandCounterIncrement, so new schema is now visible. */
			schemaOid = get_namespace_oid(schemaName, false);
		}
	}
	else if (!OidIsValid(schemaOid))
	{
		/* Neither user nor author of the extension specified schema; use the current default creation namespace, which is the first explicit entry in the search_path. */
		List	   *search_path = fetch_search_path(false);

		if (search_path == NIL) /* nothing valid in search_path? */
			ereport(ERROR,(errcode(ERRCODE_UNDEFINED_SCHEMA),errmsg("no schema has been selected to create in")));
		schemaOid = linitial_oid(search_path);
		schemaName = get_namespace_name(schemaOid);
		if (schemaName == NULL) /* recently-deleted namespace? */
			ereport(ERROR,(errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("no schema has been selected to create in")));

		list_free(search_path);
	}

	/* Make note if a temporary namespace has been accessed in this transaction. */
	if (isTempNamespace(schemaOid))
		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

	/* We don't check creation rights on the target namespace here.  If the
	 * extension script actually creates any objects there, it will fail if
	 * the user doesn't have such permissions.  But there are cases such as
	 * procedural languages where it's convenient to set schema = pg_catalog
	 * yet we don't want to restrict the command to users with ACL_CREATE for pg_catalog.*/

	/* Look up the prerequisite extensions, install them if necessary, and
	 * build lists of their OIDs and the OIDs of their target schemas. */
	requiredExtensions = NIL;
	requiredSchemas = NIL;
	foreach(lc, control->requires)
	{
		char	   *curreq = (char *) lfirst(lc);
		Oid			reqext, reqschema;

		reqext = get_required_extension(curreq,extensionName,origSchemaName,cascade,parents,is_create);
		reqschema = get_extension_schema(reqext);
		requiredExtensions = lappend_oid(requiredExtensions, reqext);
		requiredSchemas = lappend_oid(requiredSchemas, reqschema);
	}

    // 新插件信息插入到系统表pg_extension中。
	/* Insert new tuple into pg_extension, and create dependency entries. */
	address = InsertExtensionTuple(control->name, extowner,schemaOid, control->relocatable,versionName,PointerGetDatum(NULL),PointerGetDatum(NULL),requiredExtensions);
	extensionOid = address.objectId;

	/* Apply any control-file comment on extension*/
	if (control->comment != NULL)
		CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);

	/* Execute the installation script file */  // 执行插件脚本中的sql语句。
	execute_extension_script(extensionOid, control, NULL, versionName, requiredSchemas,schemaName, schemaOid);

	/* If additional update scripts have to be executed, apply the updates as though a series of ALTER EXTENSION UPDATE commands were given*/
	ApplyExtensionUpdates(extensionOid, pcontrol, versionName, updateVersions, origSchemaName, cascade, is_create);

	return address;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152

在执行execute_extension_script函数时,不同的插件有所差别,但最后都会调用到_PG_init

后续

插件创建完成后,可查系统表pg_extension查看:

postgres@postgres=# select * from pg_extension;
  oid  | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition 
-------+---------+----------+--------------+----------------+------------+-----------+--------------
 16384 | plpgsql |       10 |           11 | f              | 1.0        |           | 
(1 row)
  • 1
  • 2
  • 3
  • 4
  • 5

或者通\x进行查看:

postgres@postgres=# \dx
                 List of installed extensions
  Name   | Version |   Schema   |         Description          
---------+---------+------------+------------------------------
 plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language
(1 row)l
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/758546
推荐阅读
相关标签
  

闽ICP备14008679号