postgres源码解析38 表创建执行全流程梳理--2

news2024/11/25 4:23:03

本小节主要讲解查询执行模块,有机地调用存储、索引、事务、并发等模块,按照执行计划中的计划节点(操作执行接口)完成数据的读取或者修改。知识回顾:postgres源码解析37 表创建执行全流程梳理–1

关键数据结构

总图:
在这里插入图片描述
在这里插入图片描述

1 PortalData
postgres为每条SQL创建一个portal结构体存储查询计划树链表和执行策略等信息:

 typedef struct PortalData *Portal;
 typedef struct PortalData
{
	/* Bookkeeping data */
	const char *name;			/* portal's name */
	const char *prepStmtName;	/* source prepared statement (NULL if none) */
	MemoryContext portalContext;	/* subsidiary memory for portal */  // portal上下文
	ResourceOwner resowner;		/* resources owned by portal */			// portal持有的资源
	void		(*cleanup) (Portal portal); /* cleanup hook */			// 清理函数

	/*
	 * State data for remembering which subtransaction(s) the portal was
	 * created or used in.  If the portal is held over from a previous
	 * transaction, both subxids are InvalidSubTransactionId.  Otherwise,
	 * createSubid is the creating subxact and activeSubid is the last subxact
	 * in which we ran the portal.
	 */
	SubTransactionId createSubid;	/* the creating subxact */
	SubTransactionId activeSubid;	/* the last subxact with activity */ 

	/* The query or queries the portal will execute */
	const char *sourceText;		/* text of query (as of 8.4, never NULL) */  // 输入的SQL语句
	CommandTag	commandTag;		/* command tag for original query */		 // Tag标识 
	QueryCompletion qc;			/* command completion data for executed query */
	List	   *stmts;			/* list of PlannedStmts */					// PlannedStmt结构体
	CachedPlan *cplan;			/* CachedPlan, if stmts are from one */		// Cache plan

	ParamListInfo portalParams; /* params to pass to query */              	// portal参数链表
	QueryEnvironment *queryEnv; /* environment for query */					// 查询环境

	/* Features/options */
	PortalStrategy strategy;	/* see above */								// 策略
	int			cursorOptions;	/* DECLARE CURSOR option bits */			// 游标 bits
	bool		run_once;		/* portal will only be run once */          //是否运行一次

	/* Status data */
	PortalStatus status;		/* see above */								// portal状态
	bool		portalPinned;	/* a pinned portal can't be dropped */		// 是否被 pinned
	bool		autoHeld;		/* was automatically converted from pinned to
								 * held (see HoldPinnedPortals()) */

	/* If not NULL, Executor is active; call ExecutorEnd eventually: */
	QueryDesc  *queryDesc;		/* info needed for executor invocation */		/// 查询描述符

	/* If portal returns tuples, this is their tupdesc: */
	TupleDesc	tupDesc;		/* descriptor for result tuples */				// 元组描述符
	/* and these are the format codes to use for the columns: */
	int16	   *formats;		/* a format code for each column */				// 列形式

	/*
	 * Outermost ActiveSnapshot for execution of the portal's queries.  For
	 * all but a few utility commands, we require such a snapshot to exist.
	 * This ensures that TOAST references in query results can be detoasted,
	 * and helps to reduce thrashing of the process's exposed xmin.
	 */
	Snapshot	portalSnapshot; /* active snapshot, or NULL if none */     // 快照信息

	/*
	 * Where we store tuples for a held cursor or a PORTAL_ONE_RETURNING or
	 * PORTAL_UTIL_SELECT query.  (A cursor held past the end of its
	 * transaction no longer has any active executor state.)
	 */
	Tuplestorestate *holdStore; /* store for holdable cursors */
	MemoryContext holdContext;	/* memory containing holdStore */

	/*
	 * Snapshot under which tuples in the holdStore were read.  We must keep a
	 * reference to this snapshot if there is any possibility that the tuples
	 * contain TOAST references, because releasing the snapshot could allow
	 * recently-dead rows to be vacuumed away, along with any toast data
	 * belonging to them.  In the case of a held cursor, we avoid needing to
	 * keep such a snapshot by forcibly detoasting the data.
	 */
	Snapshot	holdSnapshot;	/* registered snapshot, or NULL if none */

	/*
	 * atStart, atEnd and portalPos indicate the current cursor position.
	 * portalPos is zero before the first row, N after fetching N'th row of
	 * query.  After we run off the end, portalPos = # of rows in query, and
	 * atEnd is true.  Note that atStart implies portalPos == 0, but not the
	 * reverse: we might have backed up only as far as the first row, not to
	 * the start.  Also note that various code inspects atStart and atEnd, but
	 * only the portal movement routines should touch portalPos.
	 */
	bool		atStart;        
	bool		atEnd;
	uint64		portalPos;

	/* Presentation data, primarily used by the pg_cursors system view */
	TimestampTz creation_time;	/* time at which this portal was defined */
	bool		visible;		/* include this portal in pg_cursors? */
}			PortalData;

