Postgresql源码(88)column definition list语义解析流程分析

news2025/7/23 15:26:51

0 总结

如果调用函数时同时满足以下几种情况

  1. 在from后面。
  2. 返回值为RECORD(或者是anyelement表示的RECORD)(anyelement的实际类型由入参决定,入参是RECORD,返回就是RECORD)。
  3. 返回值被判定为TYPEFUNC_RECORD(普通的RECORD,没有行描述符)。
  4. 函数后面不带列定义(column definition list)。

就会报错:ERROR: a column definition list is required for functions returning "record"

所以一个返回RECORD类型的函数:

  • 要么自己返回带格式的record(TYPEFUNC_COMPOSITE)
  • 要么在from后面加上列定义例如from func(1,2,3) as q(a int, b int)(as后面就是column definition list)

主流程总结?

-- SQL1:报错
SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');
	
-- SQL2:列定义从column definition list获取
SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

-- SQL3:列定义从null::person获取
SELECT * FROM json_populate_record(
	null::person, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');

语义分析transformRangeFunction中

  1. 对于函数表达式的解析transformExpr给出的结果中,可以发现SQL1、2的函数返回值是record、SQL3的返回值是person
  2. 继续构造行描述符TupleDesc,来源有两个地方:函数返回TupleDesc或SQL中有column definition list。SQL1都没有直接报错。
  3. SQL2的TupleDesc来自于column definition list;SQL3的来自于get_expr_result_type返回的tupdesc。

get_expr_result_type对于SQL2给出TYPEFUNC_RECORD的结果,表示缺失描述符
get_expr_result_type对于SQL3给出TYPEFUNC_COMPOSITE的结果,表示存在描述符,并返回tupdesc

get_expr_result_type是如何判断的?

1、基于transformExpr返回的FuncExpr里面存放的返回值类型。
2、FuncExpr里面存放的返回值类型的判断逻辑是:如果是多态函数(有anyelement的参数),那么anyelement传入的实际类型是什么,返回值就是什么。


1 案例

json_populate_record函数功能

json_populate_record ( base anyelement, from_json json ) → anyelement

  • 按base提供的record模式,from_json提供的数据,拼接元组并返回。(需要object形式的json;按key与列名匹配的规则填充数据)
  • 如果json中字段不全,使用base提供的数据填充。
CREATE TYPE address as (country TEXT, city TEXT);
CREATE TYPE person as (name TEXT, age INT, hobbies TEXT[], address address);

场景一:base提供列定义、json提供全量数据

SELECT * FROM json_populate_record(
	null::person, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');

 name | age |    hobbies    |   address    
------+-----+---------------+--------------
 Tom  |  20 | {sports,cars} | (CN,BeiJing)

场景二:base提供列定义、json提供部分数据、base补全剩余数据

 
SELECT * FROM json_populate_record(
	('x', 0, ARRAY['sports'], ('CN', 'BeiJing'))::person,
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"]}');

 name | age |    hobbies    |   address    
------+-----+---------------+--------------
 Tom  |  20 | {sports,cars} | (CN,BeiJing)

场景三:column definition list提供列定义、json提供全部数据

SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

 name | age |    hobbies    |   address    
------+-----+---------------+--------------
 Tom  |  20 | {sports,cars} | (CN,BeiJing)

场景四:column definition list提供列定义、base提供全部数据(非预期)

SELECT * FROM json_populate_record(
	('x'::text, 0, ARRAY['sports'], ('CN', 'BeiJing')::address)::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

 name | age | hobbies  |   address    
------+-----+----------+--------------
 x    |   0 | {sports} | (CN,BeiJing)

2 column definition list是什么?

从上面案例场景一、三中可以发现,列定义有两种方式提供:

1、从函数参数来:null::person

SELECT * FROM json_populate_record(
	null::person, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');
	
-- 返回值类型:person
select pg_typeof(json_populate_record(null::person, '{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}'));
 pg_typeof 
-----------
 person

2、从column definition list来:as q(name TEXT, age INT, hobbies TEXT[], address address)

SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

3 column definition list流程分析

下面对语义分析中,“from后面的函数” 的处理流程展开分析:transformRangeFunction

下面三个SQL执行进入transformRangeFunction时,参数有所区别:

-- SQL1:报错
SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');
	
-- SQL2:列定义从column definition list获取
SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

-- SQL3:列定义从null::person获取
SELECT * FROM json_populate_record(
	null::person, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');

入参:
在这里插入图片描述
可以看到SQL1(报错)和SQL3在入参中都没有coldeflist,但SQL1会报错,SQL3解析成功,原因需要分析下transformRangeFunction的逻辑。

3.1 transformRangeFunction:SQL123解析差异(主流程)

static ParseNamespaceItem *
transformRangeFunction(ParseState *pstate, RangeFunction *r)
{
	...
	foreach(lc, r->functions)
	{
		List	   *pair = (List *) lfirst(lc);
		Node	   *fexpr;
		List	   *coldeflist;
		Node	   *newfexpr;
		Node	   *last_srf;

		/* Disassemble the function-call/column-def-list pairs */
		Assert(list_length(pair) == 2);
		fexpr = (Node *) linitial(pair);
		coldeflist = (List *) lsecond(pair);

		...// 处理unnest

		/* normal case ... */
		newfexpr = transformExpr(pstate, fexpr,
								 EXPR_KIND_FROM_FUNCTION);

注意:transformExpr出来的结果中SQL2与SQL3的funcresulttype不同!

transformExpr最后展开讲。

  • SQL1:{xpr = {type = T_FuncExpr}, funcid = 3960, funcresulttype = 2249, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0, inputcollid = 0, args = 0x29f2aa0, location = 14}
  • SQL2:{xpr = {type = T_FuncExpr}, funcid = 3960, funcresulttype = 2249, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0, inputcollid = 0, args = 0x29f31a8, location = 14}
  • SQL3:{xpr = {type = T_FuncExpr}, funcid = 3960, funcresulttype = 16424, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0, inputcollid = 0, args = 0x29f2aa0, location = 14}
		
		funcexprs = lappend(funcexprs, newfexpr);
		funcnames = lappend(funcnames,
							FigureColname(fexpr));
		coldeflists = lappend(coldeflists, coldeflist);
	}

coldeflists为空List进入addRangeTableEntryForFunction

	return addRangeTableEntryForFunction(pstate,
										 funcnames, funcexprs, coldeflists,
										 r, is_lateral, true);
}

进入addRangeTableEntryForFunction,开始构造:RangeTblEntry

ParseNamespaceItem *
addRangeTableEntryForFunction(ParseState *pstate,
							  List *funcnames,
							  List *funcexprs,
							  List *coldeflists,
							  RangeFunction *rangefunc,
							  bool lateral,
							  bool inFromCl)
{
	...

	rte->rtekind = RTE_FUNCTION;
	rte->relid = InvalidOid;
	rte->subquery = NULL;
	rte->functions = NIL;		/* we'll fill this list below */
	rte->funcordinality = rangefunc->ordinality;
	rte->alias = alias;

记录别名,只有SQL2的别名:q

	if (alias)
		aliasname = alias->aliasname;
	else
		aliasname = linitial(funcnames);

	eref = makeAlias(aliasname, NIL);
	rte->eref = eref;

准备TupleDesc,元组描述符 就是 列定义。

	/* Process each function ... */
	functupdescs = (TupleDesc *) palloc(nfuncs * sizeof(TupleDesc));

	totalatts = 0;
	funcno = 0;
	forthree(lc1, funcexprs, lc2, funcnames, lc3, coldeflists)
	{
		Node	   *funcexpr = (Node *) lfirst(lc1);
		char	   *funcname = (char *) lfirst(lc2);
		List	   *coldeflist = (List *) lfirst(lc3);
		RangeTblFunction *rtfunc = makeNode(RangeTblFunction);
		TypeFuncClass functypclass;
		Oid			funcrettype;

		/* Initialize RangeTblFunction node */
		rtfunc->funcexpr = funcexpr;
		rtfunc->funccolnames = NIL;
		rtfunc->funccoltypes = NIL;
		rtfunc->funccoltypmods = NIL;
		rtfunc->funccolcollations = NIL;
		rtfunc->funcparams = NULL;	/* not set until planning */

get_expr_result_type拿到的结果不同

  • SQL1:functypclass=TYPEFUNC_RECORD
  • SQL2:functypclass=TYPEFUNC_RECORD
  • SQL3:functypclass=TYPEFUNC_COMPOSITE
		functypclass = get_expr_result_type(funcexpr,
											&funcrettype,
											&tupdesc);

		if (coldeflist != NIL)
		{
			...
		}
		else
		{

【SQL1】【SQL1】【SQL1】
SQL1只能到这里了,因为SQL1的类型为TYPEFUNC_RECORD、而且没有coldeflist。

			if (functypclass == TYPEFUNC_RECORD)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("a column definition list is required for functions returning \"record\""),
						 parser_errposition(pstate, exprLocation(funcexpr))));
		}

		if (functypclass == TYPEFUNC_COMPOSITE ||
			functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
		{

【SQL3】【SQL3】【SQL3】
SQL3返回确定性的结果TYPEFUNC_COMPOSITE,进入这个分支。

TupleDesc由get_expr_result_type内部拼接好直接返回的,不用SQL2一样进入CreateTemplateTupleDesc拼接。

			/* Composite data type, e.g. a table's row type */
			Assert(tupdesc);
		}
		else if (functypclass == TYPEFUNC_SCALAR)
		{
			/* Base data type, i.e. scalar */
			tupdesc = CreateTemplateTupleDesc(1);
			TupleDescInitEntry(tupdesc,
							   (AttrNumber) 1,
							   chooseScalarFunctionAlias(funcexpr, funcname,
														 alias, nfuncs),
							   funcrettype,
							   exprTypmod(funcexpr),
							   0);
			TupleDescInitEntryCollation(tupdesc,
										(AttrNumber) 1,
										exprCollation(funcexpr));
		}

【SQL2】【SQL2】【SQL2】
SQL2返回不确定record:TYPEFUNC_RECORD,from后带列定义,进入这个分支。

		else if (functypclass == TYPEFUNC_RECORD)
		{
			ListCell   *col;

用列定义创建临时的元组描述符。

			tupdesc = CreateTemplateTupleDesc(list_length(coldeflist));
			i = 1;
			foreach(col, coldeflist)
			{
				ColumnDef  *n = (ColumnDef *) lfirst(col);
				char	   *attrname;
				Oid			attrtype;
				int32		attrtypmod;
				Oid			attrcollation;

				attrname = n->colname;
				if (n->typeName->setof)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
							 errmsg("column \"%s\" cannot be declared SETOF",
									attrname),
							 parser_errposition(pstate, n->location)));
				typenameTypeIdAndMod(pstate, n->typeName,
									 &attrtype, &attrtypmod);
				attrcollation = GetColumnDefCollation(pstate, n, attrtype);

初始化描述符的列属性。

				TupleDescInitEntry(tupdesc,
								   (AttrNumber) i,
								   attrname,
								   attrtype,
								   attrtypmod,
								   0);
				TupleDescInitEntryCollation(tupdesc,
											(AttrNumber) i,
											attrcollation);
				rtfunc->funccolnames = lappend(rtfunc->funccolnames,
											   makeString(pstrdup(attrname)));
				rtfunc->funccoltypes = lappend_oid(rtfunc->funccoltypes,
												   attrtype);
				rtfunc->funccoltypmods = lappend_int(rtfunc->funccoltypmods,
													 attrtypmod);
				rtfunc->funccolcollations = lappend_oid(rtfunc->funccolcollations,
														attrcollation);

				i++;
			}
			CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE,
									 CHKATYPE_ANYRECORD);
		}

		/* Finish off the RangeTblFunction and add it to the RTE's list */
		rtfunc->funccolcount = tupdesc->natts;
		rte->functions = lappend(rte->functions, rtfunc);

		/* Save the tupdesc for use below */
		functupdescs[funcno] = tupdesc;
		totalatts += tupdesc->natts;
		funcno++;
	}
	...
	return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
									tupdesc);
}

3.2 get_expr_result_type:如何判断函数返回值(分支流程)

-- SQL2:列定义从column definition list获取
SELECT * FROM json_populate_record(
	null::record, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}')
as q(name TEXT, age INT, hobbies TEXT[], address address);

-- SQL3:列定义从null::person获取
SELECT * FROM json_populate_record(
	null::person, 
	'{"name": "Tom", "age": 20, "hobbies": ["sports", "cars"], "address": {"country": "CN", "city": "BeiJing"}}');

为什么get_expr_result_type拿到的结果不同?

  • SQL2:functypclass=TYPEFUNC_RECORD
  • SQL3:functypclass=TYPEFUNC_COMPOSITE

get_expr_result_type

get_expr_result_type
  internal_get_result_type
    tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid))
    procform = (Form_pg_proc) GETSTRUCT(tp)
    rettype = procform->prorettype                         -- 【SQL2】【SQL3】2283:anyelement
    tupdesc = build_function_result_tupdesc_t              -- 【SQL2】【SQL3】返回类型不是RECORDOID就会返回NULL

	if (IsPolymorphicType(rettype))    -- 开始解析返回值
		Oid newrettype = exprType(call_expr)  -- 【SQL2】从expr->funcresulttype拿真正返回值2249:record
		                                      -- 【SQL3】从expr->funcresulttype拿真正返回值16424:person
		rettype = newrettype;

	if (resultTypeId)
		*resultTypeId = rettype;	  -- 2249
	if (resultTupleDesc)
		*resultTupleDesc = NULL;

	result = get_type_func_class(rettype, &base_rettype)
		switch get_typtype(typid)      // 【SQL2】找到2249的基础类型:typtype='p'=TYPTYPE_PSEUDO
		                               // 【SQL3】找到16424的基础类型:typtype='c'=TYPTYPE_COMPOSITE
			case TYPTYPE_PSEUDO:
				if (typid == RECORDOID)
					return TYPEFUNC_RECORD;  // 【SQL2】
			case TYPTYPE_COMPOSITE:
				return TYPEFUNC_COMPOSITE;   // 【SQL3】
	
	switch result 
		case TYPEFUNC_RECORD:                //【SQL2】
			break;
		case TYPEFUNC_COMPOSITE_DOMAIN:
			*resultTupleDesc = lookup_rowtype_tupdesc_copy(base_rettype, -1)  // 【SQL3】base_rettype=16424
	
	return result;  // 【SQL2】TYPEFUNC_RECORD
	                // 【SQL3】TYPTYPE_COMPOSITE
		
      

