首页 > 数据库 >【源码日记】了解 PLpgSQL_datum

【源码日记】了解 PLpgSQL_datum

时间:2024-02-06 12:11:08浏览次数:27  
标签:name recfield expr datum 源码 PLpgSQL rec plpgsql

based on postgres commit b96115acb8a0e08a46877c2b8ef2a7b5560b371b

The SQL

CREATE OR REPLACE FUNCTION demo_fors()
RETURNS VOID AS
$$
DECLARE
  a RECORD;
BEGIN
  FOR a IN SELECT * FROM some_table LOOP
    RAISE NOTICE 'id: %, name: %', a.id, a.name;
  END LOOP;
END;
$$
LANGUAGE plpgsql;

dissect declare

a RECORD;
decl_statement	: decl_varname decl_const decl_datatype decl_collate decl_notnull decl_defval
  {
    PLpgSQL_variable    *var;
    /* $1.name is `a` */
    var = plpgsql_build_variable($1.name, $1.lineno, $3, true);
    var->isconst = $2;
    var->notnull = $5;
    var->default_val = $6;
  }

plpgsql_build_variable

PLpgSQL_variable *
plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype, bool add2namespace)
{
    PLpgSQL_variable *result;

    switch (dtype->ttype) {
      case PLPGSQL_TTYPE_REC:
        {
          /* Composite type -- build a record variable */
          PLpgSQL_rec *rec;
          /* refname is `a` */
          rec = plpgsql_build_record(refname, lineno, dtype, dtype->typoid, add2namespace);
          result = (PLpgSQL_variable *) rec;
          break;
        }
    }
    return result;
}

plpgsql_build_record

/*
 * Build empty named record variable, and optionally add it to namespace
 */
PLpgSQL_rec *
plpgsql_build_record(const char *refname, int lineno,
                     PLpgSQL_type *dtype, Oid rectypeid,
                     bool add2namespace)
{
  PLpgSQL_rec *rec;

  rec = palloc0(sizeof(PLpgSQL_rec));
  rec->dtype = PLPGSQL_DTYPE_REC;
  rec->refname = pstrdup(refname);
  rec->lineno = lineno;
  /* other fields are left as 0, might be changed by caller */
  rec->datatype = dtype;
  rec->rectypeid = rectypeid;
  rec->firstfield = -1;
  /**** !!! I will talk about `erh` field in another article !!! ****/
  rec->erh = NULL;
  plpgsql_adddatum((PLpgSQL_datum *) rec);
  if (add2namespace)
    plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->dno, rec->refname);

  return rec;
}

plpgsql_adddatum

/* ----------
 * plpgsql_adddatum			Add a variable, record or row
 *					to the compiler's datum list.
 * ----------
 */
void
plpgsql_adddatum(PLpgSQL_datum *newdatum)
{
  /* `plpgsql_nDatums` is a global variable that tracks the number of PLpgSQL_datum we know so far */
  newdatum->dno = plpgsql_nDatums;
  /* `plpgsql_Datums` is a global variable that chains all PLpgSQL_datum in an array */
  plpgsql_Datums[plpgsql_nDatums++] = newdatum;
}

plpgsql_ns_additem

/* ----------
 * plpgsql_ns_additem    Add an item to the current namespace chain
 * ----------
 */
void
plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name)
{
  PLpgSQL_nsitem *nse;

  Assert(name != NULL);
  /* first item added must be a label */
  Assert(ns_top != NULL || itemtype == PLPGSQL_NSTYPE_LABEL);

  nse = palloc(offsetof(PLpgSQL_nsitem, name) + strlen(name) + 1);
  nse->itemtype = itemtype;
  /* `itemno` is an index into the global `plpgsql_Datums` array */
  nse->itemno = itemno;
  /* `name` is `a` here */
  strcpy(nse->name, name);
  
  /* `ns_top` is another global variable which forms a namespace chain */
  nse->prev = ns_top;
  /* `ns_top` always points to newly created namespace item */
  ns_top = nse;
}

dissect raise

RAISE NOTICE 'id: %, name: %', a.id, a.name;

pl_gram.y

