SQL引擎 - parse_type.cpp分析(一)
数据库的SQL引擎是数据库重要的子系统之一,它对上负责承接应用程序发送过来的SQL语句,对下则负责指挥执行器运行执行计划。其中优化器作为SQL引擎中最重要、最复杂的模块,被称为数据库的“大脑”,优化器产生的执行计划的优劣直接决定数据库的性能。
SQL引擎主要包括查询解析(parser)、查询分流(traffic cop)、查询优化(optimizer)、查询执行(executor)。parser源码目录为/src/common/backend/parser:
parse_type.cpp 解析器处理类型操作
src/common/backend/parser/parse_type.cpp
typenameType
typenameType - 给定TypeName,返回Type结构和typmod, 这相当于LookupTypeName,只是它会报告,如果找不到类型或未定义类型,则提供合适的错误消息,因此,调用者可以假设结果是完全有效的类型。
Type typenameType(ParseState* pstate, const TypeName* typname, int32* typmod_p)
{
Type tup;
tup = LookupTypeName(pstate, typname, typmod_p);
/*
* 如果类型是relation,则进行检查
* 表是否在安装组中
*/
if (!in_logic_cluster() && !IsTypeTableInInstallationGroup(tup)) {
InsertErrorMessage("type must be in installation group", u_sess->plsql_cxt.plpgsql_yylloc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("type '%s' must be in installation group", TypeNameToString(typname))));
}
if (tup == NULL) {
InsertErrorMessage("type does not exist", u_sess->plsql_cxt.plpgsql_yylloc);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist", TypeNameToString(typname)),
parser_errposition(pstate, typname->location)));
}
if (!((Form_pg_type)GETSTRUCT(tup))->typisdefined) {
InsertErrorMessage("type is only a shell", u_sess->plsql_cxt.plpgsql_yylloc);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell", TypeNameToString(typname)),
parser_errposition(pstate, typname->location)));
}
return tup;
}
typenameTypeId2
typenamettypeid—给定一个TypeName,返回该类型的OID,这类似于typenameType,但是我们只返回类型OID而不是syscache条目
Oid typenameTypeId(ParseState* pstate, const TypeName* typname)
{
Oid typoid;
Type tup;
tup = typenameType(pstate, typname, NULL);
typoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
return typoid;
}
typenameTypeIdAndMod
typenameTypeIdAndMod——给定一个TypeName,返回该类型的OID和typmod这相当于typenameType,但我们只返回类型OID和typmod,而不是syscache条目。
void typenameTypeIdAndMod(ParseState* pstate, const TypeName* typname, Oid* typeid_p, int32* typmod_p)
{
Type tup;
tup = typenameType(pstate, typname, typmod_p);
*typeid_p = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
}
TypeNameToString
TypeNameToString生成一个表示TypeName名称的字符串。这必须工作的typename,不描述任何实际类型,它主要用于报告查找错误。
char* TypeNameToString(const TypeName* typname)
{
StringInfoData string;
initStringInfo(&string);
appendTypeNameToBuffer(typname, &string);
return string.data;
}
TypeNameListToString
TypeNameListToString生成一个字符串,表示类型名列表的名称.
char* TypeNameListToString(List* typenames)
{
StringInfoData string;
ListCell* l = NULL;
initStringInfo(&string);
foreach (l, typenames) {
TypeName* typname = (TypeName*)lfirst(l);
AssertEreport(IsA(typname, TypeName), MOD_OPT, "");
if (l != list_head(typenames)) {
appendStringInfoChar(&string, ',');
}
appendTypeNameToBuffer(typname, &string);
}
return string.data;
}
LookupCollation
LookupCollation根据名称查找排序规则,返回OID,支持错误定位。
Oid LookupCollation(ParseState* pstate, List* collnames, int location)
{
Oid colloid;
ParseCallbackState pcbstate;
if (pstate != NULL) {
setup_parser_errposition_callback(&pcbstate, pstate, location);
}
colloid = get_collation_oid(collnames, false);
if (pstate != NULL) {
cancel_parser_errposition_callback(&pcbstate);
}
return colloid;
}
typenameTypeMod
typenameTypeMod -给定一个TypeName,返回内部的typmod值。如果TypeName包含数据类型不合法的类型修饰符,则会抛出错误。TypeName所表示的实际类型OID必须已经被查找过,并被传递为“typ”。pstate仅用于错误定位信息,可能为NULL。
static int32 typenameTypeMod(ParseState* pstate, const TypeName* typname, Type typ)
{
int32 result;
Oid typmodin;
Datum* datums = NULL;
int n;
ListCell* l = NULL;
ArrayType* arrtypmod = NULL;
ParseCallbackState pcbstate;
/* 如果没有typmod表达式,则返回预先指定的typmod */
if (typname->typmods == NIL) {
return typname->typemod;
}
/*
* 否则,type最好接受typmods。我们为shell类型的情况提供了一个特殊的错误消息,因为shell不可能有typmodin函数。
*/
if (!((Form_pg_type)GETSTRUCT(typ))->typisdefined) {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifier cannot be specified for shell type \"%s\"", TypeNameToString(typname)),
parser_errposition(pstate, typname->location)));
}
typmodin = ((Form_pg_type)GETSTRUCT(typ))->typmodin;
if (typmodin == InvalidOid) {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifier is not allowed for type \"%s\"", TypeNameToString(typname)),
parser_errposition(pstate, typname->location)));
}
/*
* 将原始语法输出表达式列表转换为一个cstring数组.
* 目前,我们允许简单的数值常量、字符串字面量和标识符;这个列表可能还会扩展.
*/
datums = (Datum*)palloc(list_length(typname->typmods) * sizeof(Datum));
n = 0;
foreach (l, typname->typmods) {
Node* tm = (Node*)lfirst(l);
char* cstr = NULL;
if (IsA(tm, A_Const)) {
A_Const* ac = (A_Const*)tm;
if (IsA(&ac->val, Integer)) {
const int len = 32;
cstr = (char*)palloc0(len);
errno_t rc = snprintf_s(cstr, len, len - 1, "%ld", (long)ac->val.val.ival);
securec_check_ss(rc, "", "");
} else if (IsA(&ac->val, Float) || IsA(&ac->val, String)) {
/* 我们可以直接使用STR字段. */
cstr = ac->val.val.str;
}
} else if (IsA(tm, ColumnRef)) {
ColumnRef* cr = (ColumnRef*)tm;
if (list_length(cr->fields) == 1 && IsA(linitial(cr->fields), String)) {
cstr = strVal(linitial(cr->fields));
}
}
if (cstr == NULL) {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifiers must be simple constants or identifiers"),
parser_errposition(pstate, typname->location)));
}
datums[n++] = CStringGetDatum(cstr);
}
/* 关于cstring的表示细节的硬连线知识 */
arrtypmod = construct_array(datums, n, CSTRINGOID, -2, false, 'c');
/* 安排在类型的typmodin函数失败时报告位置 */
setup_parser_errposition_callback(&pcbstate, pstate, typname->location);
result = DatumGetInt32(OidFunctionCall1(typmodin, PointerGetDatum(arrtypmod)));
cancel_parser_errposition_callback(&pcbstate);
pfree_ext(datums);
pfree_ext(arrtypmod);
return result;
}
appendTypeNameToBuffer
appendTypeNameToBuffer将表示TypeName的名称的字符串附加到StringInfo。这是TypeNameToString和TypeNameListToString的共享内核。NB:这必须工作的typename,不描述任何实际类型;它主要用于报告查找错误。
static void appendTypeNameToBuffer(const TypeName* typname, StringInfo string)
{
if (typname->names != NIL) {
/* 发出可能限定的名称原样 */
ListCell* l = NULL;
foreach (l, typname->names) {
if (l != list_head(typname->names)) {
appendStringInfoChar(string, '.');
}
appendStringInfoString(string, strVal(lfirst(l)));
}
} else {
/* 查找内部指定的类型 */
appendStringInfoString(string, format_type_be(typname->typeOid));
}
/*
* 根据需要添加装饰,但只针对LookupTypeName考虑的字段
*/
if (typname->pct_type) {
appendStringInfoString(string, "%TYPE");
}
if (typname->arrayBounds != NIL) {
appendStringInfoString(string, "[]");
}
}