从上述分析中可以看出根因差别在FuncExpr的不同:

SQL2
FuncExpr = {xpr = {type = T_FuncExpr}, funcid = 3960, funcresulttype = 2249, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0, inputcollid = 0, args = 0x29f31a8, location = 14}

SQL3
FuncExpr = {xpr = {type = T_FuncExpr}, funcid = 3960, funcresulttype = 16424, funcretset = false, funcvariadic = false, funcformat = COERCE_EXPLICIT_CALL, funccollid = 0, inputcollid = 0, args = 0x29f2aa0, location = 14}

继续往前追溯FuncExpr是在哪拼出来的:transformExpr

  • func_get_detail:处理实际入参、处理默认参数;SQL2、3相同。
  • enforce_generic_type_consistency:
transformExpr
  transformExprRecurse
    transformFuncCall
      ParseFuncOrColumn
        ...
		
		//【SQL3】{16424,705,16}     【SQL2】{2249, 705, 16}
		foreach(l, fargs)
			Node *arg = lfirst(l);
			Oid argtype = exprType(arg);
			actual_arg_types[nargs++] = argtype;
		
        //【SQL2】fdresult = FUNCDETAIL_NORMAL  rettype = 2283(anyelement)
        //【SQL3】fdresult = FUNCDETAIL_NORMAL  rettype = 2283(anyelement)
        fdresult = func_get_detail(&rettype) // 返回rettype
  		
  		// enforce_generic_type_consistency
  		//   直接返回actual_arg_types多态参数位置(第一个参数)
        //【SQL2】返回值rettype = 2249(record)
        //【SQL3】返回值rettype = 16424(person)
        rettype = enforce_generic_type_consistency( // 入参
        	actual_arg_types,		//【SQL3】{16424,705,16}     【SQL2】{2249, 705, 16}
        	declared_arg_types,     //【SQL3】{2283,114,16}      【SQL2】{2283,114,16}
        	nargsplusdefs,          //【SQL3】3                  【SQL2】3
        	rettype,                //【SQL3】2283(anyelement)   【SQL2】2283(anyelement)
        	false)
        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/16925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.18 - 每日一题 - 408