stmt_raise: K_RAISE
{
  PLpgSQL_stmt_raise *new;
  /* initialize PLpgSQL_stmt_raise, ignored here */
  /* read `NOTICE` token, ignored here */
  /* here we only focus parsing `a.id`, `a.name` */
  while (tok == ',')
  {
    PLpgSQL_expr *expr;

    expr = read_sql_construct(',', ';', K_USING,
                              ", or ; or USING",
                              RAW_PARSE_PLPGSQL_EXPR,
                              true, true, true,
                              NULL, &tok);
    new->params = lappend(new->params, expr);
  }
}

read_sql_construct

static PLpgSQL_expr *
read_sql_construct(...)
{
  /* switch to `IDENTIFIER_LOOKUP_EXPR` mode */
  for(;;)
  {
    /* read out `a.id`, `a.name` and put it into `StringInfoData ds` */
    tok = yylex() {
      plpgsql_yylex() {
        /* use `internal_yylex()` to find out `a.id` or `a.name` */
        plpgsql_parse_dblword(...);
      }
    };
  }

  /* construct PLpgSQL_expr */
  expr = palloc0(sizeof(PLpgSQL_expr));
  expr->query = pstrdup(ds.data);
  expr->parseMode = parsemode;
  expr->plan = NULL;
  expr->paramnos = NULL;
  expr->target_param = -1;
  expr->ns = plpgsql_ns_top();

  if (valid_sql)
    check_sql_expr(expr->query, expr->parseMode, startlocation);
}

plpgsql_parse_dblworld

/*
 * `word1` is `a`, `word2` is `id` or `name`
 * `wdatum` value will be moved to YYSTYPE.wdatum, in other words, it is fed into bison parser
 */
bool plpgsql_parse_dblword(char *word1, char *word2,
                      PLwdatum *wdatum, PLcword *cword)
{
  List  *idents = list_make2(makeString(word1), makeString(word2));
  /* we can find `a` in the global namespace variable `ns_top` */
  ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, word1, word2, NULL, &nnames);
  /* and `a` is of type PLPGSQL_NSTYPE_REC */

  /*
   * First word is a record name, so second word could
   * be a field in this record.  We build a RECFIELD
   * datum whether it is or not --- any error will be
   * detected later.
   */
   PLpgSQL_rec *rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
   PLpgSQL_recfield *new = plpgsql_build_recfield(rec, word2);

   wdatum->datum = (PLpgSQL_datum *) new;
}

plpgsql_build_recfield

/* fldname is `id` or `name` */
PLpgSQL_recfield *
plpgsql_build_recfield(PLpgSQL_rec *rec, const char *fldname)
{
  /* search for an existing datum referencing this field, code ignored here */

  /* nope, so make a new one */
  PLpgSQL_recfield *recfield = palloc0(sizeof(PLpgSQL_recfield));
  recfield->dtype = PLPGSQL_DTYPE_RECFIELD;
  recfield->fieldname = pstrdup(fldname);
  recfield->recparentno = rec->dno;
  recfield->rectupledescid = INVALID_TUPLEDESC_IDENTIFIER;

  plpgsql_adddatum((PLpgSQL_datum *) recfield);

  /* now we can link it into the parent's chain */
  recfield->nextfield = rec->firstfield;
  rec->firstfield = recfield->dno;

  return recfield;
}

标签:name,recfield,expr,datum,源码,PLpgSQL,rec,plpgsql
From: https://www.cnblogs.com/lddcool/p/18009337