根据SQL语句的类型的,pg提供5种执行策略:
1)PORTAL_ONE_SELECT:针对单一SELECT语句,支持游标操作
2)PORTAL_ONE_RETURNING:针对含有RETURNING子句的单一 INSERT/DETELE/UPDATE语句。查询执行时,将首先处理所有条件的元组,在执行过程中缓存,最后返回给客户端
3)PORTAL_ONE_MOD_WITH:针对含有data-modifying CTEs 的单一SELECT语句查询
4)PORTAL_UTIL_SELECT: 针对功能性语句但返回结果类似SELECT语句,如EXPLAIN/SHOW
5)PORTAL_MULTI_QUERY: 除上述4中类型之外的语句,这个策略具有一般性,能够处理一个或者多个操作

typedef enum PortalStrategy
{
	PORTAL_ONE_SELECT,
	PORTAL_ONE_RETURNING,
	PORTAL_ONE_MOD_WITH,
	PORTAL_UTIL_SELECT,
	PORTAL_MULTI_QUERY
} PortalStrategy;

PlannedStmt 计划树头节点为plannnedStmt数据结构,其保存执行器所需的一些信息:语句类型(commandType),查询计划树根节点(planTree)、查询涉及的范围表(rtable)和结果关系表(resultRelation)

typedef struct PlannedStmt
{
	NodeTag		type;

	CmdType		commandType;	/* select|insert|update|delete|utility */

	uint64		queryId;		/* query identifier (copied from Query) */

	bool		hasReturning;	/* is it insert|update|delete RETURNING? */ 

	bool		hasModifyingCTE;	/* has insert|update|delete in WITH? */  

	bool		canSetTag;		/* do I set the command result tag? */

	bool		transientPlan;	/* redo plan when TransactionXmin changes? */

	bool		dependsOnRole;	/* is plan specific to current role? */

	bool		parallelModeNeeded; /* parallel mode required to execute? */ // 并行模式

	int			jitFlags;		/* which forms of JIT should be performed */

	struct Plan *planTree;		/* tree of Plan nodes */

	List	   *rtable;			/* list of RangeTblEntry nodes */  // 范围表

	/* rtable indexes of target relations for INSERT/UPDATE/DELETE */
	List	   *resultRelations;	/* integer list of RT indexes, or NIL */

	List	   *appendRelations;	/* list of AppendRelInfo nodes */

	List	   *subplans;		/* Plan trees for SubPlan expressions; note
								 * that some could be NULL */

	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */

	List	   *rowMarks;		/* a list of PlanRowMark's */

	List	   *relationOids;	/* OIDs of relations the plan depends on */  // oid

	List	   *invalItems;		/* other dependencies, as PlanInvalItems */

	List	   *paramExecTypes; /* type OIDs for PARAM_EXEC Params */

	Node	   *utilityStmt;	/* non-null if this is utility stmt */ // 功能性语句

	/* statement location in source string (copied from Query) */
	int			stmt_location;	/* start location, or -1 if unknown */ 
	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
} PlannedStmt;

plan的子类节点通过lefttree和righttree字段完成整个查询计划,其根节点指针被保存在PlannedStmt结构体中。包含代价等信息(执行计划的生成的基于CBO生成的)、投影以及位图参数等信息