每日一句:不如就利用孤单一人的时间,使自己变得更优秀,给来的人一个惊喜,也给自己一个好的交代 数据结构 1 当一棵有n个结点的二叉树按层次从上到下,同层次从左到右将结点中的数据存放在一维数组A[1…n]中…

Android App事件交互Event之模仿京东App实现下拉刷新功能(附源码 可直接使用)

运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一、正常下拉与下拉刷新的冲突处理 电商App的首页通常都支持下拉刷新,比如京东首页的头部轮播图一直顶到系统的状态栏,并且页面下拉到顶后,继续下拉会拉出带有下拉刷新字样的布局&#x…

leaflet教程039: 只显示一屏地图,设定范围不让循环延展

第039个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet只显示一屏地图,并且根据maxBounds和bounds的设定,来改变不同的地图呈现状态。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共68行)心得总结相…

[附源码]java毕业设计期刊在线投稿平台

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

小程序基础原理

前言 本文会围绕小程序的基础原理进行介绍。主要包括小程序的基础结构、编译、加载、通讯等几个方面。旨在阅读完毕后可以对小程序有一个基本的印象。 一、基础 对于用户来讲,小程序无需下载、用完即走、体验良好。 对于开发者来讲,小程序主要是区别…

同花顺_代码解析_技术指标_M