相关文章

  • ReentrantLock源码分析、LockSuppor、ReentrantReadWriteLock、锁优化的方法
    ReentrantLock类图我们看一下重入锁ReentrantLock类关系图,它是实现了Lock接口的类。NonfairSync和FairSync都继承自抽象类Sync,在ReentrantLock中有非公平锁NonfairSync和公平锁FairSync的实现。在重入锁ReentrantLock类关系图中,我们可以看到NonfairSync和FairSync都继承自抽象......
  • ReentrantLock源码分析、LockSuppor、ReentrantReadWriteLock、锁优化的方法
    ReentrantLock类图我们看一下重入锁ReentrantLock类关系图,它是实现了Lock接口的类。NonfairSync和FairSync都继承自抽象类Sync,在ReentrantLock中有非公平锁NonfairSync和公平锁FairSync的实现。在重入锁ReentrantLock类关系图中,我们可以看到NonfairSync和FairSync都继承自抽象......
  • 通达信瞄准底部源码副图
    {股票指标}VAR1:=ma(HHV(HIGH,485),17);VAR2:=MA(HHV(HIGH,222),17);VAR3:=MA(HHV(HIGH,96),17);VAR4:=MA(LLV(LOW,485),17);VAR5:=MA(LLV(LOW,222),17);VAR6:=MA(LLV(LOW,96),17);VAR7:=MA((VAR4*0.96+VAR5*0.96+VAR6*0.96+VAR1*0.558+VAR2*0.558+VAR3*0.558)/6,17);V......
  • 通达信金钱魔鬼源码副图
    {股票指标}VAR2:IF(Ema(CLOSE,5)/EMA(EMA(CLOSE,9),16)<=0.85ANDCLOSE/REF(CLOSE,1)>0.905ANDCLOSE/REF(CLOSE,1)<1.05ANDvol/CAPITAL*100<5,50,0);VAR3:=(-100)*(HHV(HIGH,34)-CLOSE)/(HHV(HIGH,34)-LLV(LOW,34))+100;VAR4:=(-100)*(HHV(HIGH,50)-CLOSE)......
  • 通达信专吸庄血源码副图
    {股票指标}N:=10; VAR1:4*SMA((CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100,5,1)- 3*SMA(SMA((CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100,5,1),3.2,1),COLORYELLOW,LINETHICK1; VAR2:8,COLORGREEN,LINETHICK0;增强体力:IF(CROSS(VAR1,VAR2),80,0),STIC......
  • 通达信私募顶底线源码副图
    {股票指标}底部:0,COLORWHITE;顶部:100,COLORYELLOW;N1:=5;N4:=34;CQ:=100*(C-LLV(L,N4))/(HHV(C,N4)-LLV(L,N4));SAT:=(AMOUNT/C)/(HHV(AMOUNT,N4)/HHV(C,N4));饱和度:=IF(SAT>1,1,SAT)*100;W1:=C=HHV(C,20);W2:=BArslAst(W1);W3:=IF(W2>0,REF(C,W2),REF(C,W2));W4......
  • 通达信精品成交量指标公式源码
    {股票指标}A1:=REF(CLOSE,2);A2:=SMA(maX(CLOSE-A1,0),7,1)/SMA(Abs(CLOSE-A1),7,1)*100;A3:=DATE<=1070615;A4:=REF(LOW,1);A5:=SMA(ABS(LOW-A4),3,1)/SMA(MAX(LOW-A4,0),3,1)*100;A6:=EMA(IF(CLOSE*1.3,A5*10,A5/10),3);A7:=LLV(LOW,29);A8:=HHV(A6,37);A9:=IF......
  • 通达信牛回头主图源码
    {股票指标} 开盘:=(O-REF(C,1))/REF(C,1)*100,NODRAW,COLORLIGREEN;现价:REFDATE(C,DATE),DOTLINE,COLORLIGRAY;半分:(O+C)/2,NODRAW,COLORWHITE;H2:=MAX(H,REF(H,1));L2:=MIN(L,REF(L,1));两日半:=(H2+L2)/2,NODRAW;STICKLINE(ISLASTBARAND两日半,两日半,两日半,......
  • 通达信愚公财神副图源码
    {股票指标} {愚公财神} LC:=REF(CLOSE,1); RSI1:=SMA(MAX(CLOSE-LC,0),3,1)/SMA(ABS(CLOSE-LC),3,1)*100; RSI2:=SMA(MAX(CLOSE-LC,0),5,1)/SMA(ABS(CLOSE-LC),5,1)*100; 买:EMA(RSI1,2),COLORRED,LINETHICK1; 卖:EMA(RSI2,5),LINETHICK1,COLORFFFF00;Q2:=(CL......
  • 通达信龙头选股公式源码副图
    {股票指标}HJ_1:=DYNAINFO(4)>0ANDHHV(HIGH,10)/LLV(LOW,10)<1.25ANDREF(CLOSE,1)<llv(low,15)+(hhv(high,15)-llv(low,15))*0.85and=""close="">OPENANDCLOSE>=HHV(HIGH,10);HJ_2:=IF(DATETODAY(DATE)<999999999,1,DRAWNULL)......