typedef struct Plan
{
	NodeTag		type;

	/*
	 * estimated execution costs for plan (see costsize.c for more info)
	 */
	Cost		startup_cost;	/* cost expended before fetching any tuples */
	Cost		total_cost;		/* total cost (assuming all tuples fetched) */

	/*
	 * planner's estimate of result size of this plan step
	 */
	double		plan_rows;		/* number of rows plan is expected to emit */
	int			plan_width;		/* average row width in bytes */

	/*
	 * information needed for parallel query
	 */
	bool		parallel_aware; /* engage parallel-aware logic? */
	bool		parallel_safe;	/* OK to use as part of parallel plan? */

	/*
	 * information needed for asynchronous execution
	 */
	bool		async_capable;	/* engage asynchronous-capable logic? */

	/*
	 * Common structural data for all Plan types.
	 */
	int			plan_node_id;	/* unique across entire final plan tree */
	List	   *targetlist;		/* target list to be computed at this node */
	List	   *qual;			/* implicitly-ANDed qual conditions */
	struct Plan *lefttree;		/* input plan tree(s) */
	struct Plan *righttree;
	List	   *initPlan;		/* Init Plan nodes (un-correlated expr
								 * subselects) */

	/*
	 * Information for management of parameter-change-driven rescanning
	 *
	 * extParam includes the paramIDs of all external PARAM_EXEC params
	 * affecting this plan node or its children.  setParam params from the
	 * node's initPlans are not included, but their extParams are.
	 *
	 * allParam includes all the extParam paramIDs, plus the IDs of local
	 * params that affect the node (i.e., the setParams of its initplans).
	 * These are _all_ the PARAM_EXEC params that affect this node.
	 */
	Bitmapset  *extParam;
	Bitmapset  *allParam;
} Plan;

plan节点在执行过程过会生成对应的PlanState结构指针,计划节点的PlanState结构也会按照查询计划树的结构组织成计划节点执行状态树,

typedef struct PlanState
{
	NodeTag		type;

	Plan	   *plan;			/* associated Plan node */

	EState	   *state;			/* at execution time, states of individual
								 * nodes point to one EState for the whole
								 * top-level plan */

	ExecProcNodeMtd ExecProcNode;	/* function to return next tuple */ // 返回下条元组的函数
	ExecProcNodeMtd ExecProcNodeReal;	/* actual function, if above is a   //真正执行函数
										 * wrapper */

	Instrumentation *instrument;	/* Optional runtime stats for this node */
	WorkerInstrumentation *worker_instrument;	/* per-worker instrumentation */

	/* Per-worker JIT instrumentation */
	struct SharedJitInstrumentation *worker_jit_instrument;

	/*
	 * Common structural data for all Plan types.  These links to subsidiary
	 * state trees parallel links in the associated plan tree (except for the
	 * subPlan list, which does not exist in the plan tree).
	 */
	ExprState  *qual;			/* boolean qual condition */   // 
	struct PlanState *lefttree; /* input plan tree(s) */ 
	struct PlanState *righttree;

	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
								 * subselects) */
	List	   *subPlan;		/* SubPlanState nodes in my expressions */

	/*
	 * State for management of parameter-change-driven rescanning
	 */
	Bitmapset  *chgParam;		/* set of IDs of changed Params */

	/*
	 * Other run-time state needed by most if not all node types.
	 */
	TupleDesc	ps_ResultTupleDesc; /* node's return type */  //元组描述符
	TupleTableSlot *ps_ResultTupleSlot; /* slot for my result tuples */  //存放结果元组槽
	ExprContext *ps_ExprContext;	/* node's expression-evaluation context */// 节点表达式上下文
	ProjectionInfo *ps_ProjInfo;	/* info for doing tuple projection */   // 投影信息

	bool		async_capable;	/* true if node is async-capable */

	/*
	 * Scanslot's descriptor if known. This is a bit of a hack, but otherwise
	 * it's hard for expression compilation to optimize based on the
	 * descriptor, without encoding knowledge about all executor nodes.
	 */
	TupleDesc	scandesc;

	/*
	 * Define the slot types for inner, outer and scanslots for expression
	 * contexts with this state as a parent.  If *opsset is set, then
	 * *opsfixed indicates whether *ops is guaranteed to be the type of slot
	 * used. That means that every slot in the corresponding
	 * ExprContext.ecxt_*tuple will point to a slot of that type, while
	 * evaluating the expression.  If *opsfixed is false, but *ops is set,
	 * that indicates the most likely type of slot.
	 *
	 * The scan* fields are set by ExecInitScanTupleSlot(). If that's not
	 * called, nodes can initialize the fields themselves.
	 *
	 * If outer/inneropsset is false, the information is inferred on-demand
	 * using ExecGetResultSlotOps() on ->righttree/lefttree, using the
	 * corresponding node's resultops* fields.
	 *
	 * The result* fields are automatically set when ExecInitResultSlot is
	 * used (be it directly or when the slot is created by
	 * ExecAssignScanProjectionInfo() /
	 * ExecConditionalAssignProjectionInfo()).  If no projection is necessary
	 * ExecConditionalAssignProjectionInfo() defaults those fields to the scan
	 * operations.
	 */
	const TupleTableSlotOps *scanops;
	const TupleTableSlotOps *outerops;
	const TupleTableSlotOps *innerops;
	const TupleTableSlotOps *resultops;
	bool		scanopsfixed;
	bool		outeropsfixed;
	bool		inneropsfixed;
	bool		resultopsfixed;
	bool		scanopsset;
	bool		outeropsset;
	bool		inneropsset;
	bool		resultopsset;
} PlanState;