本文通过对同花顺中现成代码进行解析,用以了解同花顺相关策略设计的思想 目录 MACD MACDFS MARSI MASS MAVOL MCL MCO MFI MI MICD MIKE MTM MTMFS MACD 指数平滑异同平均线 MACD指标说明 MACD指数平滑异同移动平均线为两条长、短的平滑平均线。 其…

拉取多CPU架构容器镜像推送到其他仓库

一、背景 一个docker镜像可能会有多种CPU架构的变体,有时需要把这些多架构的镜像全部从公共镜像库(如: hub.docker.com )同步到自建的库上。 二、解决方法 使用 docker buildx 多架构打包机制,进行重制推送&#xf…

Bean 作用域和生命周期

一 : Bean的默认作用域 Bean 默认情况下是单例状态(singleton),所有人使用的都是同一个对象.举例理解Bean的单例状态 : 假设现在有一个公共的 Bean,提供给 A 用户和 B 用户使用,然而在使用的途中 A 用户却“悄悄”地修 改了公共 Bean 的数据&#xff0…

[附源码]SSM计算机毕业设计中医药系统论文2022JAVA

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

快速清除PPT缓存文件或C盘隐藏大文件

是否遇到过电脑的磁盘空间越来越满, 请注意这些临时文件可谓是C盘的存储杀手,无形中吞噬掉很多磁盘空间,这里以PPT为例: C:\Users\Admin\AppData\Roaming\Microsoft\PowerPoint\ 1.首先在PPT2019软件中,打开一个PPT文…