TupleDescData结构体记录了元组的属性,引用计数和约束等信息,用于元组各属性的扫描

typedef struct TupleDescData
{
	int			natts;			/* number of attributes in the tuple */
	Oid			tdtypeid;		/* composite type ID for tuple type */
	int32		tdtypmod;		/* typmod for tuple type */
	int			tdrefcount;		/* reference count, or -1 if not counting */
	TupleConstr *constr;		/* constraints, or NULL if none */
	/* attrs[N] is the description of Attribute Number N+1 */
	FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER];
}			TupleDescData;

执行器执行过中会构造全局执行状态信息Estate,该结构体记录了查询涉及的范围表、上下文、用于在节点之间传递元组的全局元组表和元组上下文(每次使用后便会释放)

typedef struct EState
{
	NodeTag		type;

	/* Basic state for all query types: */
	ScanDirection es_direction; /* current scan direction */
	Snapshot	es_snapshot;	/* time qual to use */
	Snapshot	es_crosscheck_snapshot; /* crosscheck time qual for RI */
	List	   *es_range_table; /* List of RangeTblEntry */
	Index		es_range_table_size;	/* size of the range table arrays */
	Relation   *es_relations;	/* Array of per-range-table-entry Relation
								 * pointers, or NULL if not yet opened */
	struct ExecRowMark **es_rowmarks;	/* Array of per-range-table-entry
										 * ExecRowMarks, or NULL if none */
	PlannedStmt *es_plannedstmt;	/* link to top of plan tree */
	const char *es_sourceText;	/* Source text from QueryDesc */

	JunkFilter *es_junkFilter;	/* top-level junk filter, if any */

	/* If query can insert/delete tuples, the command ID to mark them with */
	CommandId	es_output_cid;

	/* Info about target table(s) for insert/update/delete queries: */
	ResultRelInfo **es_result_relations;	/* Array of per-range-table-entry
											 * ResultRelInfo pointers, or NULL
											 * if not a target table */
	List	   *es_opened_result_relations; /* List of non-NULL entries in
											 * es_result_relations in no
											 * specific order */

	PartitionDirectory es_partition_directory;	/* for PartitionDesc lookup */

	/*
	 * The following list contains ResultRelInfos created by the tuple routing
	 * code for partitions that aren't found in the es_result_relations array.
	 */
	List	   *es_tuple_routing_result_relations;

	/* Stuff used for firing triggers: */
	List	   *es_trig_target_relations;	/* trigger-only ResultRelInfos */

	/* Parameter info: */
	ParamListInfo es_param_list_info;	/* values of external params */
	ParamExecData *es_param_exec_vals;	/* values of internal params */

	QueryEnvironment *es_queryEnv;	/* query environment */

	/* Other working state: */
	MemoryContext es_query_cxt; /* per-query context in which EState lives */

	List	   *es_tupleTable;	/* List of TupleTableSlots */

	uint64		es_processed;	/* # of tuples processed */

	int			es_top_eflags;	/* eflags passed to ExecutorStart */
	int			es_instrument;	/* OR of InstrumentOption flags */
	bool		es_finished;	/* true when ExecutorFinish is done */

	List	   *es_exprcontexts;	/* List of ExprContexts within EState */

	List	   *es_subplanstates;	/* List of PlanState for SubPlans */

	List	   *es_auxmodifytables; /* List of secondary ModifyTableStates */

	/*
	 * this ExprContext is for per-output-tuple operations, such as constraint
	 * checks and index-value computations.  It will be reset for each output
	 * tuple.  Note that it will be created only if needed.
	 */
	ExprContext *es_per_tuple_exprcontext;

	/*
	 * If not NULL, this is an EPQState's EState. This is a field in EState
	 * both to allow EvalPlanQual aware executor nodes to detect that they
	 * need to perform EPQ related work, and to provide necessary information
	 * to do so.
	 */
	struct EPQState *es_epq_active;

	bool		es_use_parallel_mode;	/* can we use parallel workers? */

	/* The per-query shared memory area to use for parallel execution. */
	struct dsa_area *es_query_dsa;

	/*
	 * JIT information. es_jit_flags indicates whether JIT should be performed
	 * and with which options.  es_jit is created on-demand when JITing is
	 * performed.
	 *
	 * es_jit_worker_instr is the combined, on demand allocated,
	 * instrumentation from all workers. The leader's instrumentation is kept
	 * separate, and is combined on demand by ExplainPrintJITSummary().
	 */
	int			es_jit_flags;
	struct JitContext *es_jit;
	struct JitInstrumentation *es_jit_worker_instr;
} EState;
执行器函数的调用

在这里插入图片描述

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

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

相关文章

八、【React-Router5】路由组件传参

文章目录1、实现效果2、向路由组件传递参数总览3、修改上一节代码3.1、项目结构变更如下3.2、CODE(params传参)3.2.1、Messages.jsx3.2.2、Detail.jsx3.2.3、Result3.3、CODE(search传参)3.3.1、Messages.jsx3.3.2、Detail.jsx3.3…

【强化学习论文】小样本策略泛化的提示决策转换器

文献题目:Prompting Decision Transformer for Few-Shot Policy Generalization 摘要 人类可以利用先前的经验并从少量演示中学习新任务。与旨在通过更好的算法设计实现快速适应的离线元强化学习相比,我们研究了架构归纳偏差对少样本学习能力的影响。我…

RationalDMIS2022车削件(轴类)测量:回转体检测

1.坐标系知识 在三坐标测量机的使用中,坐标系的建立是最为关键的一步,是工件测量的基本依据。设备在组装本身中有着XYZ三个互相垂直的坐标轴,因此坐标系的建立便是通过以工件上的特征作为依据建立XYZ三个互相垂直的坐标轴与设备的坐标轴进行对应,从而使程序得以运行。 物…

虚拟机安装

虚拟机安装 能看到这里说明你的电脑中已经安装的有虚拟机了,如果没有虚拟机可以先下载安装一些虚拟机。 有了虚拟机后首先要准备的就是镜像,我用的是CentOS-7。如果没有的也可以百度或者去阿里云下载即可。 选择下载以ISO结尾的文件,可选类型…

SLAM学习笔记(二)

5.相机与图像 相机将三维世界中的坐标点(单位米)映射到二维图像平面(单位为像素)的过程中能够用一个几何模型进行描述。 单目相机(Mono)的成像过程: 1、世界坐标系下有个固定的点P,世界坐标为 2、由于相…

关于quartus 13.1出现的问题的一些总结

1,如果IP核的版本与当前的quartus ii版本不一致的情况,有时候虽然能编译成功,但是无法修改原有工程的IP核参数设置。 如: 之前我下载的工程用到的NCO IP核是12.1 version的,但是我用到的quartus 版本是13.0和13.1&am…

led灯什么牌子的质量好?2022双十二家用护眼台灯推荐

台灯作为一种晚上或者其他黑暗条件下的照明灯具,对于经常熬夜的人群来说可以说是必备工具了,无论是看书、写字,还是工作、学习,都非常实用,它不同于平常的家用的室内照明顶灯,光线不会那么刺眼,…

uni-app入门:小程序UI组件Vant Weapp