实现BIO多客户端通讯模式

实现BIO多客户端通讯模式背景思想实现服务端线程类服务端客户端结果背景 如果服务端需要处理很多个客户端的消息通信请求应该如何处理呢,此时我们就需要在服务端引入线程了,也就是说客户端每发起一个请求,服务端就创建一个新的线程来处理这个…

PyTorch搭建基于图神经网络(GCN)的天气推荐系统(附源码和数据集)

需要源码和数据集请点赞关注收藏后评论区留言~~~ 一、背景 极端天气情况一直困扰着人们的工作和生活。部分企业或者工种对极端天气的要求不同,但是目前主流的天气推荐系统是直接将天气信息推送给全部用户。这意味着重要的天气信息在用户手上得不到筛选,…

(C语言)背答案

[#4练习赛]背答案 题目描述 传智专修学院“Java程序设计”的期末考试来源于一个选择库,共有 nnn 道题目,每道题目由问题和答案组成,都是一个字符串,保证所有题目题面互不相同。这个题库已经发给同学进行备考准备。 正式考试中&…

Labview+STM32无线温湿度采集

一.介绍 该项目采用正点原子的STM32ZET6精英板DHT11温湿度模块泽耀科技的无线串口作为下位机,Labview无线串口作为上位机读取下位机发来的数据并处理。 泽耀科技的产品是我在开发过程中经常用到的,他们不仅产品做的非常不错,而且资料齐全售后…

远离cmd,拥抱powershell

简介:cmd命令提示符是在操作系统中,提示进行命令输入的一种工作提示符。在不同的操作系统环境下,命令提示符各不相同。 在windows环境下,命令行程序为cmd.exe,是一个32位的命令行程序,微软Windows系统基于W…

动态规划--区间dp

区间dp题目列表:(1)石子合并(2)环形石子合并(3)能量项链(4)加分二叉树(5)凸多边形的划分(6)棋盘分割题目列表: (1)石子合并 在复习石子合并之前,为了直接进入专题“区间dp“,做一个区间dp的基础题,这个题目具有代表性…

1.2 Android 5.0 的特点

和其他版本相比, Android 5.0 的突出特性如下所示。 (1)全新的 Material 界面设计 Android 5.0 Lollipop 界面设计的灵感来源于自然、 物理学 以及基于打印效果的粗体、图标化的设计,换句话说,它的设 计是一种基于高品…

智慧建筑BIM解决方案-最新全套文件

智慧建筑BIM解决方案-最新全套文件一、建设背景为什么要发展智慧建筑二、思路架构三、建设方案智慧建筑建设时应考虑下面3个方面:1、减少耗能,促进资源利用效率2、优化工作和生活环境3、确保运营安全可靠四、获取 - 智慧建筑BIM全套最新解决方案合集一、…

m超外差单边带接收机的simulink仿真

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 超外差是利用本地产生的振荡波与输入信号混频,将输入信号频率变换为某个预先确定的频率的方法。这种方法是为了适应远程通信对高频率、弱信号接收的需要,在外差原…

基于springboot在线玩具商城交易平台的设计与实现

随着科技创新不断突破玩具界限,特别是随着智能时代到来,电子游戏的兴起对传统玩具行业带来了冲击,智能玩具应运而生,成为新产品方向。智能玩具受消费者青睐, 随着电子商务的发展,其在我国的经济地位越来越…