1.vant介绍 2.安装步骤 2.1 通过 npm 安装 2.2 修改 app.json 2.3 修改 project.config.json 2.4 构建 npm 包 3.使用说明 1.vant介绍Vant Weapp 是一个轻量、可靠的移动端组件库,于 2017 年开源,是由有赞前端团队开…

Mysql 安装与卸载

MySQL8.0.26-Linux版安装 文章目录MySQL8.0.26-Linux版安装1. 安装1.1 准备一台Linux服务器1.2. 下载Linux版MySQL安装包1.3. 上传MySQL安装包1.4. 创建目录,并解压1.5. 安装mysql的安装包1.6. 启动MySQL服务1.7. 查询自动生成的root用户密码1.8. 修改root用户密码1.9. 创建用户…

【FRP】群晖docker中部署Frp

2022-08-24 by 崔斐然 0:需求 公司有台笔记本,现在疫情期间居家办公。我用的MacBook RDP客户端做的非常好用,如相互粘贴文件、文字等,MacBook通过远程桌面连接公司内网电脑会比较方便,时延和体验感远优于向日葵。之前…

java成神之路-基础篇 (搞定java基础看这一篇就够用)

java成神之路-基础篇 文章目录java成神之路-基础篇[toc]01面向对象**→ 什么是面向对象**→ 平台无关性→ 值传递1、什么是[值传递](https://so.csdn.net/so/search?q值传递&spm1001.2101.3001.7020),什么是引用传递?2.值传递和[引用传递](https://…

23软考备考已开始,网络工程师知识点速记~

新一轮软考备考来啦~ 为了帮助大家提高备考效率,将2023上半年软考网络工程师知识点速记分享给大家,快来跟着一起打卡学习吧! 进制的转换 数据的表示方法有二进制、八进制、十进制和十六进制等。网络工程师考试要求重点掌握这四种进制之间的…

mysql基础知识篇(六)

1.如何分库? 垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。 水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中的数据拆分到多个库…

线代 | 【提神醒脑】自用笔记串联三 —— 相似对角化 · 二次型 · 合同变换

本文总结参考于 kira 2023 线代提神醒脑技巧班。 笔记均为自用整理。加油!ヾ(◍∇◍)ノ゙ 九、相似对角化 9.1、矩阵相似的性质 ----------------------------------------------------------------------------------------------------------…

Linux 网络编程项目 —— FTP 网盘

文章目录项目简介知识点描述项目功能指令远程功能指令本地功能指令使用的关键函数access 函数popen 函数chdir 函数strtok 函数strncmp 函数linux system函数是否执行成功判断方法基本流程服务端客户端FTP代码实例头文件 ftp.h客户端 client.c服务端 server.cV2.0版 – 启用副服…

如何实现虹科物联网HMI/网关的调度器功能?

一、前言 JMobile软件提供了一个调度器引擎,通过设定的时间计划表自动执行特定动作,从而赋予HMI/网关调度器功能,减少现场操作人员的工作压力。本文主要介绍如何实现JMobile软件的调度器功能。 二、工具 1. Windows PC 2. JMobile Studio…

《论文阅读》DeepSFM: Structure From Motion Via Deep Bundle Adjustment

留个笔记自用 DeepSFM: Structure From Motion Via Deep Bundle Adjustment 做什么 首先是最基础的,Structure-from-Motion(SFM),SFM可以简单翻译成运动估计,是一种基于dui8序列图片进行三维重建的算法。简单来说就…

C++11(三)可变模板参数、lambda表达式、包装器

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸 文章目录一、可变模板参数1.通过递归推导参数包2.通过列表初始化推导参数包二、emplace三、lambda表达式1.lambda表达式语法2.使用lambda3.捕捉列表说…

phpstorm+wamp在线调试wordpress

简介 本文源自公司内部使用wordpress搭建了一套官网,经常有定制化的需求,有些插件实现不了,需要通过phpstorm调试的方式熟悉wordpress,同时修改php代码,本地测试环境window,适合用wamp作为php运行环境&…

解放你的双手----WIN7设置自动化任务

近期在使用双屏工具DualMonitor的时候遇到一个问题,每次电脑锁屏超过一定时常之后,登录解锁该软件虽然在运行但是功能失效了,需要手动关闭打开该程序,一时也没找到有效的解决方法和替代软件,于是就想着能不能在我登录解…