Ejemplo n.º 1
0
        static void ResolveAlias(Parse parse, ExprList list, int colId, Expr expr, string type, int subqueries)
        {
            Debug.Assert(colId >= 0 && colId < list.Exprs);
            Expr orig = list.Ids[colId].Expr; // The iCol-th column of the result set
            Debug.Assert(orig != null);
            Debug.Assert((orig.Flags & EP.Resolved) != 0);
            Context ctx = parse.Ctx; // The database connection
            Expr dup = Expr.Dup(ctx, orig, 0); // Copy of pOrig
            if (orig.OP != TK.COLUMN && (type.Length == 0 || type[0] != 'G'))
            {
                IncrAggFunctionDepth(dup, subqueries);
                dup = Expr.PExpr_(parse, TK.AS, dup, null, null);
                if (dup == null) return;
                if (list.Ids[colId].Alias == 0)
                    list.Ids[colId].Alias = (ushort)(++parse.Alias.length);
                dup.TableId = list.Ids[colId].Alias;
            }
            if (expr.OP == TK.COLLATE)
                dup = Expr.AddCollateString(parse, dup, expr.u.Token);

            // Before calling sqlite3ExprDelete(), set the EP_Static flag. This prevents ExprDelete() from deleting the Expr structure itself,
            // allowing it to be repopulated by the memcpy() on the following line.
            E.ExprSetProperty(expr, EP.Static);
            Expr.Delete(ctx, ref expr);
            expr.memcpy(dup);
            if (!E.ExprHasProperty(expr, EP.IntValue) && expr.u.Token != null)
            {
                Debug.Assert((dup.Flags & (EP.Reduced | EP.TokenOnly)) == 0);
                dup.u.Token = expr.u.Token;
                dup.Flags2 |= EP2.MallocedToken;
            }
            C._tagfree(ctx, ref dup);
        }
Ejemplo n.º 2
0
    /*
    ** 2008 August 18
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    **
    ** This file contains routines used for walking the parser tree and
    ** resolve all identifiers by associating them with a particular
    ** table and column.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-01-05 15:30:36 28d0d7710761114a44a1a3a425a6883c661f06e7
    **
    **  $Header$
    *************************************************************************
    */
    //#include "sqliteInt.h"
    //#include <stdlib.h>
    //#include <string.h>

    /*
    ** Turn the pExpr expression into an alias for the iCol-th column of the
    ** result set in pEList.
    **
    ** If the result set column is a simple column reference, then this routine
    ** makes an exact copy.  But for any other kind of expression, this
    ** routine make a copy of the result set column as the argument to the
    ** TK_AS operator.  The TK_AS operator causes the expression to be
    ** evaluated just once and then reused for each alias.
    **
    ** The reason for suppressing the TK_AS term when the expression is a simple
    ** column reference is so that the column reference will be recognized as
    ** usable by indices within the WHERE clause processing logic.
    **
    ** Hack:  The TK_AS operator is inhibited if zType[0]=='G'.  This means
    ** that in a GROUP BY clause, the expression is evaluated twice.  Hence:
    **
    **     SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
    **
    ** Is equivalent to:
    **
    **     SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
    **
    ** The result of random()%5 in the GROUP BY clause is probably different
    ** from the result in the result-set.  We might fix this someday.  Or
    ** then again, we might not...
    */
    static void resolveAlias(
    Parse pParse,         /* Parsing context */
    ExprList pEList,      /* A result set */
    int iCol,             /* A column in the result set.  0..pEList.nExpr-1 */
    Expr pExpr,       /* Transform this into an alias to the result set */
    string zType          /* "GROUP" or "ORDER" or "" */
    )
    {
      Expr pOrig;           /* The iCol-th column of the result set */
      Expr pDup;            /* Copy of pOrig */
      sqlite3 db;           /* The database connection */

      Debug.Assert( iCol >= 0 && iCol < pEList.nExpr );
      pOrig = pEList.a[iCol].pExpr;
      Debug.Assert( pOrig != null );
      Debug.Assert( ( pOrig.flags & EP_Resolved ) != 0 );
      db = pParse.db;
      if ( pOrig.op != TK_COLUMN && ( zType.Length == 0 || zType[0] != 'G' ) )
      {
        pDup = sqlite3ExprDup( db, pOrig, 0 );
        pDup = sqlite3PExpr( pParse, TK_AS, pDup, null, null );
        if ( pDup == null ) return;
        if ( pEList.a[iCol].iAlias == 0 )
        {
          pEList.a[iCol].iAlias = (u16)( ++pParse.nAlias );
        }
        pDup.iTable = pEList.a[iCol].iAlias;
      }
      else if ( ExprHasProperty( pOrig, EP_IntValue ) || pOrig.u.zToken == null )
      {
        pDup = sqlite3ExprDup( db, pOrig, 0 );
        if ( pDup == null ) return;
      }
      else
      {
        string zToken = pOrig.u.zToken;
        Debug.Assert( zToken != null );
        pOrig.u.zToken = null;
        pDup = sqlite3ExprDup( db, pOrig, 0 );
        pOrig.u.zToken = zToken;
        if ( pDup == null ) return;
        Debug.Assert( ( pDup.flags & ( EP_Reduced | EP_TokenOnly ) ) == 0 );
        pDup.flags2 |= EP2_MallocedToken;
        pDup.u.zToken = zToken;// sqlite3DbStrDup( db, zToken );
      }
      if ( ( pExpr.flags & EP_ExpCollate ) != 0 )
      {
        pDup.pColl = pExpr.pColl;
        pDup.flags |= EP_ExpCollate;
      }

      /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This 
      ** prevents ExprDelete() from deleting the Expr structure itself,
      ** allowing it to be repopulated by the memcpy() on the following line.
      */
      ExprSetProperty( pExpr, EP_Static );
      sqlite3ExprDelete( db, ref pExpr );
      pExpr.CopyFrom( pDup ); //memcpy(pExpr, pDup, sizeof(*pExpr));
      sqlite3DbFree( db, ref pDup );
    }
Ejemplo n.º 3
0
 /*
 ** pEList is the SET clause of an UPDATE statement.  Each entry
 ** in pEList is of the format <id>=<expr>.  If any of the entries
 ** in pEList have an <id> which matches an identifier in pIdList,
 ** then return TRUE.  If pIdList==NULL, then it is considered a
 ** wildcard that matches anything.  Likewise if pEList==NULL then
 ** it matches anything so always return true.  Return false only
 ** if there is no match.
 */
 static int checkColumnOverlap(IdList pIdList, ExprList pEList)
 {
     int e;
     if (pIdList == null || NEVER(pEList == null))
         return 1;
     for (e = 0; e < pEList.nExpr; e++)
     {
         if (sqlite3IdListIndex(pIdList, pEList.a[e].zName) >= 0)
             return 1;
     }
     return 0;
 }
Ejemplo n.º 4
0
        /// <summary>
        /// </summary>
        /// <param name="@object"></param>
        /// <param name="parameter">ÐèÒª StringComparer.OrdinalIgnoreCase</param>
        /// <exception cref="System.MemberAccessException" />
        /// <exception cref="System.Reflection.TargetInvocationException" />
        public static void Mapping(object @object, IDictionary<string, object> parameter)
        {
            ThrowHelper.ThrowIfNull(@object, "object");
            ThrowHelper.ThrowIfNull(parameter, "parameter");

            var type = @object.GetType();
            var pds = GetDescriptors(type);
            PropertyInfo pd = null;
            Type cls = null;
            object obj = null;
            object value = null;
            for (var i = 0; i < pds.Length; i++)
            {
                pd = pds[i];

                obj = parameter.GetValue(pd.Name);
                value = obj;
                cls = pd.PropertyType;
                if (obj is string)
                {
                    var @string = (string)obj;
                    if ([email protected]())
                    {
                        @string = ConfigUtil.Filter(@string);
                    }
                    if (IsPrimitiveType(cls))
                    {
                        value = Convert(cls, @string);
                    }
                }
#if CONFG_BEAN
                else if (obj is BeanConfig)
                {
                    value = CreateBean((BeanConfig) obj);
                }
                else if (obj is IList<BeanConfig>)
                {
                    var list = new ExprList<object>();
                    foreach (var beanconfig in (IList<BeanConfig>) obj)
                    {
                        list.Add(CreateBean(beanconfig));
                    }
                    value = list.ToArray();
                }
#endif
                if (cls != null && value != null)
                {
                    pd.SetValue(@object, value, null);
                }
            }
        }
Ejemplo n.º 5
0
 /*
 ** Call sqlite3WalkExpr() for every expression in list p or until
 ** an abort request is seen.
 */
 static int sqlite3WalkExprList( Walker pWalker, ExprList p )
 {
   int i;
   ExprList_item pItem;
   if ( p != null )
   {
     for ( i = p.nExpr; i > 0; i-- )
     {//, pItem++){
       pItem = p.a[p.nExpr - i];
       if ( sqlite3WalkExpr( pWalker, ref pItem.pExpr ) != 0 ) return WRC_Abort;
     }
   }
   return WRC_Continue;
 }
Ejemplo n.º 6
0
 static Select sqlite3SelectNew(
 Parse pParse,        /* Parsing context */
 ExprList pEList,     /* which columns to include in the result */
 SrcList pSrc,        /* the FROM clause -- which tables to scan */
 Expr pWhere,         /* the WHERE clause */
 ExprList pGroupBy,   /* the GROUP BY clause */
 Expr pHaving,        /* the HAVING clause */
 ExprList pOrderBy,   /* the ORDER BY clause */
 int isDistinct,       /* true if the DISTINCT keyword is present */
 Expr pLimit,         /* LIMIT value.  NULL means not used */
 Expr pOffset         /* OFFSET value.  NULL means no offset */
 )
 {
   Select pNew;
   //           Select standin;
   sqlite3 db = pParse.db;
   pNew = new Select();//sqlite3DbMallocZero(db, sizeof(*pNew) );
   Debug.Assert( //db.mallocFailed != 0 ||
   null == pOffset || pLimit != null ); /* OFFSET implies LIMIT */
   //if( pNew==null   ){
   //  pNew = standin;
   //  memset(pNew, 0, sizeof(*pNew));
   //}
   if ( pEList == null )
   {
     pEList = sqlite3ExprListAppend( pParse, null, sqlite3Expr( db, TK_ALL, null ) );
   }
   pNew.pEList = pEList;
   pNew.pSrc = pSrc;
   pNew.pWhere = pWhere;
   pNew.pGroupBy = pGroupBy;
   pNew.pHaving = pHaving;
   pNew.pOrderBy = pOrderBy;
   pNew.selFlags = (u16)( isDistinct != 0 ? SF_Distinct : 0 );
   pNew.op = TK_SELECT;
   pNew.pLimit = pLimit;
   pNew.pOffset = pOffset;
   Debug.Assert( pOffset == null || pLimit != null );
   pNew.addrOpenEphm[0] = -1;
   pNew.addrOpenEphm[1] = -1;
   pNew.addrOpenEphm[2] = -1;
   //if ( db.mallocFailed != 0 )
   //{
   //  clearSelect( db, pNew );
   //  //if ( pNew != standin ) sqlite3DbFree( db, ref pNew );
   //  pNew = null;
   //}
   return pNew;
 }
Ejemplo n.º 7
0
 public static Select New(Parse parse, ExprList list, SrcList src, Expr where_, ExprList groupBy, Expr having, ExprList orderBy, SF selFlags, Expr limit, Expr offset)
 {
     Context ctx = parse.Ctx;
     Select newSelect = new Select();
     Debug.Assert(ctx.MallocFailed || offset == null || limit != null); // OFFSET implies LIMIT
     // Select standin;
     if (newSelect == null)
     {
         Debug.Assert(ctx.MallocFailed);
         //newSelect = standin;
         //_memset(newSelect, 0, sizeof(newSelect));
     }
     if (list == null)
         list = Expr.ListAppend(parse, null, Expr.Expr_(ctx, TK.ALL, null));
     newSelect.EList = list;
     if (src == null) src = new SrcList();
     newSelect.Src = src;
     newSelect.Where = where_;
     newSelect.GroupBy = groupBy;
     newSelect.Having = having;
     newSelect.OrderBy = orderBy;
     newSelect.SelFlags = selFlags;
     newSelect.OP = TK.SELECT;
     newSelect.Limit = limit;
     newSelect.Offset = offset;
     Debug.Assert(offset == null || limit != null);
     newSelect.AddrOpenEphms[0] = (OP) - 1;
     newSelect.AddrOpenEphms[1] = (OP) - 1;
     newSelect.AddrOpenEphms[2] = (OP) - 1;
     if (ctx.MallocFailed)
     {
         ClearSelect(ctx, newSelect);
         //if (newSelect != standin) C._tagfree(ctx, ref newSelect);
         newSelect = null;
     }
     else
         Debug.Assert(newSelect.Src != null || parse.Errs > 0);
     //Debug.Assert(newSelect != standin);
     return newSelect;
 }
Ejemplo n.º 8
0
 /*
 ** Insert code into "v" that will push the record on the top of the
 ** stack into the sorter.
 */
 static void pushOntoSorter(
 Parse pParse,         /* Parser context */
 ExprList pOrderBy,    /* The ORDER BY clause */
 Select pSelect,       /* The whole SELECT statement */
 int regData           /* Register holding data to be sorted */
 )
 {
   Vdbe v = pParse.pVdbe;
   int nExpr = pOrderBy.nExpr;
   int regBase = sqlite3GetTempRange( pParse, nExpr + 2 );
   int regRecord = sqlite3GetTempReg( pParse );
   sqlite3ExprCacheClear( pParse );
   sqlite3ExprCodeExprList( pParse, pOrderBy, regBase, false );
   sqlite3VdbeAddOp2( v, OP_Sequence, pOrderBy.iECursor, regBase + nExpr );
   sqlite3ExprCodeMove( pParse, regData, regBase + nExpr + 1, 1 );
   sqlite3VdbeAddOp3( v, OP_MakeRecord, regBase, nExpr + 2, regRecord );
   sqlite3VdbeAddOp2( v, OP_IdxInsert, pOrderBy.iECursor, regRecord );
   sqlite3ReleaseTempReg( pParse, regRecord );
   sqlite3ReleaseTempRange( pParse, regBase, nExpr + 2 );
   if ( pSelect.iLimit != 0 )
   {
     int addr1, addr2;
     int iLimit;
     if ( pSelect.iOffset != 0 )
     {
       iLimit = pSelect.iOffset + 1;
     }
     else
     {
       iLimit = pSelect.iLimit;
     }
     addr1 = sqlite3VdbeAddOp1( v, OP_IfZero, iLimit );
     sqlite3VdbeAddOp2( v, OP_AddImm, iLimit, -1 );
     addr2 = sqlite3VdbeAddOp0( v, OP_Goto );
     sqlite3VdbeJumpHere( v, addr1 );
     sqlite3VdbeAddOp1( v, OP_Last, pOrderBy.iECursor );
     sqlite3VdbeAddOp1( v, OP_Delete, pOrderBy.iECursor );
     sqlite3VdbeJumpHere( v, addr2 );
   }
 }
Ejemplo n.º 9
0
    /* Forward Declarations */
    //static void substExprList(sqlite3*, ExprList*, int, ExprList);
    //static void substSelect(sqlite3*, Select *, int, ExprList );

    /*
    ** Scan through the expression pExpr.  Replace every reference to
    ** a column in table number iTable with a copy of the iColumn-th
    ** entry in pEList.  (But leave references to the ROWID column
    ** unchanged.)
    **
    ** This routine is part of the flattening procedure.  A subquery
    ** whose result set is defined by pEList appears as entry in the
    ** FROM clause of a SELECT such that the VDBE cursor assigned to that
    ** FORM clause entry is iTable.  This routine make the necessary
    ** changes to pExpr so that it refers directly to the source table
    ** of the subquery rather the result set of the subquery.
    */
    static Expr substExpr(
    sqlite3 db,        /* Report malloc errors to this connection */
    Expr pExpr,        /* Expr in which substitution occurs */
    int iTable,        /* Table to be substituted */
    ExprList pEList    /* Substitute expressions */
    )
    {
      if ( pExpr == null )
        return null;
      if ( pExpr.op == TK_COLUMN && pExpr.iTable == iTable )
      {
        if ( pExpr.iColumn < 0 )
        {
          pExpr.op = TK_NULL;
        }
        else
        {
          Expr pNew;
          Debug.Assert( pEList != null && pExpr.iColumn < pEList.nExpr );
          Debug.Assert( pExpr.pLeft == null && pExpr.pRight == null );
          pNew = sqlite3ExprDup( db, pEList.a[pExpr.iColumn].pExpr, 0 );
          if ( pExpr.pColl != null )
          {
            pNew.pColl = pExpr.pColl;
          }
          sqlite3ExprDelete( db, ref pExpr );
          pExpr = pNew;
        }
      }
      else
      {
        pExpr.pLeft = substExpr( db, pExpr.pLeft, iTable, pEList );
        pExpr.pRight = substExpr( db, pExpr.pRight, iTable, pEList );
        if ( ExprHasProperty( pExpr, EP_xIsSelect ) )
        {
          substSelect( db, pExpr.x.pSelect, iTable, pEList );
        }
        else
        {
          substExprList( db, pExpr.x.pList, iTable, pEList );
        }
      }
      return pExpr;
    }
Ejemplo n.º 10
0
 static void substSelect(
 sqlite3 db,         /* Report malloc errors here */
 Select p,           /* SELECT statement in which to make substitutions */
 int iTable,          /* Table to be replaced */
 ExprList pEList     /* Substitute values */
 )
 {
   SrcList pSrc;
   SrcList_item pItem;
   int i;
   if ( p == null )
     return;
   substExprList( db, p.pEList, iTable, pEList );
   substExprList( db, p.pGroupBy, iTable, pEList );
   substExprList( db, p.pOrderBy, iTable, pEList );
   p.pHaving = substExpr( db, p.pHaving, iTable, pEList );
   p.pWhere = substExpr( db, p.pWhere, iTable, pEList );
   substSelect( db, p.pPrior, iTable, pEList );
   pSrc = p.pSrc;
   Debug.Assert( pSrc != null );  /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */
   if ( ALWAYS( pSrc ) )
   {
     for ( i = pSrc.nSrc; i > 0; i-- )//, pItem++ )
     {
       pItem = pSrc.a[pSrc.nSrc - i];
       substSelect( db, pItem.pSelect, iTable, pEList );
     }
   }
 }
Ejemplo n.º 11
0
        static RC SelectColumnsFromExprList(Parse parse, ExprList list, ref short colsLengthOut, ref Column[] colsOut)
        {
            Context ctx = parse.Ctx; // Database connection
            int j;

            int colsLength; // Number of columns in the result set
            Column[] cols; // For looping over result columns
            if (list != null)
            {
                colsLength = list.Exprs;
                cols = new Column[colsLength];
                C.ASSERTCOVERAGE(cols == null);
            }
            else
            {
                colsLength = 0;
                cols = null;
            }
            colsLengthOut = (short)colsLength;
            colsOut = cols;

            int i;
            Column col; // For looping over result columns
            for (i = 0; i < colsLength; i++)//, pCol++)
            {
                if (cols[i] == null) cols[i] = new Column();
                col = cols[i];
                // Get an appropriate name for the column
                Expr p = list.Ids[i].Expr; // Expression for a single result column
                string name; // Column name
                if (list.Ids[i].Name != null && (name = list.Ids[i].Name) != null) { }
                //: name = _tagstrdup(ctx, name); // If the column contains an "AS <name>" phrase, use <name> as the name 
                else
                {
                    Expr colExpr = p; // The expression that is the result column name

                    while (colExpr.OP == TK.DOT)
                    {
                        colExpr = colExpr.Right;
                        Debug.Assert(colExpr != null);
                    }
                    if (colExpr.OP == TK.COLUMN && C._ALWAYS(colExpr.Table != null))
                    {
                        // For columns use the column name name
                        int colId = colExpr.ColumnIdx;
                        Table table = colExpr.Table; // Table associated with this expression
                        if (colId < 0) colId = table.PKey;
                        name = C._mtagprintf(ctx, "%s", (colId >= 0 ? table.Cols[colId].Name : "rowid"));
                    }
                    else if (colExpr.OP == TK.ID)
                    {
                        Debug.Assert(!E.ExprHasProperty(colExpr, EP.IntValue));
                        name = C._mtagprintf(ctx, "%s", colExpr.u.Token);
                    }
                    else
                        name = C._mtagprintf(ctx, "%s", list.Ids[i].Span); // Use the original text of the column expression as its name
                }
                if (ctx.MallocFailed)
                {
                    C._tagfree(ctx, ref name);
                    break;
                }

                // Make sure the column name is unique.  If the name is not unique, append a integer to the name so that it becomes unique.
                int nameLength = name.Length; // Size of name in zName[]
                int cnt; // Index added to make the name unique
                for (j = cnt = 0; j < i; j++)
                {
                    if (string.Equals(cols[j].Name, name, StringComparison.OrdinalIgnoreCase))
                    {
                        name = name.Substring(0, nameLength);
                        string newName = C._mtagprintf(ctx, "%s:%d", name, ++cnt);
                        C._tagfree(ctx, ref name);
                        name = newName;
                        j = -1;
                        if (name == null) break;
                    }
                }
                col.Name = name;
            }
            if (ctx.MallocFailed)
            {
                for (j = 0; j < i; j++)
                    C._tagfree(ctx, ref cols[j].Name);
                C._tagfree(ctx, ref cols);
                colsOut = null;
                colsLengthOut = 0;
                return RC.NOMEM;
            }
            return RC.OK;
        }
Ejemplo n.º 12
0
    /*
    ** Generate code that will tell the VDBE the names of columns
    ** in the result set.  This information is used to provide the
    ** azCol[] values in the callback.
    */
    static void generateColumnNames(
    Parse pParse,      /* Parser context */
    SrcList pTabList,  /* List of tables */
    ExprList pEList    /* Expressions defining the result set */
    )
    {
      Vdbe v = pParse.pVdbe;
      int i, j;
      sqlite3 db = pParse.db;
      bool fullNames;
      bool shortNames;

#if !SQLITE_OMIT_EXPLAIN
      /* If this is an EXPLAIN, skip this step */
      if ( pParse.explain != 0 )
      {
        return;
      }
#endif

      if ( pParse.colNamesSet != 0 || NEVER( v == null ) /*|| db.mallocFailed != 0 */ )
        return;
      pParse.colNamesSet = 1;
      fullNames = ( db.flags & SQLITE_FullColNames ) != 0;
      shortNames = ( db.flags & SQLITE_ShortColNames ) != 0;
      sqlite3VdbeSetNumCols( v, pEList.nExpr );
      for ( i = 0; i < pEList.nExpr; i++ )
      {
        Expr p;
        p = pEList.a[i].pExpr;
        if ( NEVER( p == null ) )
          continue;
        if ( pEList.a[i].zName != null )
        {
          string zName = pEList.a[i].zName;
          sqlite3VdbeSetColName( v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT );
        }
        else if ( ( p.op == TK_COLUMN || p.op == TK_AGG_COLUMN ) && pTabList != null )
        {
          Table pTab;
          string zCol;
          int iCol = p.iColumn;
          for ( j = 0; ALWAYS( j < pTabList.nSrc ); j++ )
          {
            if ( pTabList.a[j].iCursor == p.iTable )
              break;
          }
          Debug.Assert( j < pTabList.nSrc );
          pTab = pTabList.a[j].pTab;
          if ( iCol < 0 )
            iCol = pTab.iPKey;
          Debug.Assert( iCol == -1 || ( iCol >= 0 && iCol < pTab.nCol ) );
          if ( iCol < 0 )
          {
            zCol = "rowid";
          }
          else
          {
            zCol = pTab.aCol[iCol].zName;
          }
          if ( !shortNames && !fullNames )
          {
            sqlite3VdbeSetColName( v, i, COLNAME_NAME,
            pEList.a[i].zSpan, SQLITE_DYNAMIC );//sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC);
          }
          else if ( fullNames )
          {
            string zName;
            zName = sqlite3MPrintf( db, "%s.%s", pTab.zName, zCol );
            sqlite3VdbeSetColName( v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC );
          }
          else
          {
            sqlite3VdbeSetColName( v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT );
          }
        }
        else
        {
          sqlite3VdbeSetColName( v, i, COLNAME_NAME,
          pEList.a[i].zSpan, SQLITE_DYNAMIC );//sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC);
        }
      }
      generateColumnTypes( pParse, pTabList, pEList );
    }
Ejemplo n.º 13
0
    static TriggerStep sqlite3TriggerInsertStep(
    sqlite3 db,        /* The database connection */
    Token pTableName,  /* Name of the table into which we insert */
    IdList pColumn,    /* List of columns in pTableName to insert into */
    ExprList pEList,   /* The VALUE clause: a list of values to be inserted */
    Select pSelect,    /* A SELECT statement that supplies values */
    u8 orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
    )
    {
      TriggerStep pTriggerStep;

      Debug.Assert( pEList == null || pSelect == null );
      Debug.Assert( pEList != null || pSelect != null /*|| db.mallocFailed != 0 */ );

      pTriggerStep = triggerStepAllocate( db, TK_INSERT, pTableName );
      //if ( pTriggerStep != null )
      //{
      pTriggerStep.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE );
      pTriggerStep.pIdList = pColumn;
      pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE );
      pTriggerStep.orconf = orconf;
      //}
      //else
      //{
      //  sqlite3IdListDelete( db, ref pColumn );
      //}
      sqlite3ExprListDelete( db, ref pEList );
      sqlite3SelectDelete( db, ref pSelect );

      return pTriggerStep;
    }
Ejemplo n.º 14
0
 //-----------------------------------------------------------
 public void Visit(ExprList node)
 {
     VisitChildren(node);
 }
Ejemplo n.º 15
0
        /* Make sure "isView" and other macros defined above are undefined. Otherwise
        ** thely may interfere with compilation of other functions in this file
        ** (or in another file, if this file becomes part of the amalgamation).  */
        //#if isView
        // #undef isView
        //#endif
        //#if pTrigger
        // #undef pTrigger
        //#endif

#if !SQLITE_OMIT_VIRTUALTABLE
        /*
        ** Generate code for an UPDATE of a virtual table.
        **
        ** The strategy is that we create an ephemerial table that contains
        ** for each row to be changed:
        **
        **   (A)  The original rowid of that row.
        **   (B)  The revised rowid for the row. (note1)
        **   (C)  The content of every column in the row.
        **
        ** Then we loop over this ephemeral table and for each row in
        ** the ephermeral table call VUpdate.
        **
        ** When finished, drop the ephemeral table.
        **
        ** (note1) Actually, if we know in advance that (A) is always the same
        ** as (B) we only store (A), then duplicate (A) when pulling
        ** it out of the ephemeral table before calling VUpdate.
        */

        private static void updateVirtualTable(
            Parse pParse,           /* The parsing context */
            SrcList pSrc,           /* The virtual table to be modified */
            Table pTab,             /* The virtual table */
            ExprList pChanges,      /* The columns to change in the UPDATE statement */
            Expr pRowid,            /* Expression used to recompute the rowid */
            int[] aXRef,            /* Mapping from columns of pTab to entries in pChanges */
            Expr pWhere,            /* WHERE clause of the UPDATE statement */
            int onError             /* ON CONFLICT strategy */
            )
        {
            Vdbe       v       = pParse.pVdbe;  /* Virtual machine under construction */
            ExprList   pEList  = null;          /* The result set of the SELECT statement */
            Select     pSelect = null;          /* The SELECT statement */
            Expr       pExpr;                   /* Temporary expression */
            int        ephemTab;                /* Table holding the result of the SELECT */
            int        i;                       /* Loop counter */
            int        addr;                    /* Address of top of loop */
            int        iReg;                    /* First register in set passed to OP_VUpdate */
            sqlite3    db    = pParse.db;       /* Database connection */
            VTable     pVTab = sqlite3GetVTable(db, pTab);
            SelectDest dest  = new SelectDest();

            /* Construct the SELECT statement that will find the new values for
            ** all updated rows.
            */
            pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
            if (pRowid != null)
            {
                pEList = sqlite3ExprListAppend(pParse, pEList,
                                               sqlite3ExprDup(db, pRowid, 0));
            }
            Debug.Assert(pTab.iPKey < 0);
            for (i = 0; i < pTab.nCol; i++)
            {
                if (aXRef[i] >= 0)
                {
                    pExpr = sqlite3ExprDup(db, pChanges.a[aXRef[i]].pExpr, 0);
                }
                else
                {
                    pExpr = sqlite3Expr(db, TK_ID, pTab.aCol[i].zName);
                }
                pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
            }
            pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, null, null, null, 0, null, null);

            /* Create the ephemeral table into which the update results will
            ** be stored.
            */
            Debug.Assert(v != null);
            ephemTab = pParse.nTab++;
            sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab.nCol + 1 + ((pRowid != null) ? 1 : 0));
            sqlite3VdbeChangeP5(v, BTREE_UNORDERED);

            /* fill the ephemeral table
             */
            sqlite3SelectDestInit(dest, SRT_Table, ephemTab);
            sqlite3Select(pParse, pSelect, ref dest);

            /* Generate code to scan the ephemeral table and call VUpdate. */
            iReg         = ++pParse.nMem;
            pParse.nMem += pTab.nCol + 1;
            addr         = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
            sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
            sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid != null ? 1 : 0), iReg + 1);
            for (i = 0; i < pTab.nCol; i++)
            {
                sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i + 1 + ((pRowid != null) ? 1 : 0), iReg + 2 + i);
            }
            sqlite3VtabMakeWritable(pParse, pTab);
            sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab.nCol + 2, iReg, pVTab, P4_VTAB);
            sqlite3VdbeChangeP5(v, (byte)(onError == OE_Default ? OE_Abort : onError));
            sqlite3MayAbort(pParse);
            sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr + 1);
            sqlite3VdbeJumpHere(v, addr);
            sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);

            /* Cleanup */
            sqlite3SelectDelete(db, ref pSelect);
        }
Ejemplo n.º 16
0
        /*
        ** This is called to code FOR EACH ROW triggers.
        **
        ** When the code that this function generates is executed, the following
        ** must be true:
        **
        ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
        **    can be indices of cursors in temporary tables.  See below.)
        **
        ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
        **    a temporary vdbe cursor (index newIdx) must be open and pointing at
        **    a row containing values to be substituted for new.* expressions in the
        **    trigger program(s).
        **
        ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
        **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
        **    a row containing values to be substituted for old.* expressions in the
        **    trigger program(s).
        **
        ** If they are not NULL, the piOldColMask and piNewColMask output variables
        ** are set to values that describe the columns used by the trigger program
        ** in the OLD.* and NEW.* tables respectively. If column N of the
        ** pseudo-table is read at least once, the corresponding bit of the output
        ** mask is set. If a column with an index greater than 32 is read, the
        ** output mask is set to the special value 0xffffffff.
        **
        */
        static int sqlite3CodeRowTrigger(
            Parse pParse,         /* Parse context */
            Trigger pTrigger,     /* List of triggers on table pTab */
            int op,               /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
            ExprList pChanges,    /* Changes list for any UPDATE OF triggers */
            int tr_tm,            /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
            Table pTab,           /* The table to code triggers from */
            int newIdx,           /* The indice of the "new" row to access */
            int oldIdx,           /* The indice of the "old" row to access */
            int orconf,           /* ON CONFLICT policy */
            int ignoreJump,       /* Instruction to jump to for RAISE(IGNORE) */
            ref u32 piOldColMask, /* OUT: Mask of columns used from the OLD.* table */
            ref u32 piNewColMask  /* OUT: Mask of columns used from the NEW.* table */
            )
        {
            Trigger      p;
            sqlite3      db             = pParse.db;
            TriggerStack trigStackEntry = new TriggerStack();

            trigStackEntry.oldColMask = 0;
            trigStackEntry.newColMask = 0;

            Debug.Assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
            Debug.Assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);

            Debug.Assert(newIdx != -1 || oldIdx != -1);

            for (p = pTrigger; p != null; p = p.pNext)
            {
                bool fire_this = false;

                /* Sanity checking:  The schema for the trigger and for the table are
                ** always defined.  The trigger must be in the same schema as the table
                ** or else it must be a TEMP trigger. */
                Debug.Assert(p.pSchema != null);
                Debug.Assert(p.pTabSchema != null);
                Debug.Assert(p.pSchema == p.pTabSchema || p.pSchema == db.aDb[1].pSchema);

                /* Determine whether we should code this trigger */
                if (
                    p.op == op &&
                    p.tr_tm == tr_tm &&
                    checkColumnOverlap(p.pColumns, pChanges) != 0)
                {
                    TriggerStack pS; /* Pointer to trigger-stack entry */
                    for (pS = pParse.trigStack; pS != null && p != pS.pTrigger; pS = pS.pNext)
                    {
                    }
                    if (pS == null)
                    {
                        fire_this = true;
                    }
#if FALSE   // * Give no warning for recursive triggers.  Just do not do them */
                    else
                    {
                        sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)",
                                        p.name);
                        return(SQLITE_ERROR);
                    }
#endif
                }

                if (fire_this)
                {
                    int         endTrigger;
                    Expr        whenExpr;
                    AuthContext sContext;
                    NameContext sNC;

#if !SQLITE_OMIT_TRACE
                    sqlite3VdbeAddOp4(pParse.pVdbe, OP_Trace, 0, 0, 0,
                                      sqlite3MPrintf(db, "-- TRIGGER %s", p.name),
                                      P4_DYNAMIC);
#endif
                    sNC        = new NameContext();// memset( &sNC, 0, sizeof(sNC) )
                    sNC.pParse = pParse;

                    /* Push an entry on to the trigger stack */
                    trigStackEntry.pTrigger   = p;
                    trigStackEntry.newIdx     = newIdx;
                    trigStackEntry.oldIdx     = oldIdx;
                    trigStackEntry.pTab       = pTab;
                    trigStackEntry.pNext      = pParse.trigStack;
                    trigStackEntry.ignoreJump = ignoreJump;
                    pParse.trigStack          = trigStackEntry;
#if !SQLITE_OMIT_AUTHORIZATION
                    sqlite3AuthContextPush(pParse, sContext, p.name);
#endif

                    /* code the WHEN clause */
                    endTrigger = sqlite3VdbeMakeLabel(pParse.pVdbe);
                    whenExpr   = sqlite3ExprDup(db, p.pWhen, 0);
                    if (/* db.mallocFailed != 0 || */ sqlite3ResolveExprNames(sNC, ref whenExpr) != 0)
                    {
                        pParse.trigStack = trigStackEntry.pNext;
                        sqlite3ExprDelete(db, ref whenExpr);
                        return(1);
                    }
                    sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL);
                    sqlite3ExprDelete(db, ref whenExpr);

                    codeTriggerProgram(pParse, p.step_list, orconf);

                    /* Pop the entry off the trigger stack */
                    pParse.trigStack = trigStackEntry.pNext;
#if !SQLITE_OMIT_AUTHORIZATION
                    sqlite3AuthContextPop(sContext);
#endif
                    sqlite3VdbeResolveLabel(pParse.pVdbe, endTrigger);
                }
            }
            piOldColMask |= trigStackEntry.oldColMask; // if ( piOldColMask != 0 ) piOldColMask |= trigStackEntry.oldColMask;
            piNewColMask |= trigStackEntry.newColMask; // if ( piNewColMask != 0 ) piNewColMask |= trigStackEntry.newColMask;
            return(0);
        }
Ejemplo n.º 17
0
        public Expr LimitWhere(Parse parse, SrcList src, Expr where_, ExprList orderBy, Expr limit, Expr offset, char stmtType)
        {
            // Check that there isn't an ORDER BY without a LIMIT clause.
            if (orderBy != null && (limit == null))
            {
                parse.ErrorMsg("ORDER BY without LIMIT on %s", stmtType);
                goto limit_where_cleanup_2;
            }

            // We only need to generate a select expression if there is a limit/offset term to enforce.
            if (limit == null)
            {
                Debug.Assert(offset == null); // if pLimit is null, pOffset will always be null as well.
                return where_;
            }

            // Generate a select expression tree to enforce the limit/offset term for the DELETE or UPDATE statement.  For example:
            //   DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
            // becomes:
            //   DELETE FROM table_a WHERE rowid IN ( 
            //     SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
            //   );
            Expr selectRowid = Expr.PExpr_(parse, TK.ROW, null, null, null); // SELECT rowid ...
            if (selectRowid == null) goto limit_where_cleanup_2;
            ExprList elist = ExprList.Append(parse, null, selectRowid); // Expression list contaning only pSelectRowid
            if (elist == null) goto limit_where_cleanup_2;

            // duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree and the SELECT subtree.
            SrcList selectSrc = SrcList.Dup(parse.Ctx, src, 0); // SELECT rowid FROM x ... (dup of pSrc)
            if (selectSrc == null)
            {
                ExprList.Delete(parse.Ctx, elist);
                goto limit_where_cleanup_2;
            }

            // generate the SELECT expression tree.
            Select select = Select.New(parse, elist, selectSrc, where_, null, null, orderBy, 0, limit, offset); // Complete SELECT tree
            if (select == null) return null;

            // now generate the new WHERE rowid IN clause for the DELETE/UDPATE
            Expr whereRowid = Expr.PExpr_(parse, TK.ROW, null, null, null); // WHERE rowid ..
            if (whereRowid == null) goto limit_where_cleanup_1;
            Expr inClause = Expr.PExpr_(parse, TK.IN, whereRowid, null, null); // WHERE rowid IN ( select )
            if (inClause == null) goto limit_where_cleanup_1;

            inClause.x.Select = select;
            inClause.Flags |= EP.xIsSelect;
            Expr.SetHeight(parse, inClause);
            return inClause;

        // something went wrong. clean up anything allocated.
        limit_where_cleanup_1:
            Select.Delete(parse.Ctx, select);
            return null;

        limit_where_cleanup_2:
            Expr.Delete(parse.Ctx, ref where_);
            ExprList.Delete(parse.Ctx, orderBy);
            Expr.Delete(parse.Ctx, ref limit);
            Expr.Delete(parse.Ctx, ref offset);
            return null;
        }
Ejemplo n.º 18
0
        /*
        ** Process an UPDATE statement.
        **
        **   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
        **          \_______/ \________/     \______/       \________________/
        *            onError   pTabList      pChanges             pWhere
        */

        private static void sqlite3Update(
            Parse pParse,             /* The parser context */
            SrcList pTabList,         /* The table in which we should change things */
            ExprList pChanges,        /* Things to be changed */
            Expr pWhere,              /* The WHERE clause.  May be null */
            int onError               /* How to handle constraint errors */
            )
        {
            int       i, j;                         /* Loop counters */
            Table     pTab;                         /* The table to be updated */
            int       addr = 0;                     /* VDBE instruction address of the start of the loop */
            WhereInfo pWInfo;                       /* Information about the WHERE clause */
            Vdbe      v;                            /* The virtual database engine */
            Index     pIdx;                         /* For looping over indices */
            int       nIdx;                         /* Number of indices that need updating */
            int       iCur;                         /* VDBE Cursor number of pTab */
            sqlite3   db;                           /* The database structure */

            int[] aRegIdx = null;                   /* One register assigned to each index to be updated */
            int[] aXRef   = null;                   /* aXRef[i] is the index in pChanges.a[] of the
                                                    ** an expression for the i-th column of the table.
                                                    ** aXRef[i]==-1 if the i-th column is not changed. */
            bool        chngRowid;                  /* True if the record number is being changed */
            Expr        pRowidExpr = null;          /* Expression defining the new record number */
            bool        openAll    = false;         /* True if all indices need to be opened */
            AuthContext sContext;                   /* The authorization context */
            NameContext sNC;                        /* The name-context to resolve expressions in */
            int         iDb;                        /* Database containing the table being updated */
            bool        okOnePass;                  /* True for one-pass algorithm without the FIFO */
            bool        hasFK;                      /* True if foreign key processing is required */

#if !SQLITE_OMIT_TRIGGER
            bool    isView;                    /* True when updating a view (INSTEAD OF trigger) */
            Trigger pTrigger;                  /* List of triggers on pTab, if required */
            int     tmask = 0;                 /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
#endif
            int newmask;                       /* Mask of NEW.* columns accessed by BEFORE triggers */

            /* Register Allocations */
            int regRowCount = 0;                     /* A count of rows changed */
            int regOldRowid;                         /* The old rowid */
            int regNewRowid;                         /* The new rowid */
            int regNew;
            int regOld    = 0;
            int regRowSet = 0;                       /* Rowset of rows to be updated */

            sContext = new AuthContext();            //memset( &sContext, 0, sizeof( sContext ) );
            db       = pParse.db;
            if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */)
            {
                goto update_cleanup;
            }
            Debug.Assert(pTabList.nSrc == 1);

            /* Locate the table which we want to update.
             */
            pTab = sqlite3SrcListLookup(pParse, pTabList);
            if (pTab == null)
            {
                goto update_cleanup;
            }
            iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);

            /* Figure out if we have any triggers and if the table being
            ** updated is a view.
            */
#if !SQLITE_OMIT_TRIGGER
            pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, out tmask);
            isView   = pTab.pSelect != null;
            Debug.Assert(pTrigger != null || tmask == 0);
#else
            const Trigger pTrigger = null; //# define pTrigger 0
            const int     tmask    = 0;    //# define tmask 0
#endif
#if SQLITE_OMIT_TRIGGER || SQLITE_OMIT_VIEW
//    # undef isView
            const bool isView = false; //# define isView 0
#endif

            if (sqlite3ViewGetColumnNames(pParse, pTab) != 0)
            {
                goto update_cleanup;
            }
            if (sqlite3IsReadOnly(pParse, pTab, tmask))
            {
                goto update_cleanup;
            }
            aXRef = new int[pTab.nCol];            // sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol);
            //if ( aXRef == null ) goto update_cleanup;
            for (i = 0; i < pTab.nCol; i++)
            {
                aXRef[i] = -1;
            }

            /* Allocate a cursors for the main database table and for all indices.
            ** The index cursors might not be used, but if they are used they
            ** need to occur right after the database cursor.  So go ahead and
            ** allocate enough space, just in case.
            */
            pTabList.a[0].iCursor = iCur = pParse.nTab++;
            for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
            {
                pParse.nTab++;
            }

            /* Initialize the name-context */
            sNC          = new NameContext();   // memset(&sNC, 0, sNC).Length;
            sNC.pParse   = pParse;
            sNC.pSrcList = pTabList;

            /* Resolve the column names in all the expressions of the
            ** of the UPDATE statement.  Also find the column index
            ** for each column to be updated in the pChanges array.  For each
            ** column to be updated, make sure we have authorization to change
            ** that column.
            */
            chngRowid = false;
            for (i = 0; i < pChanges.nExpr; i++)
            {
                if (sqlite3ResolveExprNames(sNC, ref pChanges.a[i].pExpr) != 0)
                {
                    goto update_cleanup;
                }
                for (j = 0; j < pTab.nCol; j++)
                {
                    if (pTab.aCol[j].zName.Equals(pChanges.a[i].zName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (j == pTab.iPKey)
                        {
                            chngRowid  = true;
                            pRowidExpr = pChanges.a[i].pExpr;
                        }
                        aXRef[j] = i;
                        break;
                    }
                }
                if (j >= pTab.nCol)
                {
                    if (sqlite3IsRowid(pChanges.a[i].zName))
                    {
                        chngRowid  = true;
                        pRowidExpr = pChanges.a[i].pExpr;
                    }
                    else
                    {
                        sqlite3ErrorMsg(pParse, "no such column: %s", pChanges.a[i].zName);
                        pParse.checkSchema = 1;
                        goto update_cleanup;
                    }
                }
#if !SQLITE_OMIT_AUTHORIZATION
                {
                    int rc;
                    rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName,
                                          pTab.aCol[j].zName, db.aDb[iDb].zName);
                    if (rc == SQLITE_DENY)
                    {
                        goto update_cleanup;
                    }
                    else if (rc == SQLITE_IGNORE)
                    {
                        aXRef[j] = -1;
                    }
                }
#endif
            }

            hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid ? 1 : 0) != 0;

            /* Allocate memory for the array aRegIdx[].  There is one entry in the
            ** array for each index associated with table being updated.  Fill in
            ** the value with a register number for indices that are to be used
            ** and with zero for unused indices.
            */
            for (nIdx = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, nIdx++)
            {
            }
            if (nIdx > 0)
            {
                aRegIdx = new int[nIdx];                 // sqlite3DbMallocRaw(db, Index*.Length * nIdx);
                if (aRegIdx == null)
                {
                    goto update_cleanup;
                }
            }
            for (j = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, j++)
            {
                int reg;
                if (hasFK || chngRowid)
                {
                    reg = ++pParse.nMem;
                }
                else
                {
                    reg = 0;
                    for (i = 0; i < pIdx.nColumn; i++)
                    {
                        if (aXRef[pIdx.aiColumn[i]] >= 0)
                        {
                            reg = ++pParse.nMem;
                            break;
                        }
                    }
                }
                aRegIdx[j] = reg;
            }

            /* Begin generating code. */
            v = sqlite3GetVdbe(pParse);
            if (v == null)
            {
                goto update_cleanup;
            }
            if (pParse.nested == 0)
            {
                sqlite3VdbeCountChanges(v);
            }
            sqlite3BeginWriteOperation(pParse, 1, iDb);

#if !SQLITE_OMIT_VIRTUALTABLE
            /* Virtual tables must be handled separately */
            if (IsVirtual(pTab))
            {
                updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
                                   pWhere, onError);
                pWhere   = null;
                pTabList = null;
                goto update_cleanup;
            }
#endif

            /* Allocate required registers. */
            regOldRowid = regNewRowid = ++pParse.nMem;
            if (pTrigger != null || hasFK)
            {
                regOld       = pParse.nMem + 1;
                pParse.nMem += pTab.nCol;
            }
            if (chngRowid || pTrigger != null || hasFK)
            {
                regNewRowid = ++pParse.nMem;
            }
            regNew       = pParse.nMem + 1;
            pParse.nMem += pTab.nCol;

            /* Start the view context. */
            if (isView)
            {
                sqlite3AuthContextPush(pParse, sContext, pTab.zName);
            }

            /* If we are trying to update a view, realize that view into
            ** a ephemeral table.
            */
#if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER)
            if (isView)
            {
                sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
            }
#endif

            /* Resolve the column names in all the expressions in the
            ** WHERE clause.
            */
            if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0)
            {
                goto update_cleanup;
            }

            /* Begin the database scan
             */
            sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
            ExprList NullOrderby = null;
            pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED);
            if (pWInfo == null)
            {
                goto update_cleanup;
            }
            okOnePass = pWInfo.okOnePass != 0;

            /* Remember the rowid of every item to be updated.
             */
            sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
            if (!okOnePass)
            {
                regRowSet = ++pParse.nMem;
                sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
            }

            /* End the database scan loop.
             */
            sqlite3WhereEnd(pWInfo);

            /* Initialize the count of updated rows
             */
            if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab)
            {
                regRowCount = ++pParse.nMem;
                sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
            }

            if (!isView)
            {
                /*
                ** Open every index that needs updating.  Note that if any
                ** index could potentially invoke a REPLACE conflict resolution
                ** action, then we need to open all indices because we might need
                ** to be deleting some records.
                */
                if (!okOnePass)
                {
                    sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
                }
                if (onError == OE_Replace)
                {
                    openAll = true;
                }
                else
                {
                    openAll = false;
                    for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
                    {
                        if (pIdx.onError == OE_Replace)
                        {
                            openAll = true;
                            break;
                        }
                    }
                }
                for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++)
                {
                    if (openAll || aRegIdx[i] > 0)
                    {
                        KeyInfo pKey = sqlite3IndexKeyinfo(pParse, pIdx);
                        sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb,
                                          pKey, P4_KEYINFO_HANDOFF);
                        Debug.Assert(pParse.nTab > iCur + i + 1);
                    }
                }
            }

            /* Top of the update loop */
            if (okOnePass)
            {
                int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
                addr = sqlite3VdbeAddOp0(v, OP_Goto);
                sqlite3VdbeJumpHere(v, a1);
            }
            else
            {
                addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
            }

            /* Make cursor iCur point to the record that is being updated. If
            ** this record does not exist for some reason (deleted by a trigger,
            ** for example, then jump to the next iteration of the RowSet loop.  */
            sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);

            /* If the record number will change, set register regNewRowid to
            ** contain the new value. If the record number is not being modified,
            ** then regNewRowid is the same register as regOldRowid, which is
            ** already populated.  */
            Debug.Assert(chngRowid || pTrigger != null || hasFK || regOldRowid == regNewRowid);
            if (chngRowid)
            {
                sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
                sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
            }

            /* If there are triggers on this table, populate an array of registers
            ** with the required old.* column data.  */
            if (hasFK || pTrigger != null)
            {
                u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
                oldmask |= sqlite3TriggerColmask(pParse,
                                                 pTrigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onError
                                                 );
                for (i = 0; i < pTab.nCol; i++)
                {
                    if (aXRef[i] < 0 || oldmask == 0xffffffff || (i < 32 && 0 != (oldmask & (1 << i))))
                    {
                        sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld + i);
                    }
                    else
                    {
                        sqlite3VdbeAddOp2(v, OP_Null, 0, regOld + i);
                    }
                }
                if (chngRowid == false)
                {
                    sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
                }
            }

            /* Populate the array of registers beginning at regNew with the new
            ** row data. This array is used to check constaints, create the new
            ** table and index records, and as the values for any new.* references
            ** made by triggers.
            **
            ** If there are one or more BEFORE triggers, then do not populate the
            ** registers associated with columns that are (a) not modified by
            ** this UPDATE statement and (b) not accessed by new.* references. The
            ** values for registers not modified by the UPDATE must be reloaded from
            ** the database after the BEFORE triggers are fired anyway (as the trigger
            ** may have modified them). So not loading those that are not going to
            ** be used eliminates some redundant opcodes.
            */
            newmask = (int)sqlite3TriggerColmask(
                pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
                );
            for (i = 0; i < pTab.nCol; i++)
            {
                if (i == pTab.iPKey)
                {
                    sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i);
                }
                else
                {
                    j = aXRef[i];
                    if (j >= 0)
                    {
                        sqlite3ExprCode(pParse, pChanges.a[j].pExpr, regNew + i);
                    }
                    else if (0 == (tmask & TRIGGER_BEFORE) || i > 31 || (newmask & (1 << i)) != 0)
                    {
                        /* This branch loads the value of a column that will not be changed
                        ** into a register. This is done if there are no BEFORE triggers, or
                        ** if there are one or more BEFORE triggers that use this value via
                        ** a new.* reference in a trigger program.
                        */
                        testcase(i == 31);
                        testcase(i == 32);
                        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i);
                        sqlite3ColumnDefault(v, pTab, i, regNew + i);
                    }
                }
            }

            /* Fire any BEFORE UPDATE triggers. This happens before constraints are
            ** verified. One could argue that this is wrong.
            */
            if ((tmask & TRIGGER_BEFORE) != 0)
            {
                sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab.nCol);
                sqlite3TableAffinityStr(v, pTab);
                sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
                                      TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);

                /* The row-trigger may have deleted the row being updated. In this
                ** case, jump to the next row. No updates or AFTER triggers are
                ** required. This behaviour - what happens when the row being updated
                ** is deleted or renamed by a BEFORE trigger - is left undefined in the
                ** documentation.
                */
                sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);

                /* If it did not delete it, the row-trigger may still have modified
                ** some of the columns of the row being updated. Load the values for
                ** all columns not modified by the update statement into their
                ** registers in case this has happened.
                */
                for (i = 0; i < pTab.nCol; i++)
                {
                    if (aXRef[i] < 0 && i != pTab.iPKey)
                    {
                        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i);
                        sqlite3ColumnDefault(v, pTab, i, regNew + i);
                    }
                }
            }

            if (!isView)
            {
                int j1;                                       /* Address of jump instruction */

                /* Do constraint checks. */
                int iDummy;
                sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
                                                aRegIdx, (chngRowid ? regOldRowid : 0), true, onError, addr, out iDummy);

                /* Do FK constraint checks. */
                if (hasFK)
                {
                    sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
                }

                /* Delete the index entries associated with the current record.  */
                j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
                sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);

                /* If changing the record number, delete the old record.  */
                if (hasFK || chngRowid)
                {
                    sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
                }
                sqlite3VdbeJumpHere(v, j1);

                if (hasFK)
                {
                    sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
                }

                /* Insert the new index entries and the new record. */
                sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, true, false, false);

                /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
                ** handle rows (possibly in other tables) that refer via a foreign key
                ** to the row just updated. */
                if (hasFK)
                {
                    sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
                }
            }

            /* Increment the row counter
             */
            if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab)
            {
                sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
            }

            sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
                                  TRIGGER_AFTER, pTab, regOldRowid, onError, addr);

            /* Repeat the above with the next record to be updated, until
            ** all record selected by the WHERE clause have been updated.
            */
            sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
            sqlite3VdbeJumpHere(v, addr);

            /* Close all tables */
            for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++)
            {
                if (openAll || aRegIdx[i] > 0)
                {
                    sqlite3VdbeAddOp2(v, OP_Close, iCur + i + 1, 0);
                }
            }
            sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);

            /* Update the sqlite_sequence table by storing the content of the
            ** maximum rowid counter values recorded while inserting into
            ** autoincrement tables.
            */
            if (pParse.nested == 0 && pParse.pTriggerTab == null)
            {
                sqlite3AutoincrementEnd(pParse);
            }

            /*
            ** Return the number of rows that were changed. If this routine is
            ** generating code because of a call to sqlite3NestedParse(), do not
            ** invoke the callback function.
            */
            if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab && 0 == pParse.nested)
            {
                sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
                sqlite3VdbeSetNumCols(v, 1);
                sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
            }

update_cleanup:
#if !SQLITE_OMIT_AUTHORIZATION
            sqlite3AuthContextPop(sContext);
#endif
            sqlite3DbFree(db, ref aRegIdx);
            sqlite3DbFree(db, ref aXRef);
            sqlite3SrcListDelete(db, ref pTabList);
            sqlite3ExprListDelete(db, ref pChanges);
            sqlite3ExprDelete(db, ref pWhere);
            return;
        }
Ejemplo n.º 19
0
        public static void DeleteFrom(Parse parse, SrcList tabList, Expr where_)
        {
            AuthContext sContext = new AuthContext(); // Authorization context
            Context ctx = parse.Ctx; // Main database structure
            if (parse.Errs != 0 || ctx.MallocFailed)
                goto delete_from_cleanup;
            Debug.Assert(tabList.Srcs == 1);

            // Locate the table which we want to delete.  This table has to be put in an SrcList structure because some of the subroutines we
            // will be calling are designed to work with multiple tables and expect an SrcList* parameter instead of just a Table* parameter.
            Table table = SrcList.Lookup(parse, tabList); // The table from which records will be deleted
            if (table == null) goto delete_from_cleanup;

            // Figure out if we have any triggers and if the table being deleted from is a view
#if !OMIT_TRIGGER
            int dummy;
            Trigger trigger = Triggers.Exist(parse, table, TK.DELETE, null, out dummy); // List of table triggers, if required
#if OMIT_VIEW
            const bool isView = false;
#else
            bool isView = (table.Select != null); // True if attempting to delete from a view
#endif
#else
            const Trigger trigger = null;
            bool isView = false;
#endif

            // If pTab is really a view, make sure it has been initialized.
            if (sqlite3ViewGetColumnNames(parse, table) != null || IsReadOnly(parse, table, (trigger != null)))
                goto delete_from_cleanup;
            int db = sqlite3SchemaToIndex(ctx, table.Schema); // Database number
            Debug.Assert(db < ctx.DBs.length);
            string dbName = ctx.DBs[db].Name; // Name of database holding pTab
            ARC rcauth = Auth.Check(parse, AUTH.DELETE, table.Name, 0, dbName); // Value returned by authorization callback
            Debug.Assert(rcauth == ARC.OK || rcauth == ARC.DENY || rcauth == ARC.IGNORE);
            if (rcauth == ARC.DENY)
                goto delete_from_cleanup;
            Debug.Assert(!isView || trigger != null);

            // Assign cursor number to the table and all its indices.
            Debug.Assert(tabList.Srcs == 1);
            int curId = tabList.Ids[0].Cursor = parse.Tabs++; // VDBE VdbeCursor number for pTab
            Index idx; // For looping over indices of the table
            for (idx = table.Index; idx != null; idx = idx.Next)
                parse.Tabs++;

            // Start the view context
            if (isView)
                Auth.ContextPush(parse, sContext, table.Name);

            // Begin generating code.
            Vdbe v = parse.GetVdbe(); // The virtual database engine 
            if (v == null)
                goto delete_from_cleanup;
            if (parse.Nested == 0) v.CountChanges();
            parse.BeginWriteOperation(1, db);

            // If we are trying to delete from a view, realize that view into a ephemeral table.
#if !OMIT_VIEW && !OMIT_TRIGGER
            if (isView)
                MaterializeView(parse, table, where_, curId);
#endif
            // Resolve the column names in the WHERE clause.
            NameContext sNC = new NameContext(); // Name context to resolve expressions in
            sNC.Parse = parse;
            sNC.SrcList = tabList;
            if (sqlite3ResolveExprNames(sNC, ref where_) != 0)
                goto delete_from_cleanup;

            // Initialize the counter of the number of rows deleted, if we are counting rows.
            int memCnt = -1; // Memory cell used for change counting
            if ((ctx.Flags & Context.FLAG.CountRows) != 0)
            {
                memCnt = ++parse.Mems;
                v.AddOp2(OP.Integer, 0, memCnt);
            }

#if !OMIT_TRUNCATE_OPTIMIZATION
            // Special case: A DELETE without a WHERE clause deletes everything. It is easier just to erase the whole table. Prior to version 3.6.5,
            // this optimization caused the row change count (the value returned by API function sqlite3_count_changes) to be set incorrectly.
            if (rcauth == ARC.OK && where_ == null && trigger == null && !IsVirtual(table) && !FKey.FkRequired(parse, table, null, 0))
            {
                Debug.Assert(!isView);
                v.AddOp4(OP.Clear, table.Id, db, memCnt, table.Name, Vdbe.P4T.STATIC);
                for (idx = table.Index; idx != null; idx = idx.Next)
                {
                    Debug.Assert(idx.Schema == table.Schema);
                    v.AddOp2(OP.Clear, idx.Id, db);
                }
            }
            else
#endif
            // The usual case: There is a WHERE clause so we have to scan through the table and pick which records to delete.
            {
                int rowSet = ++parse.Mems; // Register for rowset of rows to delete
                int rowid = ++parse.Mems; // Used for storing rowid values.

                // Collect rowids of every row to be deleted.
                v.AddOp2(OP.Null, 0, rowSet);
                ExprList dummy = null;
                WhereInfo winfo = Where.Begin(parse, tabList, where_, ref dummy, WHERE_DUPLICATES_OK, 0); // Information about the WHERE clause
                if (winfo == null) goto delete_from_cleanup;
                int regRowid = Expr.CodeGetColumn(parse, table, -1, curId, rowid); // Actual register containing rowids
                v.AddOp2(OP.RowSetAdd, rowSet, regRowid);
                if ((ctx.Flags & Context.FLAG.CountRows) != 0)
                    v.AddOp2(OP.AddImm, memCnt, 1);
                Where.End(winfo);

                // Delete every item whose key was written to the list during the database scan.  We have to delete items after the scan is complete
                // because deleting an item can change the scan order.
                int end = v.MakeLabel();

                // Unless this is a view, open cursors for the table we are deleting from and all its indices. If this is a view, then the
                // only effect this statement has is to fire the INSTEAD OF triggers.
                if (!isView)
                    sqlite3OpenTableAndIndices(parse, table, curId, OP.OpenWrite);
                int addr = v.AddOp3(OP.RowSetRead, rowSet, end, rowid);

                // Delete the row
#if !OMIT_VIRTUALTABLE
                if (IsVirtual(table))
                {
                    VTable vtable = VTable.GetVTable(ctx, table);
                    VTable.MakeWritable(parse, table);
                    v.AddOp4(OP.VUpdate, 0, 1, rowid, vtable, Vdbe.P4T.VTAB);
                    v.ChangeP5(OE.Abort);
                    sqlite3MayAbort(parse);
                }
                else
#endif
                {
                    int count = (parse.Nested == 0; // True to count changes
                    GenerateRowDelete(parse, table, curId, rowid, count, trigger, OE.Default);
                }

                // End of the delete loop
                v.AddOp2(OP.Goto, 0, addr);
                v.ResolveLabel(end);

                // Close the cursors open on the table and its indexes.
                if (!isView && !IsVirtual(table))
                {
                    for (int i = 1, idx = table.Index; idx != null; i++, idx = idx.Next)
                        v.AddOp2(OP.Close, curId + i, idx.Id);
                    v.AddOp1(OP.Close, curId);
                }
            }

            // Update the sqlite_sequence table by storing the content of the maximum rowid counter values recorded while inserting into
		    // autoincrement tables.
            if (parse.Nested == 0 && parse.TriggerTab == null)
                sqlite3AutoincrementEnd(parse);

            // Return the number of rows that were deleted. If this routine is generating code because of a call to sqlite3NestedParse(), do not
		    // invoke the callback function.
            if ((ctx.Flags & Context.FLAG.CountRows) != 0 && parse.Nested == 0 && parse.TriggerTab == null)
            {
                v.AddOp2(OP.ResultRow, memCnt, 1);
                v.SetNumCols(1);
                v.SetColName(0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
            }

        delete_from_cleanup:
            Auth.ContextPop(sContext);
            SrcList.Delete(ctx, ref tabList);
            Expr.Delete(ctx, ref where_);
            return;
        }
Ejemplo n.º 20
0
 public static ExprList WithExpressions(this ExprList original, IReadOnlyList <IExprExec> newExpressions)
 => new ExprList(expressions: newExpressions);
Ejemplo n.º 21
0
        static WRC ResolveSelectStep(Walker walker, Select p)
        {
            Debug.Assert(p != null);
            if ((p.SelFlags & SF.Resolved) != 0)
            {
                return(WRC.Prune);
            }
            NameContext outerNC = walker.u.NC;  // Context that contains this SELECT
            Parse       parse   = walker.Parse; // Parsing context
            Context     ctx     = parse.Ctx;    // Database connection

            // Normally sqlite3SelectExpand() will be called first and will have already expanded this SELECT.  However, if this is a subquery within
            // an expression, sqlite3ResolveExprNames() will be called without a prior call to sqlite3SelectExpand().  When that happens, let
            // sqlite3SelectPrep() do all of the processing for this SELECT. sqlite3SelectPrep() will invoke both sqlite3SelectExpand() and
            // this routine in the correct order.
            if ((p.SelFlags & SF.Expanded) == 0)
            {
                p.Prep(parse, outerNC);
                return(parse.Errs != 0 || ctx.MallocFailed ? WRC.Abort : WRC.Prune);
            }

            bool        isCompound = (p.Prior != null); // True if p is a compound select
            int         compounds  = 0;                 // Number of compound terms processed so far
            Select      leftmost   = p;                 // Left-most of SELECT of a compound
            int         i;
            NameContext nc;                             // Name context of this SELECT

            while (p != null)
            {
                Debug.Assert((p.SelFlags & SF.Expanded) != 0);
                Debug.Assert((p.SelFlags & SF.Resolved) == 0);
                p.SelFlags |= SF.Resolved;

                // Resolve the expressions in the LIMIT and OFFSET clauses. These are not allowed to refer to any names, so pass an empty NameContext.
                nc       = new NameContext(); //: _memset(&nc, 0, sizeof(nc));
                nc.Parse = parse;
                if (Walker.ResolveExprNames(nc, ref p.Limit) || Walker.ResolveExprNames(nc, ref p.Offset))
                {
                    return(WRC.Abort);
                }

                // Recursively resolve names in all subqueries
                SrcList.SrcListItem item;
                for (i = 0; i < p.Src.Srcs; i++)
                {
                    item = p.Src.Ids[i];
                    if (item.Select != null)
                    {
                        NameContext nc2;              // Used to iterate name contexts
                        int         refs         = 0; // Refcount for pOuterNC and outer contexts
                        string      savedContext = parse.AuthContext;

                        // Count the total number of references to pOuterNC and all of its parent contexts. After resolving references to expressions in
                        // pItem->pSelect, check if this value has changed. If so, then SELECT statement pItem->pSelect must be correlated. Set the
                        // pItem->isCorrelated flag if this is the case.
                        for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next)
                        {
                            refs += nc2.Refs;
                        }

                        if (item.Name != null)
                        {
                            parse.AuthContext = item.Name;
                        }
                        Walker.ResolveSelectNames(parse, item.Select, outerNC);
                        parse.AuthContext = savedContext;
                        if (parse.Errs != 0 || ctx.MallocFailed)
                        {
                            return(WRC.Abort);
                        }

                        for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next)
                        {
                            refs -= nc2.Refs;
                        }
                        Debug.Assert(!item.IsCorrelated && refs <= 0);
                        item.IsCorrelated = (refs != 0);
                    }
                }

                // Set up the local name-context to pass to sqlite3ResolveExprNames() to resolve the result-set expression list.
                nc.NCFlags = NC.AllowAgg;
                nc.SrcList = p.Src;
                nc.Next    = outerNC;

                // Resolve names in the result set.
                ExprList list = p.EList; // Result set expression list
                Debug.Assert(list != null);
                for (i = 0; i < list.Exprs; i++)
                {
                    Expr expr = list.Ids[i].Expr;
                    if (Walker.ResolveExprNames(nc, ref expr))
                    {
                        return(WRC.Abort);
                    }
                }

                // If there are no aggregate functions in the result-set, and no GROUP BY expression, do not allow aggregates in any of the other expressions.
                Debug.Assert((p.SelFlags & SF.Aggregate) == 0);
                ExprList groupBy = p.GroupBy; // The GROUP BY clause
                if (groupBy != null || (nc.NCFlags & NC.HasAgg) != 0)
                {
                    p.SelFlags |= SF.Aggregate;
                }
                else
                {
                    nc.NCFlags &= ~NC.AllowAgg;
                }

                // If a HAVING clause is present, then there must be a GROUP BY clause.
                if (p.Having != null && groupBy == null)
                {
                    parse.ErrorMsg("a GROUP BY clause is required before HAVING");
                    return(WRC.Abort);
                }

                // Add the expression list to the name-context before parsing the other expressions in the SELECT statement. This is so that
                // expressions in the WHERE clause (etc.) can refer to expressions by aliases in the result set.
                //
                // Minor point: If this is the case, then the expression will be re-evaluated for each reference to it.
                nc.EList = p.EList;
                if (Walker.ResolveExprNames(nc, ref p.Where) || Walker.ResolveExprNames(nc, ref p.Having))
                {
                    return(WRC.Abort);
                }

                // The ORDER BY and GROUP BY clauses may not refer to terms in outer queries
                nc.Next     = null;
                nc.NCFlags |= NC.AllowAgg;

                // Process the ORDER BY clause for singleton SELECT statements. The ORDER BY clause for compounds SELECT statements is handled
                // below, after all of the result-sets for all of the elements of the compound have been resolved.
                if (!isCompound && Walker.ResolveOrderGroupBy(nc, p, p.OrderBy, "ORDER"))
                {
                    return(WRC.Abort);
                }
                if (ctx.MallocFailed)
                {
                    return(WRC.Abort);
                }

                // Resolve the GROUP BY clause.  At the same time, make sure the GROUP BY clause does not contain aggregate functions.
                if (groupBy != null)
                {
                    if (Walker.ResolveOrderGroupBy(nc, p, groupBy, "GROUP") || ctx.MallocFailed)
                    {
                        return(WRC.Abort);
                    }
                    ExprList.ExprListItem item2;
                    for (i = 0; i < groupBy.Exprs; i++)
                    {
                        item2 = groupBy.Ids[i];
                        if (E.ExprHasProperty(item2.Expr, EP.Agg))
                        {
                            parse.ErrorMsg("aggregate functions are not allowed in the GROUP BY clause");
                            return(WRC.Abort);
                        }
                    }
                }

                // Advance to the next term of the compound
                p = p.Prior;
                compounds++;
            }

            // Resolve the ORDER BY on a compound SELECT after all terms of the compound have been resolved.
            return(isCompound && ResolveCompoundOrderBy(parse, leftmost) != 0 ? WRC.Abort : WRC.Prune);
        }
Ejemplo n.º 22
0
    /*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
**
** If srcTab and nColumn are both zero, then the pEList expressions
** are evaluated in order to get the data for this row.  If nColumn>0
** then data is pulled from srcTab and pEList is used only to get the
** datatypes for each column.
*/
    static void selectInnerLoop(
    Parse pParse,          /* The parser context */
    Select p,              /* The complete select statement being coded */
    ExprList pEList,       /* List of values being extracted */
    int srcTab,            /* Pull data from this table */
    int nColumn,           /* Number of columns in the source table */
    ExprList pOrderBy,     /* If not NULL, sort results using this key */
    int distinct,          /* If >=0, make sure results are distinct */
    SelectDest pDest,      /* How to dispose of the results */
    int iContinue,         /* Jump here to continue with next row */
    int iBreak             /* Jump here to break out of the inner loop */
    )
    {
      Vdbe v = pParse.pVdbe;
      int i;
      bool hasDistinct;         /* True if the DISTINCT keyword is present */
      int regResult;            /* Start of memory holding result set */
      int eDest = pDest.eDest;  /* How to dispose of results */
      int iParm = pDest.iParm;  /* First argument to disposal method */
      int nResultCol;           /* Number of result columns */

      Debug.Assert( v != null );
      if ( NEVER( v == null ) )
        return;
      Debug.Assert( pEList != null );
      hasDistinct = distinct >= 0;
      if ( pOrderBy == null && !hasDistinct )
      {
        codeOffset( v, p, iContinue );
      }

      /* Pull the requested columns.
      */
      if ( nColumn > 0 )
      {
        nResultCol = nColumn;
      }
      else
      {
        nResultCol = pEList.nExpr;
      }
      if ( pDest.iMem == 0 )
      {
        pDest.iMem = pParse.nMem + 1;
        pDest.nMem = nResultCol;
        pParse.nMem += nResultCol;
      }
      else
      {
        Debug.Assert( pDest.nMem == nResultCol );
      }
      regResult = pDest.iMem;
      if ( nColumn > 0 )
      {
        for ( i = 0; i < nColumn; i++ )
        {
          sqlite3VdbeAddOp3( v, OP_Column, srcTab, i, regResult + i );
        }
      }
      else if ( eDest != SRT_Exists )
      {
        /* If the destination is an EXISTS(...) expression, the actual
        ** values returned by the SELECT are not required.
        */
        sqlite3ExprCacheClear( pParse );
        sqlite3ExprCodeExprList( pParse, pEList, regResult, eDest == SRT_Output );
      }
      nColumn = nResultCol;

      /* If the DISTINCT keyword was present on the SELECT statement
      ** and this row has been seen before, then do not make this row
      ** part of the result.
      */
      if ( hasDistinct )
      {
        Debug.Assert( pEList != null );
        Debug.Assert( pEList.nExpr == nColumn );
        codeDistinct( pParse, distinct, iContinue, nColumn, regResult );
        if ( pOrderBy == null )
        {
          codeOffset( v, p, iContinue );
        }
      }

      switch ( eDest )
      {
        /* In this mode, write each query result to the key of the temporary
        ** table iParm.
        */
#if !SQLITE_OMIT_COMPOUND_SELECT
        case SRT_Union:
          {
            int r1;
            r1 = sqlite3GetTempReg( pParse );
            sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 );
            sqlite3VdbeAddOp2( v, OP_IdxInsert, iParm, r1 );
            sqlite3ReleaseTempReg( pParse, r1 );
            break;
          }

        /* Construct a record from the query result, but instead of
        ** saving that record, use it as a key to delete elements from
        ** the temporary table iParm.
        */
        case SRT_Except:
          {
            sqlite3VdbeAddOp3( v, OP_IdxDelete, iParm, regResult, nColumn );
            break;
          }
#endif

        /* Store the result as data using a unique key.
*/
        case SRT_Table:
        case SRT_EphemTab:
          {
            int r1 = sqlite3GetTempReg( pParse );
            testcase( eDest == SRT_Table );
            testcase( eDest == SRT_EphemTab );
            sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 );
            if ( pOrderBy != null )
            {
              pushOntoSorter( pParse, pOrderBy, p, r1 );
            }
            else
            {
              int r2 = sqlite3GetTempReg( pParse );
              sqlite3VdbeAddOp2( v, OP_NewRowid, iParm, r2 );
              sqlite3VdbeAddOp3( v, OP_Insert, iParm, r1, r2 );
              sqlite3VdbeChangeP5( v, OPFLAG_APPEND );
              sqlite3ReleaseTempReg( pParse, r2 );
            }
            sqlite3ReleaseTempReg( pParse, r1 );
            break;
          }

#if !SQLITE_OMIT_SUBQUERY
        /* If we are creating a set for an "expr IN (SELECT ...)" construct,
** then there should be a single item on the stack.  Write this
** item into the set table with bogus data.
*/
        case SRT_Set:
          {
            Debug.Assert( nColumn == 1 );
            p.affinity = sqlite3CompareAffinity( pEList.a[0].pExpr, pDest.affinity );
            if ( pOrderBy != null )
            {
              /* At first glance you would think we could optimize out the
              ** ORDER BY in this case since the order of entries in the set
              ** does not matter.  But there might be a LIMIT clause, in which
              ** case the order does matter */
              pushOntoSorter( pParse, pOrderBy, p, regResult );
            }
            else
            {
              int r1 = sqlite3GetTempReg( pParse );
              sqlite3VdbeAddOp4( v, OP_MakeRecord, regResult, 1, r1, p.affinity, 1 );
              sqlite3ExprCacheAffinityChange( pParse, regResult, 1 );
              sqlite3VdbeAddOp2( v, OP_IdxInsert, iParm, r1 );
              sqlite3ReleaseTempReg( pParse, r1 );
            }
            break;
          }

        /* If any row exist in the result set, record that fact and abort.
        */
        case SRT_Exists:
          {
            sqlite3VdbeAddOp2( v, OP_Integer, 1, iParm );
            /* The LIMIT clause will terminate the loop for us */
            break;
          }

        /* If this is a scalar select that is part of an expression, then
        ** store the results in the appropriate memory cell and break out
        ** of the scan loop.
        */
        case SRT_Mem:
          {
            Debug.Assert( nColumn == 1 );
            if ( pOrderBy != null )
            {
              pushOntoSorter( pParse, pOrderBy, p, regResult );
            }
            else
            {
              sqlite3ExprCodeMove( pParse, regResult, iParm, 1 );
              /* The LIMIT clause will jump out of the loop for us */
            }
            break;
          }
#endif // * #if !SQLITE_OMIT_SUBQUERY */

        /* Send the data to the callback function or to a subroutine.  In the
** case of a subroutine, the subroutine itself is responsible for
** popping the data from the stack.
*/
        case SRT_Coroutine:
        case SRT_Output:
          {
            testcase( eDest == SRT_Coroutine );
            testcase( eDest == SRT_Output );
            if ( pOrderBy != null )
            {
              int r1 = sqlite3GetTempReg( pParse );
              sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 );
              pushOntoSorter( pParse, pOrderBy, p, r1 );
              sqlite3ReleaseTempReg( pParse, r1 );
            }
            else if ( eDest == SRT_Coroutine )
            {
              sqlite3VdbeAddOp1( v, OP_Yield, pDest.iParm );
            }
            else
            {
              sqlite3VdbeAddOp2( v, OP_ResultRow, regResult, nColumn );
              sqlite3ExprCacheAffinityChange( pParse, regResult, nColumn );
            }
            break;
          }

#if !SQLITE_OMIT_TRIGGER
        /* Discard the results.  This is used for SELECT statements inside
** the body of a TRIGGER.  The purpose of such selects is to call
** user-defined functions that have side effects.  We do not care
** about the actual results of the select.
*/
        default:
          {
            Debug.Assert( eDest == SRT_Discard );
            break;
          }
#endif
      }

      /* Jump to the end of the loop if the LIMIT is reached.  Except, if
      ** there is a sorter, in which case the sorter has already limited
      ** the output for us.
      */
      if ( pOrderBy == null && p.iLimit != 0 )
      {
        sqlite3VdbeAddOp3( v, OP_IfZero, p.iLimit, iBreak, -1 );
      }
    }
Ejemplo n.º 23
0
 //-----------------------------------------------------------
 public string Visit(ExprList node)
 {
     return(VisitChildren(node));
 }
Ejemplo n.º 24
0
        static int ResolveCompoundOrderBy(Parse parse, Select select)
        {
            ExprList orderBy = select.OrderBy;

            if (orderBy == null)
            {
                return(0);
            }
            Context ctx = parse.Ctx;

#if true || MAX_COLUMN
            if (orderBy.Exprs > ctx.Limits[(int)LIMIT.COLUMN])
            {
                parse.ErrorMsg("too many terms in ORDER BY clause");
                return(1);
            }
#endif
            int i;
            for (i = 0; i < orderBy.Exprs; i++)
            {
                orderBy.Ids[i].Done = false;
            }
            select.Next = null;
            while (select.Prior != null)
            {
                select.Prior.Next = select;
                select            = select.Prior;
            }
            bool moreToDo = true;
            while (select != null && moreToDo)
            {
                moreToDo = false;
                ExprList list = select.EList;
                Debug.Assert(list != null);
                ExprList.ExprListItem item;
                for (i = 0; i < orderBy.Exprs; i++)
                {
                    item = orderBy.Ids[i];

                    Expr pDup;
                    if (item.Done)
                    {
                        continue;
                    }
                    Expr expr  = item.Expr;
                    int  colId = -1;
                    if (expr.IsInteger(ref colId))
                    {
                        if (colId <= 0 || colId > list.Exprs)
                        {
                            ResolveOutOfRangeError(parse, "ORDER", i + 1, list.Exprs);
                            return(1);
                        }
                    }
                    else
                    {
                        colId = ResolveAsName(parse, list, expr);
                        if (colId == 0)
                        {
                            Expr dupExpr = Expr.Dup(ctx, expr, 0);
                            if (!ctx.MallocFailed)
                            {
                                Debug.Assert(dupExpr != null);
                                colId = ResolveOrderByTermToExprList(parse, select, dupExpr);
                            }
                            Expr.Delete(ctx, ref dupExpr);
                        }
                    }
                    if (colId > 0)
                    {
                        // Convert the ORDER BY term into an integer column number iCol, taking care to preserve the COLLATE clause if it exists
                        Expr newExpr = Expr.Expr_(ctx, TK.INTEGER, null);
                        if (newExpr == null)
                        {
                            return(1);
                        }
                        newExpr.Flags |= EP.IntValue;
                        newExpr.u.I    = colId;
                        if (item.Expr == expr)
                        {
                            item.Expr = newExpr;
                        }
                        else
                        {
                            Debug.Assert(item.Expr.OP == TK.COLLATE);
                            Debug.Assert(item.Expr.Left == expr);
                            item.Expr.Left = newExpr;
                        }
                        Expr.Delete(ctx, ref expr);
                        item.OrderByCol = (ushort)colId;
                        item.Done       = true;
                    }
                    else
                    {
                        moreToDo = true;
                    }
                }
                select = select.Next;
            }
            for (i = 0; i < orderBy.Exprs; i++)
            {
                if (!orderBy.Ids[i].Done)
                {
                    parse.ErrorMsg("%r ORDER BY term does not match any column in the result set", i + 1);
                    return(1);
                }
            }
            return(0);
        }
Ejemplo n.º 25
0
/*
** Generate an expression tree to implement the WHERE, ORDER BY,
** and LIMIT/OFFSET portion of DELETE and UPDATE statements.
**
**     DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1;
**                            \__________________________/
**                               pLimitWhere (pInClause)
*/
        Expr sqlite3LimitWhere(
            Parse pParse,      /* The parser context */
            SrcList pSrc,      /* the FROM clause -- which tables to scan */
            Expr pWhere,       /* The WHERE clause.  May be null */
            ExprList pOrderBy, /* The ORDER BY clause.  May be null */
            Expr pLimit,       /* The LIMIT clause.  May be null */
            Expr pOffset,      /* The OFFSET clause.  May be null */
            char zStmtType     /* Either DELETE or UPDATE.  For error messages. */
            )
        {
            Expr     pWhereRowid  = null; /* WHERE rowid .. */
            Expr     pInClause    = null; /* WHERE rowid IN ( select ) */
            Expr     pSelectRowid = null; /* SELECT rowid ... */
            ExprList pEList       = null; /* Expression list contaning only pSelectRowid */
            SrcList  pSelectSrc   = null; /* SELECT rowid FROM x ... (dup of pSrc) */
            Select   pSelect      = null; /* Complete SELECT tree */

/* Check that there isn't an ORDER BY without a LIMIT clause.
 */

            if (pOrderBy != null && (pLimit == null))
            {
                sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
                pParse.parseError = 1;
                goto limit_where_cleanup_2;
            }

/* We only need to generate a select expression if there
** is a limit/offset term to enforce.
*/
            if (pLimit == null)
            {
/* if pLimit is null, pOffset will always be null as well. */
                Debug.Assert(pOffset == null);
                return(pWhere);
            }

/* Generate a select expression tree to enforce the limit/offset
** term for the DELETE or UPDATE statement.  For example:
**   DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
** becomes:
**   DELETE FROM table_a WHERE rowid IN (
**     SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
**   );
*/

            pSelectRowid = sqlite3PExpr(pParse, TK_ROW, null, null, null);
            if (pSelectRowid == null)
            {
                goto limit_where_cleanup_2;
            }
            pEList = sqlite3ExprListAppend(pParse, null, pSelectRowid);
            if (pEList == null)
            {
                goto limit_where_cleanup_2;
            }

/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
            pSelectSrc = sqlite3SrcListDup(pParse.db, pSrc, 0);
            if (pSelectSrc == null)
            {
                sqlite3ExprListDelete(pParse.db, pEList);
                goto limit_where_cleanup_2;
            }

/* generate the SELECT expression tree. */
            pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, null, null,
                                       pOrderBy, 0, pLimit, pOffset);
            if (pSelect == null)
            {
                return(null);
            }

/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
            pWhereRowid = sqlite3PExpr(pParse, TK_ROW, null, null, null);
            if (pWhereRowid == null)
            {
                goto limit_where_cleanup_1;
            }
            pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, null, null);
            if (pInClause == null)
            {
                goto limit_where_cleanup_1;
            }

            pInClause->x.pSelect = pSelect;
            pInClause->flags    |= EP_xIsSelect;
            sqlite3ExprSetHeight(pParse, pInClause);
            return(pInClause);

/* something went wrong. clean up anything allocated. */
limit_where_cleanup_1:
            sqlite3SelectDelete(pParse.db, pSelect);
            return(null);

limit_where_cleanup_2:
            sqlite3ExprDelete(pParse.db, ref pWhere);
            sqlite3ExprListDelete(pParse.db, pOrderBy);
            sqlite3ExprDelete(pParse.db, ref pLimit);
            sqlite3ExprDelete(pParse.db, ref pOffset);
            return(null);
        }
Ejemplo n.º 26
0
    /*
    ** Return a list of all triggers on table pTab if there exists at least
    ** one trigger that must be fired when an operation of type 'op' is
    ** performed on the table, and, if that operation is an UPDATE, if at
    ** least one of the columns in pChanges is being modified.
    */
    static Trigger sqlite3TriggersExist(
    Parse pParse,          /* Parse context */
    Table pTab,            /* The table the contains the triggers */
    int op,                /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
    ExprList pChanges,     /* Columns that change in an UPDATE statement */
    out int pMask          /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
    )
    {
      int mask = 0;
      Trigger pList = null;
      Trigger p;

      if ( ( pParse.db.flags & SQLITE_EnableTrigger ) != 0 )
      {
        pList = sqlite3TriggerList( pParse, pTab );
      }
      Debug.Assert( pList == null || IsVirtual( pTab ) == false );
      for ( p = pList; p != null; p = p.pNext )
      {
        if ( p.op == op && checkColumnOverlap( p.pColumns, pChanges ) != 0 )
        {
          mask |= p.tr_tm;
        }
      }
      //if ( pMask != 0 )
      {
        pMask = mask;
      }
      return ( mask != 0 ? pList : null );
    }
Ejemplo n.º 27
0
        /*
        ** Generate code for a DELETE FROM statement.
        **
        **     DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL;
        **                 \________/       \________________/
        **                  pTabList              pWhere
        */
        static void sqlite3DeleteFrom(
            Parse pParse,     /* The parser context */
            SrcList pTabList, /* The table from which we should delete things */
            Expr pWhere       /* The WHERE clause.  May be null */
            )
        {
            Vdbe        v;             /* The virtual database engine */
            Table       pTab;          /* The table from which records will be deleted */
            string      zDb;           /* Name of database holding pTab */
            int         end, addr = 0; /* A couple addresses of generated code */
            int         i;             /* Loop counter */
            WhereInfo   pWInfo;        /* Information about the WHERE clause */
            Index       pIdx;          /* For looping over indices of the table */
            int         iCur;          /* VDBE VdbeCursor number for pTab */
            sqlite3     db;            /* Main database structure */
            AuthContext sContext;      /* Authorization context */
            NameContext sNC;           /* Name context to resolve expressions in */
            int         iDb;           /* Database number */
            int         memCnt = -1;   /* Memory cell used for change counting */
            int         rcauth;        /* Value returned by authorization callback */

#if !SQLITE_OMIT_TRIGGER
            bool    isView;               /* True if attempting to delete from a view */
            Trigger pTrigger;             /* List of table triggers, if required */
#endif
            sContext = new AuthContext(); //memset(&sContext, 0, sizeof(sContext));

            db = pParse.db;
            if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */)
            {
                goto delete_from_cleanup;
            }
            Debug.Assert(pTabList.nSrc == 1);

            /* Locate the table which we want to delete.  This table has to be
            ** put in an SrcList structure because some of the subroutines we
            ** will be calling are designed to work with multiple tables and expect
            ** an SrcList* parameter instead of just a Table* parameter.
            */
            pTab = sqlite3SrcListLookup(pParse, pTabList);
            if (pTab == null)
            {
                goto delete_from_cleanup;
            }

            /* Figure out if we have any triggers and if the table being
            ** deleted from is a view
            */
#if !SQLITE_OMIT_TRIGGER
            int iDummy = 0;
            pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, null, ref iDummy);
            isView   = pTab.pSelect != null;
#else
            const Trigger pTrigger = null;
            bool          isView   = false;
#endif
#if SQLITE_OMIT_VIEW
//# undef isView
            isView = false;
#endif

            /* If pTab is really a view, make sure it has been initialized.
             */
            if (sqlite3ViewGetColumnNames(pParse, pTab) != 0)
            {
                goto delete_from_cleanup;
            }

            if (sqlite3IsReadOnly(pParse, pTab, (pTrigger != null ? 1 : 0)))
            {
                goto delete_from_cleanup;
            }
            iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
            Debug.Assert(iDb < db.nDb);
            zDb = db.aDb[iDb].zName;
#if !SQLITE_OMIT_AUTHORIZATION
            rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
#else
            rcauth = SQLITE_OK;
#endif
            Debug.Assert(rcauth == SQLITE_OK || rcauth == SQLITE_DENY || rcauth == SQLITE_IGNORE);
            if (rcauth == SQLITE_DENY)
            {
                goto delete_from_cleanup;
            }
            Debug.Assert(!isView || pTrigger != null);

            /* Assign  cursor number to the table and all its indices.
             */
            Debug.Assert(pTabList.nSrc == 1);
            iCur = pTabList.a[0].iCursor = pParse.nTab++;
            for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
            {
                pParse.nTab++;
            }

#if !SQLITE_OMIT_AUTHORIZATION
/* Start the view context
 */
            if (isView)
            {
                sqlite3AuthContextPush(pParse, sContext, pTab.zName);
            }
#endif

            /* Begin generating code.
             */
            v = sqlite3GetVdbe(pParse);
            if (v == null)
            {
                goto delete_from_cleanup;
            }
            if (pParse.nested == 0)
            {
                sqlite3VdbeCountChanges(v);
            }
            sqlite3BeginWriteOperation(pParse, 1, iDb);

            /* If we are trying to delete from a view, realize that view into
            ** a ephemeral table.
            */
#if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER)
            if (isView)
            {
                sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
            }
#endif

            /* Resolve the column names in the WHERE clause.
             */
            sNC          = new NameContext();// memset( &sNC, 0, sizeof( sNC ) );
            sNC.pParse   = pParse;
            sNC.pSrcList = pTabList;
            if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0)
            {
                goto delete_from_cleanup;
            }


            /* Initialize the counter of the number of rows deleted, if
            ** we are counting rows.
            */
            if ((db.flags & SQLITE_CountRows) != 0)
            {
                memCnt = ++pParse.nMem;
                sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
            }

#if !SQLITE_OMIT_TRUNCATE_OPTIMIZATION
            /* Special case: A DELETE without a WHERE clause deletes everything.
            ** It is easier just to erase the whole table. Prior to version 3.6.5,
            ** this optimization caused the row change count (the value returned by
            ** API function sqlite3_count_changes) to be set incorrectly.  */
            if (rcauth == SQLITE_OK && pWhere == null && null == pTrigger && !IsVirtual(pTab) &&
                0 == sqlite3FkRequired(pParse, pTab, null, 0)
                )
            {
                Debug.Assert(!isView);
                sqlite3VdbeAddOp4(v, OP_Clear, pTab.tnum, iDb, memCnt,
                                  pTab.zName, P4_STATIC);
                for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
                {
                    Debug.Assert(pIdx.pSchema == pTab.pSchema);
                    sqlite3VdbeAddOp2(v, OP_Clear, pIdx.tnum, iDb);
                }
            }
            else
#endif //* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */

            /* The usual case: There is a WHERE clause so we have to scan through
            ** the table and pick which records to delete.
            */
            {
                int iRowSet = ++pParse.nMem; /* Register for rowset of rows to delete */
                int iRowid  = ++pParse.nMem; /* Used for storing rowid values. */
                int regRowid;                /* Actual register containing rowids */

                /* Collect rowids of every row to be deleted.
                 */
                sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
                ExprList elDummy = null;
                pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref elDummy, WHERE_DUPLICATES_OK);
                if (pWInfo == null)
                {
                    goto delete_from_cleanup;
                }
                regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
                sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
                if ((db.flags & SQLITE_CountRows) != 0)
                {
                    sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
                }

                sqlite3WhereEnd(pWInfo);

                /* Delete every item whose key was written to the list during the
                ** database scan.  We have to delete items after the scan is complete
                ** because deleting an item can change the scan order. */
                end = sqlite3VdbeMakeLabel(v);

                /* Unless this is a view, open cursors for the table we are
                ** deleting from and all its indices. If this is a view, then the
                ** only effect this statement has is to fire the INSTEAD OF
                ** triggers.  */
                if (!isView)
                {
                    sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
                }

                addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);

                /* Delete the row */
#if !SQLITE_OMIT_VIRTUALTABLE
                if (IsVirtual(pTab))
                {
                    const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
                    sqlite3VtabMakeWritable(pParse, pTab);
                    sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
                    sqlite3MayAbort(pParse);
                }
                else
#endif
                {
                    int count = (pParse.nested == 0) ? 1 : 0; /* True to count changes */
                    sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
                }

                /* End of the delete loop */
                sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
                sqlite3VdbeResolveLabel(v, end);

                /* Close the cursors open on the table and its indexes. */
                if (!isView && !IsVirtual(pTab))
                {
                    for (i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext)
                    {
                        sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx.tnum);
                    }
                    sqlite3VdbeAddOp1(v, OP_Close, iCur);
                }
            }

            /* Update the sqlite_sequence table by storing the content of the
            ** maximum rowid counter values recorded while inserting into
            ** autoincrement tables.
            */
            if (pParse.nested == 0 && pParse.pTriggerTab == null)
            {
                sqlite3AutoincrementEnd(pParse);
            }

            /* Return the number of rows that were deleted. If this routine is
            ** generating code because of a call to sqlite3NestedParse(), do not
            ** invoke the callback function.
            */

            if ((db.flags & SQLITE_CountRows) != 0 && 0 == pParse.nested && null == pParse.pTriggerTab)
            {
                sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
                sqlite3VdbeSetNumCols(v, 1);
                sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
            }

delete_from_cleanup:
#if !SQLITE_OMIT_AUTHORIZATION
            sqlite3AuthContextPop(sContext);
#endif
            sqlite3SrcListDelete(db, ref pTabList);
            sqlite3ExprDelete(db, ref pWhere);
            return;
        }
Ejemplo n.º 28
0
        static void GenerateColumnNames(Parse parse, SrcList tabList, ExprList list)
        {
#if !OMIT_EXPLAIN
            // If this is an EXPLAIN, skip this step
            if (parse.Explain != 0)
                return;
#endif
            Vdbe v = parse.V;
            Context ctx = parse.Ctx;
            if (parse.ColNamesSet != 0 || C._NEVER(v == null) || ctx.MallocFailed) return;
            parse.ColNamesSet = 1;
            bool fullNames = ((ctx.Flags & Context.FLAG.FullColNames) != 0);
            bool shortNames = ((ctx.Flags & Context.FLAG.ShortColNames) != 0);
            v.SetNumCols(list.Exprs);
            for (int i = 0; i < list.Exprs; i++)
            {
                Expr p = list.Ids[i].Expr;
                if (C._NEVER(p == null)) continue;
                if (list.Ids[i].Name != null)
                {
                    string name = list.Ids[i].Name;
                    v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_TRANSIENT);
                }
                else if ((p.OP == TK.COLUMN || p.OP == TK.AGG_COLUMN) && tabList != null)
                {
                    int colId = p.ColumnIdx;
                    int j;
                    for (j = 0; C._ALWAYS(j < tabList.Srcs); j++)
                        if (tabList.Ids[j].Cursor == p.TableId) break;
                    Debug.Assert(j < tabList.Srcs);
                    Table table = tabList.Ids[j].Table;
                    if (colId < 0) colId = table.PKey;
                    Debug.Assert(colId == -1 || (colId >= 0 && colId < table.Cols.length));
                    string colName = (colId < 0 ? "rowid" : table.Cols[colId].Name);
                    if (!shortNames && !fullNames)
                        v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_DYNAMIC);
                    else if (fullNames)
                    {
                        string name = C._mtagprintf(ctx, "%s.%s", table.Name, colName);
                        v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_DYNAMIC);
                    }
                    else
                        v.SetColName(i, COLNAME_NAME, colName, C.DESTRUCTOR_TRANSIENT);
                }
                else
                    v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_TRANSIENT);
            }
            GenerateColumnTypes(parse, tabList, list);
        }
        /////////////////////////////////////////////////////////////////////////////////

        private Expression GenerateConvert(ExprCall pExpr)
        {
            PREDEFMETH pm = pExpr.PredefinedMethod;
            Expression e;
            Type       t;

            if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED ||
                pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED)
            {
                // If we have a user defined conversion, then we'll have the object
                // as the first element, and another list as a second element. This list
                // contains a TYPEOF as the first element, and the METHODINFO for the call
                // as the second.

                ExprList list  = (ExprList)pExpr.OptionalArguments;
                ExprList list2 = (ExprList)list.OptionalNextListNode;
                e = GetExpression(list.OptionalElement);
                t = ((ExprTypeOf)list2.OptionalElement).SourceType.AssociatedSystemType;

                if (e.Type.MakeByRefType() == t)
                {
                    // We're trying to convert from a type to its by ref type. Don't do that.
                    return(e);
                }
                Debug.Assert((pExpr.Flags & EXPRFLAG.EXF_UNBOXRUNTIME) == 0);

                MethodInfo m = ((ExprMethodInfo)list2.OptionalNextListNode).MethodInfo;

                if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED)
                {
                    return(Expression.Convert(e, t, m));
                }
                return(Expression.ConvertChecked(e, t, m));
            }
            else
            {
                Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT ||
                             pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED);

                // If we have a standard conversion, then we'll have some object as
                // the first list element (ie a WRAP or a CALL), and then a TYPEOF
                // as the second list element.
                ExprList list = (ExprList)pExpr.OptionalArguments;

                e = GetExpression(list.OptionalElement);
                t = ((ExprTypeOf)list.OptionalNextListNode).SourceType.AssociatedSystemType;

                if (e.Type.MakeByRefType() == t)
                {
                    // We're trying to convert from a type to its by ref type. Don't do that.
                    return(e);
                }

                if ((pExpr.Flags & EXPRFLAG.EXF_UNBOXRUNTIME) != 0)
                {
                    // If we want to unbox this thing, return that instead of the convert.
                    return(Expression.Unbox(e, t));
                }

                if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT)
                {
                    return(Expression.Convert(e, t));
                }
                return(Expression.ConvertChecked(e, t));
            }
        }
Ejemplo n.º 30
0
    /*
    ** Generate code that will tell the VDBE the declaration types of columns
    ** in the result set.
    */
    static void generateColumnTypes(
    Parse pParse,      /* Parser context */
    SrcList pTabList,  /* List of tables */
    ExprList pEList    /* Expressions defining the result set */
    )
    {
#if !SQLITE_OMIT_DECLTYPE
      Vdbe v = pParse.pVdbe;
      int i;
      NameContext sNC = new NameContext();
      sNC.pSrcList = pTabList;
      sNC.pParse = pParse;
      for ( i = 0; i < pEList.nExpr; i++ )
      {
        Expr p = pEList.a[i].pExpr;
        string zType;
#if SQLITE_ENABLE_COLUMN_METADATA
        string zOrigDb = null;
        string zOrigTab = null;
        string zOrigCol = null;
        zType = columnType( sNC, p, ref zOrigDb, ref zOrigTab, ref zOrigCol );

        /* The vdbe must make its own copy of the column-type and other
        ** column specific strings, in case the schema is reset before this
        ** virtual machine is deleted.
        */
        sqlite3VdbeSetColName( v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT );
        sqlite3VdbeSetColName( v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT );
        sqlite3VdbeSetColName( v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT );
#else
        string sDummy = null;
        zType = columnType( sNC, p, ref sDummy, ref sDummy, ref sDummy );
#endif
        sqlite3VdbeSetColName( v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT );
      }
#endif //* SQLITE_OMIT_DECLTYPE */
    }
        /////////////////////////////////////////////////////////////////////////////////

        private Expression GenerateBinaryOperator(ExprCall pExpr)
        {
            ExprList list = (ExprList)pExpr.OptionalArguments;

            Debug.Assert(list != null);
            Expression arg1 = GetExpression(list.OptionalElement);
            Expression arg2 = GetExpression(list.OptionalNextListNode);

            switch (pExpr.PredefinedMethod)
            {
            case PREDEFMETH.PM_EXPRESSION_ADD:
                return(Expression.Add(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_AND:
                return(Expression.And(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_DIVIDE:
                return(Expression.Divide(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_EQUAL:
                return(Expression.Equal(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR:
                return(Expression.ExclusiveOr(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_GREATERTHAN:
                return(Expression.GreaterThan(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL:
                return(Expression.GreaterThanOrEqual(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT:
                return(Expression.LeftShift(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_LESSTHAN:
                return(Expression.LessThan(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL:
                return(Expression.LessThanOrEqual(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_MODULO:
                return(Expression.Modulo(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_MULTIPLY:
                return(Expression.Multiply(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_NOTEQUAL:
                return(Expression.NotEqual(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_OR:
                return(Expression.Or(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT:
                return(Expression.RightShift(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_SUBTRACT:
                return(Expression.Subtract(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_ORELSE:
                return(Expression.OrElse(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_ANDALSO:
                return(Expression.AndAlso(arg1, arg2));

            // Checked
            case PREDEFMETH.PM_EXPRESSION_ADDCHECKED:
                return(Expression.AddChecked(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED:
                return(Expression.MultiplyChecked(arg1, arg2));

            case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED:
                return(Expression.SubtractChecked(arg1, arg2));

            default:
                Debug.Fail("Invalid Predefined Method in GenerateBinaryOperator");
                throw Error.InternalCompilerError();
            }
        }
Ejemplo n.º 32
0
    /*
    ** Given a an expression list (which is really the list of expressions
    ** that form the result set of a SELECT statement) compute appropriate
    ** column names for a table that would hold the expression list.
    **
    ** All column names will be unique.
    **
    ** Only the column names are computed.  Column.zType, Column.zColl,
    ** and other fields of Column are zeroed.
    **
    ** Return SQLITE_OK on success.  If a memory allocation error occurs,
    ** store NULL in paCol and 0 in pnCol and return SQLITE_NOMEM.
    */
    static int selectColumnsFromExprList(
    Parse pParse,          /* Parsing context */
    ExprList pEList,       /* Expr list from which to derive column names */
    ref int pnCol,             /* Write the number of columns here */
    ref Column[] paCol          /* Write the new column list here */
    )
    {
      sqlite3 db = pParse.db;     /* Database connection */
      int i, j;                   /* Loop counters */
      int cnt;                    /* Index added to make the name unique */
      Column[] aCol;
      Column pCol; /* For looping over result columns */
      int nCol;                   /* Number of columns in the result set */
      Expr p;                     /* Expression for a single result column */
      string zName;               /* Column name */
      int nName;                  /* Size of name in zName[] */


      pnCol = nCol = pEList.nExpr;
      aCol = paCol = new Column[nCol];//sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
      //if ( aCol == null )
      //  return SQLITE_NOMEM;
      for ( i = 0; i < nCol; i++ )//, pCol++)
      {
        if ( aCol[i] == null )
          aCol[i] = new Column();
        pCol = aCol[i];
        /* Get an appropriate name for the column
        */
        p = pEList.a[i].pExpr;
        Debug.Assert( p.pRight == null || ExprHasProperty( p.pRight, EP_IntValue )
        || p.pRight.u.zToken == null || p.pRight.u.zToken.Length > 0 );
        if ( pEList.a[i].zName != null && ( zName = pEList.a[i].zName ) != string.Empty )
        {
          /* If the column contains an "AS <name>" phrase, use <name> as the name */
          //zName = sqlite3DbStrDup(db, zName);
        }
        else
        {
          Expr pColExpr = p;      /* The expression that is the result column name */
          Table pTab;             /* Table associated with this expression */
          while ( pColExpr.op == TK_DOT )
            pColExpr = pColExpr.pRight;
          if ( pColExpr.op == TK_COLUMN && ALWAYS( pColExpr.pTab != null ) )
          {
            /* For columns use the column name name */
            int iCol = pColExpr.iColumn;
            pTab = pColExpr.pTab;
            if ( iCol < 0 )
              iCol = pTab.iPKey;
            zName = sqlite3MPrintf( db, "%s",
            iCol >= 0 ? pTab.aCol[iCol].zName : "rowid" );
          }
          else if ( pColExpr.op == TK_ID )
          {
            Debug.Assert( !ExprHasProperty( pColExpr, EP_IntValue ) );
            zName = sqlite3MPrintf( db, "%s", pColExpr.u.zToken );
          }
          else
          {
            /* Use the original text of the column expression as its name */
            zName = sqlite3MPrintf( db, "%s", pEList.a[i].zSpan );
          }
        }
        //if ( db.mallocFailed != 0 )
        //{
        //  sqlite3DbFree( db, ref zName );
        //  break;
        //}

        /* Make sure the column name is unique.  If the name is not unique,
        ** append a integer to the name so that it becomes unique.
        */
        nName = sqlite3Strlen30( zName );
        for ( j = cnt = 0; j < i; j++ )
        {
          if ( aCol[j].zName.Equals( zName, StringComparison.OrdinalIgnoreCase ) )
          {
            string zNewName;
            //zName[nName] = 0;
            zNewName = sqlite3MPrintf( db, "%s:%d", zName.Substring( 0, nName ), ++cnt );
            sqlite3DbFree( db, ref zName );
            zName = zNewName;
            j = -1;
            if ( zName.Length == 0 )
              break;
          }
        }
        pCol.zName = zName;
      }
      //if ( db.mallocFailed != 0 )
      //{
      //  for ( j = 0 ; j < i ; j++ )
      //  {
      //    sqlite3DbFree( db, aCol[j].zName );
      //  }
      //  sqlite3DbFree( db, aCol );
      //  paCol = null;
      //  pnCol = 0;
      //  return SQLITE_NOMEM;
      //}
      return SQLITE_OK;
    }
        /////////////////////////////////////////////////////////////////////////////////

        private Expression GenerateUserDefinedBinaryOperator(ExprCall pExpr)
        {
            ExprList   list = (ExprList)pExpr.OptionalArguments;
            Expression arg1 = GetExpression(list.OptionalElement);
            Expression arg2 = GetExpression(((ExprList)list.OptionalNextListNode).OptionalElement);

            list = (ExprList)list.OptionalNextListNode;
            MethodInfo methodInfo;
            bool       bIsLifted = false;

            if (list.OptionalNextListNode is ExprList next)
            {
                ExprConstant isLifted = (ExprConstant)next.OptionalElement;
                Debug.Assert(isLifted != null);
                bIsLifted  = isLifted.Val.Int32Val == 1;
                methodInfo = ((ExprMethodInfo)next.OptionalNextListNode).MethodInfo;
            }
            else
            {
                methodInfo = ((ExprMethodInfo)list.OptionalNextListNode).MethodInfo;
            }

            switch (pExpr.PredefinedMethod)
            {
            case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED:
                return(Expression.Add(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED:
                return(Expression.And(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED:
                return(Expression.Divide(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED:
                return(Expression.Equal(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED:
                return(Expression.ExclusiveOr(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED:
                return(Expression.GreaterThan(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED:
                return(Expression.GreaterThanOrEqual(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED:
                return(Expression.LeftShift(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED:
                return(Expression.LessThan(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED:
                return(Expression.LessThanOrEqual(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED:
                return(Expression.Modulo(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED:
                return(Expression.Multiply(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED:
                return(Expression.NotEqual(arg1, arg2, bIsLifted, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED:
                return(Expression.Or(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED:
                return(Expression.RightShift(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED:
                return(Expression.Subtract(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED:
                return(Expression.OrElse(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED:
                return(Expression.AndAlso(arg1, arg2, methodInfo));

            // Checked
            case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED:
                return(Expression.AddChecked(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED:
                return(Expression.MultiplyChecked(arg1, arg2, methodInfo));

            case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED:
                return(Expression.SubtractChecked(arg1, arg2, methodInfo));

            default:
                Debug.Fail("Invalid Predefined Method in GenerateUserDefinedBinaryOperator");
                throw Error.InternalCompilerError();
            }
        }
Ejemplo n.º 34
0
 static void substExprList(
 sqlite3 db,         /* Report malloc errors here */
 ExprList pList,     /* List to scan and in which to make substitutes */
 int iTable,          /* Table to be substituted */
 ExprList pEList     /* Substitute values */
 )
 {
   int i;
   if ( pList == null )
     return;
   for ( i = 0; i < pList.nExpr; i++ )
   {
     pList.a[i].pExpr = substExpr( db, pList.a[i].pExpr, iTable, pEList );
   }
 }
        /////////////////////////////////////////////////////////////////////////////////

        private Expression GetExpression(Expr pExpr)
        {
            if (pExpr is ExprWrap wrap)
            {
                return(_DictionaryOfParameters[(ExprCall)wrap.OptionalExpression]);
            }
            else if (pExpr is ExprConstant)
            {
                Debug.Assert(pExpr.Type is NullType);
                return(null);
            }
            else
            {
                // We can have a convert node or a call of a user defined conversion.
                ExprCall call = (ExprCall)pExpr;
                Debug.Assert(call != null);
                PREDEFMETH pm = call.PredefinedMethod;
                Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT ||
                             pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT ||
                             pm == PREDEFMETH.PM_EXPRESSION_CALL ||
                             pm == PREDEFMETH.PM_EXPRESSION_PROPERTY ||
                             pm == PREDEFMETH.PM_EXPRESSION_FIELD ||
                             pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX ||
                             pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2 ||
                             pm == PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE ||
                             pm == PREDEFMETH.PM_EXPRESSION_NEW ||

                             // Binary operators.
                             pm == PREDEFMETH.PM_EXPRESSION_ASSIGN ||
                             pm == PREDEFMETH.PM_EXPRESSION_ADD ||
                             pm == PREDEFMETH.PM_EXPRESSION_AND ||
                             pm == PREDEFMETH.PM_EXPRESSION_DIVIDE ||
                             pm == PREDEFMETH.PM_EXPRESSION_EQUAL ||
                             pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR ||
                             pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN ||
                             pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL ||
                             pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT ||
                             pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN ||
                             pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL ||
                             pm == PREDEFMETH.PM_EXPRESSION_MODULO ||
                             pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY ||
                             pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL ||
                             pm == PREDEFMETH.PM_EXPRESSION_OR ||
                             pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT ||
                             pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT ||
                             pm == PREDEFMETH.PM_EXPRESSION_ORELSE ||
                             pm == PREDEFMETH.PM_EXPRESSION_ANDALSO ||
                             pm == PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED ||

                             // Checked binary
                             pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED ||
                             pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED ||
                             pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED ||
                             pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED ||

                             // Unary operators.
                             pm == PREDEFMETH.PM_EXPRESSION_NOT ||
                             pm == PREDEFMETH.PM_EXPRESSION_NEGATE ||
                             pm == PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED ||

                             // Checked unary
                             pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED ||
                             pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED ||
                             pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED ||
                             pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED
                             );

                switch (pm)
                {
                case PREDEFMETH.PM_EXPRESSION_CALL:
                    return(GenerateCall(call));

                case PREDEFMETH.PM_EXPRESSION_CONVERT:
                case PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED:
                case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED:
                    return(GenerateConvert(call));

                case PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT:
                    ExprList list = (ExprList)call.OptionalArguments;
                    return
                        (Expression.NewArrayInit(
                             ((ExprTypeOf)list.OptionalElement).SourceType.AssociatedSystemType,
                             GetArgumentsFromArrayInit((ExprArrayInit)list.OptionalNextListNode)));

                case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX:
                case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2:
                    return(GenerateArrayIndex(call));

                case PREDEFMETH.PM_EXPRESSION_NEW:
                    return(GenerateNew(call));

                case PREDEFMETH.PM_EXPRESSION_PROPERTY:
                    return(GenerateProperty(call));

                case PREDEFMETH.PM_EXPRESSION_FIELD:
                    return(GenerateField(call));

                case PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE:
                    return(GenerateConstantType(call));

                case PREDEFMETH.PM_EXPRESSION_ASSIGN:
                    return(GenerateAssignment(call));

                case PREDEFMETH.PM_EXPRESSION_ADD:
                case PREDEFMETH.PM_EXPRESSION_AND:
                case PREDEFMETH.PM_EXPRESSION_DIVIDE:
                case PREDEFMETH.PM_EXPRESSION_EQUAL:
                case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR:
                case PREDEFMETH.PM_EXPRESSION_GREATERTHAN:
                case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL:
                case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT:
                case PREDEFMETH.PM_EXPRESSION_LESSTHAN:
                case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL:
                case PREDEFMETH.PM_EXPRESSION_MODULO:
                case PREDEFMETH.PM_EXPRESSION_MULTIPLY:
                case PREDEFMETH.PM_EXPRESSION_NOTEQUAL:
                case PREDEFMETH.PM_EXPRESSION_OR:
                case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT:
                case PREDEFMETH.PM_EXPRESSION_SUBTRACT:
                case PREDEFMETH.PM_EXPRESSION_ORELSE:
                case PREDEFMETH.PM_EXPRESSION_ANDALSO:
                // Checked
                case PREDEFMETH.PM_EXPRESSION_ADDCHECKED:
                case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED:
                case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED:
                    return(GenerateBinaryOperator(call));

                case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED:
                // Checked
                case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED:
                    return(GenerateUserDefinedBinaryOperator(call));

                case PREDEFMETH.PM_EXPRESSION_NOT:
                case PREDEFMETH.PM_EXPRESSION_NEGATE:
                case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED:
                    return(GenerateUnaryOperator(call));

                case PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED:
                case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED:
                    return(GenerateUserDefinedUnaryOperator(call));

                default:
                    Debug.Fail("Invalid Predefined Method in GetExpression");
                    throw Error.InternalCompilerError();
                }
            }
        }
Ejemplo n.º 36
0
    static int sqlite3Select(
    Parse pParse,         /* The parser context */
    Select p,             /* The SELECT statement being coded. */
    ref SelectDest pDest /* What to do with the query results */
    )
    {
      int i, j;               /* Loop counters */
      WhereInfo pWInfo;       /* Return from sqlite3WhereBegin() */
      Vdbe v;                 /* The virtual machine under construction */
      bool isAgg;             /* True for select lists like "count()" */
      ExprList pEList = new ExprList();      /* List of columns to extract. */
      SrcList pTabList = new SrcList();     /* List of tables to select from */
      Expr pWhere;            /* The WHERE clause.  May be NULL */
      ExprList pOrderBy;      /* The ORDER BY clause.  May be NULL */
      ExprList pGroupBy;      /* The GROUP BY clause.  May be NULL */
      Expr pHaving;           /* The HAVING clause.  May be NULL */
      bool isDistinct;        /* True if the DISTINCT keyword is present */
      int distinct;           /* Table to use for the distinct set */
      int rc = 1;             /* Value to return from this function */
      int addrSortIndex;      /* Address of an OP_OpenEphemeral instruction */
      AggInfo sAggInfo;       /* Information used by aggregate queries */
      int iEnd;               /* Address of the end of the query */
      sqlite3 db;             /* The database connection */

#if !SQLITE_OMIT_EXPLAIN
      int iRestoreSelectId = pParse.iSelectId;
      pParse.iSelectId = pParse.iNextSelectId++;
#endif

      db = pParse.db;
      if ( p == null /*|| db.mallocFailed != 0 */ || pParse.nErr != 0 )
      {
        return 1;
      }
#if !SQLITE_OMIT_AUTHORIZATION
if (sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0)) return 1;
#endif
      sAggInfo = new AggInfo();// memset(sAggInfo, 0, sAggInfo).Length;

      if ( pDest.eDest <= SRT_Discard ) //IgnorableOrderby(pDest))
      {
        Debug.Assert( pDest.eDest == SRT_Exists || pDest.eDest == SRT_Union ||
        pDest.eDest == SRT_Except || pDest.eDest == SRT_Discard );
        /* If ORDER BY makes no difference in the output then neither does
        ** DISTINCT so it can be removed too. */
        sqlite3ExprListDelete( db, ref p.pOrderBy );
        p.pOrderBy = null;
        p.selFlags = (u16)( p.selFlags & ~SF_Distinct );
      }
      sqlite3SelectPrep( pParse, p, null );
      pOrderBy = p.pOrderBy;
      pTabList = p.pSrc;
      pEList = p.pEList;
      if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
      {
        goto select_end;
      }
      isAgg = ( p.selFlags & SF_Aggregate ) != 0;
      Debug.Assert( pEList != null );

      /* Begin generating code.
      */
      v = sqlite3GetVdbe( pParse );
      if ( v == null )
        goto select_end;

      /* If writing to memory or generating a set
      ** only a single column may be output.
      */
#if !SQLITE_OMIT_SUBQUERY
      if ( checkForMultiColumnSelectError( pParse, pDest, pEList.nExpr ) )
      {
        goto select_end;
      }
#endif

      /* Generate code for all sub-queries in the FROM clause
*/
#if !SQLITE_OMIT_SUBQUERY || !SQLITE_OMIT_VIEW
      for ( i = 0; p.pPrior == null && i < pTabList.nSrc; i++ )
      {
        SrcList_item pItem = pTabList.a[i];
        SelectDest dest = new SelectDest();
        Select pSub = pItem.pSelect;
        bool isAggSub;

        if ( pSub == null || pItem.isPopulated != 0 )
          continue;

        /* Increment Parse.nHeight by the height of the largest expression
        ** tree refered to by this, the parent select. The child select
        ** may contain expression trees of at most
        ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit
        ** more conservative than necessary, but much easier than enforcing
        ** an exact limit.
        */
        pParse.nHeight += sqlite3SelectExprHeight( p );

        /* Check to see if the subquery can be absorbed into the parent. */
        isAggSub = ( pSub.selFlags & SF_Aggregate ) != 0;
        if ( flattenSubquery( pParse, p, i, isAgg, isAggSub ) != 0 )
        {
          if ( isAggSub )
          {
            isAgg = true;
            p.selFlags |= SF_Aggregate;
          }
          i = -1;
        }
        else
        {
          sqlite3SelectDestInit( dest, SRT_EphemTab, pItem.iCursor );
          Debug.Assert( 0 == pItem.isPopulated );
          explainSetInteger( ref pItem.iSelectId, (int)pParse.iNextSelectId );
          sqlite3Select( pParse, pSub, ref dest );
          pItem.isPopulated = 1;
          pItem.pTab.nRowEst = (uint)pSub.nSelectRow;
        }
        //if ( /* pParse.nErr != 0 || */ db.mallocFailed != 0 )
        //{
        //  goto select_end;
        //}
        pParse.nHeight -= sqlite3SelectExprHeight( p );
        pTabList = p.pSrc;
        if ( !( pDest.eDest <= SRT_Discard ) )//        if( null==IgnorableOrderby(pDest) )
        {
          pOrderBy = p.pOrderBy;
        }
      }
      pEList = p.pEList;
#endif
      pWhere = p.pWhere;
      pGroupBy = p.pGroupBy;
      pHaving = p.pHaving;
      isDistinct = ( p.selFlags & SF_Distinct ) != 0;

#if  !SQLITE_OMIT_COMPOUND_SELECT
      /* If there is are a sequence of queries, do the earlier ones first.
*/
      if ( p.pPrior != null )
      {
        if ( p.pRightmost == null )
        {
          Select pLoop, pRight = null;
          int cnt = 0;
          int mxSelect;
          for ( pLoop = p; pLoop != null; pLoop = pLoop.pPrior, cnt++ )
          {
            pLoop.pRightmost = p;
            pLoop.pNext = pRight;
            pRight = pLoop;
          }
          mxSelect = db.aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
          if ( mxSelect != 0 && cnt > mxSelect )
          {
            sqlite3ErrorMsg( pParse, "too many terms in compound SELECT" );
            goto select_end;
          }
        }
        rc = multiSelect( pParse, p, pDest );
        explainSetInteger( ref pParse.iSelectId, iRestoreSelectId );
        return rc;
      }
#endif

      /* If possible, rewrite the query to use GROUP BY instead of DISTINCT.
** GROUP BY might use an index, DISTINCT never does.
*/
      Debug.Assert( p.pGroupBy == null || ( p.selFlags & SF_Aggregate ) != 0 );
      if ( ( p.selFlags & ( SF_Distinct | SF_Aggregate ) ) == SF_Distinct )
      {
        p.pGroupBy = sqlite3ExprListDup( db, p.pEList, 0 );
        pGroupBy = p.pGroupBy;
        p.selFlags = (u16)( p.selFlags & ~SF_Distinct );
      }

      /* If there is both a GROUP BY and an ORDER BY clause and they are
      ** identical, then disable the ORDER BY clause since the GROUP BY
      ** will cause elements to come out in the correct order.  This is
      ** an optimization - the correct answer should result regardless.
      ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER
      ** to disable this optimization for testing purposes.
      */
      if ( sqlite3ExprListCompare( p.pGroupBy, pOrderBy ) == 0
      && ( db.flags & SQLITE_GroupByOrder ) == 0 )
      {
        pOrderBy = null;
      }

      /* If there is an ORDER BY clause, then this sorting
      ** index might end up being unused if the data can be
      ** extracted in pre-sorted order.  If that is the case, then the
      ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
      ** we figure out that the sorting index is not needed.  The addrSortIndex
      ** variable is used to facilitate that change.
      */
      if ( pOrderBy != null )
      {
        KeyInfo pKeyInfo;
        pKeyInfo = keyInfoFromExprList( pParse, pOrderBy );
        pOrderBy.iECursor = pParse.nTab++;
        p.addrOpenEphm[2] = addrSortIndex =
        sqlite3VdbeAddOp4( v, OP_OpenEphemeral,
        pOrderBy.iECursor, pOrderBy.nExpr + 2, 0,
        pKeyInfo, P4_KEYINFO_HANDOFF );
      }
      else
      {
        addrSortIndex = -1;
      }

      /* If the output is destined for a temporary table, open that table.
      */
      if ( pDest.eDest == SRT_EphemTab )
      {
        sqlite3VdbeAddOp2( v, OP_OpenEphemeral, pDest.iParm, pEList.nExpr );
      }

      /* Set the limiter.
      */
      iEnd = sqlite3VdbeMakeLabel( v );
      p.nSelectRow = (double)LARGEST_INT64;
      computeLimitRegisters( pParse, p, iEnd );

      /* Open a virtual index to use for the distinct set.
      */
      if ( ( p.selFlags & SF_Distinct ) != 0 )
      {
        KeyInfo pKeyInfo;
        Debug.Assert( isAgg || pGroupBy != null );
        distinct = pParse.nTab++;
        pKeyInfo = keyInfoFromExprList( pParse, p.pEList );
        sqlite3VdbeAddOp4( v, OP_OpenEphemeral, distinct, 0, 0,
        pKeyInfo, P4_KEYINFO_HANDOFF );
        sqlite3VdbeChangeP5( v, BTREE_UNORDERED );
      }
      else
      {
        distinct = -1;
      }

      /* Aggregate and non-aggregate queries are handled differently */
      if ( !isAgg && pGroupBy == null )
      {
        /* This case is for non-aggregate queries
        ** Begin the database scan
        */
        pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pOrderBy, 0 );
        if ( pWInfo == null )
          goto select_end;
        if ( pWInfo.nRowOut < p.nSelectRow )
          p.nSelectRow = pWInfo.nRowOut;

        /* If sorting index that was created by a prior OP_OpenEphemeral
        ** instruction ended up not being needed, then change the OP_OpenEphemeral
        ** into an OP_Noop.
        */
        if ( addrSortIndex >= 0 && pOrderBy == null )
        {
          sqlite3VdbeChangeToNoop( v, addrSortIndex, 1 );
          p.addrOpenEphm[2] = -1;
        }

        /* Use the standard inner loop
        */
        Debug.Assert( !isDistinct );
        selectInnerLoop( pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
        pWInfo.iContinue, pWInfo.iBreak );

        /* End the database scan loop.
        */
        sqlite3WhereEnd( pWInfo );
      }
      else
      {
        /* This is the processing for aggregate queries */
        NameContext sNC;    /* Name context for processing aggregate information */
        int iAMem;          /* First Mem address for storing current GROUP BY */
        int iBMem;          /* First Mem address for previous GROUP BY */
        int iUseFlag;       /* Mem address holding flag indicating that at least
** one row of the input to the aggregator has been
** processed */
        int iAbortFlag;     /* Mem address which causes query abort if positive */
        int groupBySort;    /* Rows come from source in GR BY' clause thanROUP BY order */

        int addrEnd;        /* End of processing for this SELECT */

        /* Remove any and all aliases between the result set and the
        ** GROUP BY clause.
        */
        if ( pGroupBy != null )
        {
          int k;                        /* Loop counter */
          ExprList_item pItem;          /* For looping over expression in a list */

          for ( k = p.pEList.nExpr; k > 0; k-- )//, pItem++)
          {
            pItem = p.pEList.a[p.pEList.nExpr - k];
            pItem.iAlias = 0;
          }
          for ( k = pGroupBy.nExpr; k > 0; k-- )//, pItem++ )
          {
            pItem = pGroupBy.a[pGroupBy.nExpr - k];
            pItem.iAlias = 0;
          }
          if ( p.nSelectRow > (double)100 )
            p.nSelectRow = (double)100;
        }
        else
        {
          p.nSelectRow = (double)1;
        }

        /* Create a label to jump to when we want to abort the query */
        addrEnd = sqlite3VdbeMakeLabel( v );

        /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
        ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
        ** SELECT statement.
        */
        sNC = new NameContext(); // memset(sNC, 0, sNC).Length;
        sNC.pParse = pParse;
        sNC.pSrcList = pTabList;
        sNC.pAggInfo = sAggInfo;
        sAggInfo.nSortingColumn = pGroupBy != null ? pGroupBy.nExpr + 1 : 0;
        sAggInfo.pGroupBy = pGroupBy;
        sqlite3ExprAnalyzeAggList( sNC, pEList );
        sqlite3ExprAnalyzeAggList( sNC, pOrderBy );
        if ( pHaving != null )
        {
          sqlite3ExprAnalyzeAggregates( sNC, ref pHaving );
        }
        sAggInfo.nAccumulator = sAggInfo.nColumn;
        for ( i = 0; i < sAggInfo.nFunc; i++ )
        {
          Debug.Assert( !ExprHasProperty( sAggInfo.aFunc[i].pExpr, EP_xIsSelect ) );
          sqlite3ExprAnalyzeAggList( sNC, sAggInfo.aFunc[i].pExpr.x.pList );
        }
        //      if ( db.mallocFailed != 0 ) goto select_end;

        /* Processing for aggregates with GROUP BY is very different and
        ** much more complex than aggregates without a GROUP BY.
        */
        if ( pGroupBy != null )
        {
          KeyInfo pKeyInfo;  /* Keying information for the group by clause */
          int j1;             /* A-vs-B comparision jump */
          int addrOutputRow;  /* Start of subroutine that outputs a result row */
          int regOutputRow;   /* Return address register for output subroutine */
          int addrSetAbort;   /* Set the abort flag and return */
          int addrTopOfLoop;  /* Top of the input loop */
          int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */
          int addrReset;      /* Subroutine for resetting the accumulator */
          int regReset;       /* Return address register for reset subroutine */

          /* If there is a GROUP BY clause we might need a sorting index to
          ** implement it.  Allocate that sorting index now.  If it turns out
          ** that we do not need it after all, the OpenEphemeral instruction
          ** will be converted into a Noop.
          */
          sAggInfo.sortingIdx = pParse.nTab++;
          pKeyInfo = keyInfoFromExprList( pParse, pGroupBy );
          addrSortingIdx = sqlite3VdbeAddOp4( v, OP_OpenEphemeral,
          sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
          0, pKeyInfo, P4_KEYINFO_HANDOFF );

          /* Initialize memory locations used by GROUP BY aggregate processing
          */
          iUseFlag = ++pParse.nMem;
          iAbortFlag = ++pParse.nMem;
          regOutputRow = ++pParse.nMem;
          addrOutputRow = sqlite3VdbeMakeLabel( v );
          regReset = ++pParse.nMem;
          addrReset = sqlite3VdbeMakeLabel( v );
          iAMem = pParse.nMem + 1;
          pParse.nMem += pGroupBy.nExpr;
          iBMem = pParse.nMem + 1;
          pParse.nMem += pGroupBy.nExpr;
          sqlite3VdbeAddOp2( v, OP_Integer, 0, iAbortFlag );
#if SQLITE_DEBUG
          VdbeComment( v, "clear abort flag" );
#endif
          sqlite3VdbeAddOp2( v, OP_Integer, 0, iUseFlag );
#if SQLITE_DEBUG
          VdbeComment( v, "indicate accumulator empty" );
#endif

          /* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
** it might be a single loop that uses an index to extract information
** in the right order to begin with.
*/
          sqlite3VdbeAddOp2( v, OP_Gosub, regReset, addrReset );
          pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pGroupBy, 0 );
          if ( pWInfo == null )
            goto select_end;
          if ( pGroupBy == null )
          {
            /* The optimizer is able to deliver rows in group by order so
            ** we do not have to sort.  The OP_OpenEphemeral table will be
            ** cancelled later because we still need to use the pKeyInfo
            */
            pGroupBy = p.pGroupBy;
            groupBySort = 0;
          }
          else
          {
            /* Rows are coming out in undetermined order.  We have to push
            ** each row into a sorting index, terminate the first loop,
            ** then loop over the sorting index in order to get the output
            ** in sorted order
            */
            int regBase;
            int regRecord;
            int nCol;
            int nGroupBy;

            explainTempTable( pParse,
            isDistinct && 0 == ( p.selFlags & SF_Distinct ) ? "DISTINCT" : "GROUP BY" );

            groupBySort = 1;
            nGroupBy = pGroupBy.nExpr;
            nCol = nGroupBy + 1;
            j = nGroupBy + 1;
            for ( i = 0; i < sAggInfo.nColumn; i++ )
            {
              if ( sAggInfo.aCol[i].iSorterColumn >= j )
              {
                nCol++;
                j++;
              }
            }
            regBase = sqlite3GetTempRange( pParse, nCol );
            sqlite3ExprCacheClear( pParse );
            sqlite3ExprCodeExprList( pParse, pGroupBy, regBase, false );
            sqlite3VdbeAddOp2( v, OP_Sequence, sAggInfo.sortingIdx, regBase + nGroupBy );
            j = nGroupBy + 1;
            for ( i = 0; i < sAggInfo.nColumn; i++ )
            {
              AggInfo_col pCol = sAggInfo.aCol[i];
              if ( pCol.iSorterColumn >= j )
              {
                int r1 = j + regBase;
                int r2;
                r2 = sqlite3ExprCodeGetColumn( pParse,
                pCol.pTab, pCol.iColumn, pCol.iTable, r1 );
                if ( r1 != r2 )
                {
                  sqlite3VdbeAddOp2( v, OP_SCopy, r2, r1 );
                }
                j++;
              }
            }
            regRecord = sqlite3GetTempReg( pParse );
            sqlite3VdbeAddOp3( v, OP_MakeRecord, regBase, nCol, regRecord );
            sqlite3VdbeAddOp2( v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord );
            sqlite3ReleaseTempReg( pParse, regRecord );
            sqlite3ReleaseTempRange( pParse, regBase, nCol );
            sqlite3WhereEnd( pWInfo );
            sqlite3VdbeAddOp2( v, OP_Sort, sAggInfo.sortingIdx, addrEnd );
#if SQLITE_DEBUG
            VdbeComment( v, "GROUP BY sort" );
#endif
            sAggInfo.useSortingIdx = 1;
            sqlite3ExprCacheClear( pParse );
          }

          /* Evaluate the current GROUP BY terms and store in b0, b1, b2...
          ** (b0 is memory location iBMem+0, b1 is iBMem+1, and so forth)
          ** Then compare the current GROUP BY terms against the GROUP BY terms
          ** from the previous row currently stored in a0, a1, a2...
          */
          addrTopOfLoop = sqlite3VdbeCurrentAddr( v );
          sqlite3ExprCacheClear( pParse );
          for ( j = 0; j < pGroupBy.nExpr; j++ )
          {
            if ( groupBySort != 0 )
            {
              sqlite3VdbeAddOp3( v, OP_Column, sAggInfo.sortingIdx, j, iBMem + j );
            }
            else
            {
              sAggInfo.directMode = 1;
              sqlite3ExprCode( pParse, pGroupBy.a[j].pExpr, iBMem + j );
            }
          }
          sqlite3VdbeAddOp4( v, OP_Compare, iAMem, iBMem, pGroupBy.nExpr,
          pKeyInfo, P4_KEYINFO );
          j1 = sqlite3VdbeCurrentAddr( v );
          sqlite3VdbeAddOp3( v, OP_Jump, j1 + 1, 0, j1 + 1 );

          /* Generate code that runs whenever the GROUP BY changes.
          ** Changes in the GROUP BY are detected by the previous code
          ** block.  If there were no changes, this block is skipped.
          **
          ** This code copies current group by terms in b0,b1,b2,...
          ** over to a0,a1,a2.  It then calls the output subroutine
          ** and resets the aggregate accumulator registers in preparation
          ** for the next GROUP BY batch.
          */
          sqlite3ExprCodeMove( pParse, iBMem, iAMem, pGroupBy.nExpr );
          sqlite3VdbeAddOp2( v, OP_Gosub, regOutputRow, addrOutputRow );
#if SQLITE_DEBUG
          VdbeComment( v, "output one row" );
#endif
          sqlite3VdbeAddOp2( v, OP_IfPos, iAbortFlag, addrEnd );
#if SQLITE_DEBUG
          VdbeComment( v, "check abort flag" );
#endif
          sqlite3VdbeAddOp2( v, OP_Gosub, regReset, addrReset );
#if SQLITE_DEBUG
          VdbeComment( v, "reset accumulator" );
#endif

          /* Update the aggregate accumulators based on the content of
** the current row
*/
          sqlite3VdbeJumpHere( v, j1 );
          updateAccumulator( pParse, sAggInfo );
          sqlite3VdbeAddOp2( v, OP_Integer, 1, iUseFlag );
#if SQLITE_DEBUG
          VdbeComment( v, "indicate data in accumulator" );
#endif
          /* End of the loop
*/
          if ( groupBySort != 0 )
          {
            sqlite3VdbeAddOp2( v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop );
          }
          else
          {
            sqlite3WhereEnd( pWInfo );
            sqlite3VdbeChangeToNoop( v, addrSortingIdx, 1 );
          }

          /* Output the final row of result
          */
          sqlite3VdbeAddOp2( v, OP_Gosub, regOutputRow, addrOutputRow );
#if SQLITE_DEBUG
          VdbeComment( v, "output final row" );
#endif
          /* Jump over the subroutines
*/
          sqlite3VdbeAddOp2( v, OP_Goto, 0, addrEnd );

          /* Generate a subroutine that outputs a single row of the result
          ** set.  This subroutine first looks at the iUseFlag.  If iUseFlag
          ** is less than or equal to zero, the subroutine is a no-op.  If
          ** the processing calls for the query to abort, this subroutine
          ** increments the iAbortFlag memory location before returning in
          ** order to signal the caller to abort.
          */
          addrSetAbort = sqlite3VdbeCurrentAddr( v );
          sqlite3VdbeAddOp2( v, OP_Integer, 1, iAbortFlag );
          VdbeComment( v, "set abort flag" );
          sqlite3VdbeAddOp1( v, OP_Return, regOutputRow );
          sqlite3VdbeResolveLabel( v, addrOutputRow );
          addrOutputRow = sqlite3VdbeCurrentAddr( v );
          sqlite3VdbeAddOp2( v, OP_IfPos, iUseFlag, addrOutputRow + 2 );
          VdbeComment( v, "Groupby result generator entry point" );
          sqlite3VdbeAddOp1( v, OP_Return, regOutputRow );
          finalizeAggFunctions( pParse, sAggInfo );
          sqlite3ExprIfFalse( pParse, pHaving, addrOutputRow + 1, SQLITE_JUMPIFNULL );
          selectInnerLoop( pParse, p, p.pEList, 0, 0, pOrderBy,
          distinct, pDest,
          addrOutputRow + 1, addrSetAbort );
          sqlite3VdbeAddOp1( v, OP_Return, regOutputRow );
          VdbeComment( v, "end groupby result generator" );

          /* Generate a subroutine that will reset the group-by accumulator
          */
          sqlite3VdbeResolveLabel( v, addrReset );
          resetAccumulator( pParse, sAggInfo );
          sqlite3VdbeAddOp1( v, OP_Return, regReset );

        } /* endif pGroupBy.  Begin aggregate queries without GROUP BY: */
        else
        {
          ExprList pDel = null;
#if !SQLITE_OMIT_BTREECOUNT
          Table pTab;
          if ( ( pTab = isSimpleCount( p, sAggInfo ) ) != null )
          {
            /* If isSimpleCount() returns a pointer to a Table structure, then
            ** the SQL statement is of the form:
            **
            **   SELECT count() FROM <tbl>
            **
            ** where the Table structure returned represents table <tbl>.
            **
            ** This statement is so common that it is optimized specially. The
            ** OP_Count instruction is executed either on the intkey table that
            ** contains the data for table <tbl> or on one of its indexes. It
            ** is better to execute the op on an index, as indexes are almost
            ** always spread across less pages than their corresponding tables.
            */
            int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
            int iCsr = pParse.nTab++;     /* Cursor to scan b-tree */
            Index pIdx;                   /* Iterator variable */
            KeyInfo pKeyInfo = null;      /* Keyinfo for scanned index */
            Index pBest = null;           /* Best index found so far */
            int iRoot = pTab.tnum;        /* Root page of scanned b-tree */

            sqlite3CodeVerifySchema( pParse, iDb );
            sqlite3TableLock( pParse, iDb, pTab.tnum, 0, pTab.zName );

            /* Search for the index that has the least amount of columns. If
            ** there is such an index, and it has less columns than the table
            ** does, then we can assume that it consumes less space on disk and
            ** will therefore be cheaper to scan to determine the query result.
            ** In this case set iRoot to the root page number of the index b-tree
            ** and pKeyInfo to the KeyInfo structure required to navigate the
            ** index.
            **
            ** (2011-04-15) Do not do a full scan of an unordered index.
            **
            ** In practice the KeyInfo structure will not be used. It is only
            ** passed to keep OP_OpenRead happy.
            */
            for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
            {
              if ( pIdx.bUnordered == 0 && ( null == pBest || pIdx.nColumn < pBest.nColumn ) )
              {
                pBest = pIdx;
              }
            }
            if ( pBest != null && pBest.nColumn < pTab.nCol )
            {
              iRoot = pBest.tnum;
              pKeyInfo = sqlite3IndexKeyinfo( pParse, pBest );
            }

            /* Open a read-only cursor, execute the OP_Count, close the cursor. */
            sqlite3VdbeAddOp3( v, OP_OpenRead, iCsr, iRoot, iDb );
            if ( pKeyInfo != null )
            {
              sqlite3VdbeChangeP4( v, -1, pKeyInfo, P4_KEYINFO_HANDOFF );
            }
            sqlite3VdbeAddOp2( v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem );
            sqlite3VdbeAddOp1( v, OP_Close, iCsr );
            explainSimpleCount( pParse, pTab, pBest );
          }
          else
#endif //* SQLITE_OMIT_BTREECOUNT */
          {

            /* Check if the query is of one of the following forms:
            **
            **   SELECT min(x) FROM ...
            **   SELECT max(x) FROM ...
            **
            ** If it is, then ask the code in where.c to attempt to sort results
            ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
            ** If where.c is able to produce results sorted in this order, then
            ** add vdbe code to break out of the processing loop after the
            ** first iteration (since the first iteration of the loop is
            ** guaranteed to operate on the row with the minimum or maximum
            ** value of x, the only row required).
            **
            ** A special flag must be passed to sqlite3WhereBegin() to slightly
            ** modify behavior as follows:
            **
            **   + If the query is a "SELECT min(x)", then the loop coded by
            **     where.c should not iterate over any values with a NULL value
            **     for x.
            **
            **   + The optimizer code in where.c (the thing that decides which
            **     index or indices to use) should place a different priority on
            **     satisfying the 'ORDER BY' clause than it does in other cases.
            **     Refer to code and comments in where.c for details.
            */
            ExprList pMinMax = null;
            int flag = minMaxQuery( p );
            if ( flag != 0 )
            {
              Debug.Assert( !ExprHasProperty( p.pEList.a[0].pExpr, EP_xIsSelect ) );
              pMinMax = sqlite3ExprListDup( db, p.pEList.a[0].pExpr.x.pList, 0 );
              pDel = pMinMax;
              if ( pMinMax != null )///* && 0 == db.mallocFailed */ )
              {
                pMinMax.a[0].sortOrder = (u8)( flag != WHERE_ORDERBY_MIN ? 1 : 0 );
                pMinMax.a[0].pExpr.op = TK_COLUMN;
              }
            }

            /* This case runs if the aggregate has no GROUP BY clause.  The
            ** processing is much simpler since there is only a single row
            ** of output.
            */
            resetAccumulator( pParse, sAggInfo );
            pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pMinMax, (byte)flag );
            if ( pWInfo == null )
            {
              sqlite3ExprListDelete( db, ref pDel );
              goto select_end;
            }
            updateAccumulator( pParse, sAggInfo );
            if ( pMinMax == null && flag != 0 )
            {
              sqlite3VdbeAddOp2( v, OP_Goto, 0, pWInfo.iBreak );
#if SQLITE_DEBUG
              VdbeComment( v, "%s() by index",
              ( flag == WHERE_ORDERBY_MIN ? "min" : "max" ) );
#endif
            }
            sqlite3WhereEnd( pWInfo );
            finalizeAggFunctions( pParse, sAggInfo );
          }

          pOrderBy = null;
          sqlite3ExprIfFalse( pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL );
          selectInnerLoop( pParse, p, p.pEList, 0, 0, null, -1,
          pDest, addrEnd, addrEnd );

          sqlite3ExprListDelete( db, ref pDel );
        }
        sqlite3VdbeResolveLabel( v, addrEnd );

      } /* endif aggregate query */

      if ( distinct >= 0 )
      {
        explainTempTable( pParse, "DISTINCT" );
      }

      /* If there is an ORDER BY clause, then we need to sort the results
      ** and send them to the callback one by one.
      */
      if ( pOrderBy != null )
      {
        explainTempTable( pParse, "ORDER BY" );
        generateSortTail( pParse, p, v, pEList.nExpr, pDest );
      }

      /* Jump here to skip this query
      */
      sqlite3VdbeResolveLabel( v, iEnd );

      /* The SELECT was successfully coded.   Set the return code to 0
      ** to indicate no errors.
      */
      rc = 0;

    /* Control jumps to here if an error is encountered above, or upon
    ** successful coding of the SELECT.
    */
select_end:
      explainSetInteger( ref pParse.iSelectId, iRestoreSelectId );

      /* Identify column names if results of the SELECT are to be output.
      */
      if ( rc == SQLITE_OK && pDest.eDest == SRT_Output )
      {
        generateColumnNames( pParse, pTabList, pEList );
      }

      sqlite3DbFree( db, ref sAggInfo.aCol );
      sqlite3DbFree( db, ref sAggInfo.aFunc );
      return rc;
    }
Ejemplo n.º 37
0
    /*
    ** Given an expression list, generate a KeyInfo structure that records
    ** the collating sequence for each expression in that expression list.
    **
    ** If the ExprList is an ORDER BY or GROUP BY clause then the resulting
    ** KeyInfo structure is appropriate for initializing a virtual index to
    ** implement that clause.  If the ExprList is the result set of a SELECT
    ** then the KeyInfo structure is appropriate for initializing a virtual
    ** index to implement a DISTINCT test.
    **
    ** Space to hold the KeyInfo structure is obtain from malloc.  The calling
    ** function is responsible for seeing that this structure is eventually
    ** freed.  Add the KeyInfo structure to the P4 field of an opcode using
    ** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
    */
    static KeyInfo keyInfoFromExprList( Parse pParse, ExprList pList )
    {
      sqlite3 db = pParse.db;
      int nExpr;
      KeyInfo pInfo;
      ExprList_item pItem;
      int i;

      nExpr = pList.nExpr;
      pInfo = new KeyInfo();//sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(CollSeq*.Length+1) );
      if ( pInfo != null )
      {
        pInfo.aSortOrder = new byte[nExpr];// pInfo.aColl[nExpr];
        pInfo.aColl = new CollSeq[nExpr];
        pInfo.nField = (u16)nExpr;
        pInfo.enc = db.aDbStatic[0].pSchema.enc;// ENC(db);
        pInfo.db = db;
        for ( i = 0; i < nExpr; i++ )
        {//, pItem++){
          pItem = pList.a[i];
          CollSeq pColl;
          pColl = sqlite3ExprCollSeq( pParse, pItem.pExpr );
          if ( pColl == null )
          {
            pColl = db.pDfltColl;
          }
          pInfo.aColl[i] = pColl;
          pInfo.aSortOrder[i] = (byte)pItem.sortOrder;
        }
      }
      return pInfo;
    }
Ejemplo n.º 38
0
 void sqlite3PrintExprList( ExprList pList )
 {
   int i;
   for ( i = 0; i < pList.nExpr; i++ )
   {
     sqlite3PrintExpr( pList.a[i].pExpr );
     if ( i < pList.nExpr - 1 )
     {
       sqlite3DebugPrintf( ", " );
     }
   }
 }
Ejemplo n.º 39
0
    /*
    ** Triggers may access values stored in the old.* or new.* pseudo-table. 
    ** This function returns a 32-bit bitmask indicating which columns of the 
    ** old.* or new.* tables actually are used by triggers. This information 
    ** may be used by the caller, for example, to avoid having to load the entire
    ** old.* record into memory when executing an UPDATE or DELETE command.
    **
    ** Bit 0 of the returned mask is set if the left-most column of the
    ** table may be accessed using an [old|new].<col> reference. Bit 1 is set if
    ** the second leftmost column value is required, and so on. If there
    ** are more than 32 columns in the table, and at least one of the columns
    ** with an index greater than 32 may be accessed, 0xffffffff is returned.
    **
    ** It is not possible to determine if the old.rowid or new.rowid column is 
    ** accessed by triggers. The caller must always assume that it is.
    **
    ** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned
    ** applies to the old.* table. If 1, the new.* table.
    **
    ** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE
    ** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only
    ** included in the returned mask if the TRIGGER_BEFORE bit is set in the
    ** tr_tm parameter. Similarly, values accessed by AFTER triggers are only
    ** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm.
    */
    static u32 sqlite3TriggerColmask(
      Parse pParse,        /* Parse context */
      Trigger pTrigger,    /* List of triggers on table pTab */
      ExprList pChanges,   /* Changes list for any UPDATE OF triggers */
      int isNew,           /* 1 for new.* ref mask, 0 for old.* ref mask */
      int tr_tm,           /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
      Table pTab,          /* The table to code triggers from */
      int orconf           /* Default ON CONFLICT policy for trigger steps */
    )
    {
      int op = pChanges != null ? TK_UPDATE : TK_DELETE;
      u32 mask = 0;
      Trigger p;

      Debug.Assert( isNew == 1 || isNew == 0 );
      for ( p = pTrigger; p != null; p = p.pNext )
      {
        if ( p.op == op && ( tr_tm & p.tr_tm ) != 0
         && checkColumnOverlap( p.pColumns, pChanges ) != 0
        )
        {
          TriggerPrg pPrg;
          pPrg = getRowTrigger( pParse, p, pTab, orconf );
          if ( pPrg != null )
          {
            mask |= pPrg.aColmask[isNew];
          }
        }
      }

      return mask;
    }
Ejemplo n.º 40
0
 static TriggerStep sqlite3TriggerInsertStep(sqlite3 db, Token pTableName, IdList pColumn, ExprList pEList, int null_5, u8 orconf)
 {
     return(sqlite3TriggerInsertStep(db, pTableName, pColumn, pEList, null, orconf));
 }
Ejemplo n.º 41
0
        static WRC ResolveExprStep(Walker walker, Expr expr)
        {
            NameContext nc = walker.u.NC;

            Debug.Assert(nc != null);
            Parse parse = nc.Parse;

            Debug.Assert(parse == walker.Parse);

            if (E.ExprHasAnyProperty(expr, EP.Resolved))
            {
                return(WRC.Prune);
            }
            E.ExprSetProperty(expr, EP.Resolved);
#if !NDEBUG
            if (nc.SrcList != null && nc.SrcList.Allocs > 0)
            {
                SrcList srcList = nc.SrcList;
                for (int i = 0; i < nc.SrcList.Srcs; i++)
                {
                    Debug.Assert(srcList.Ids[i].Cursor >= 0 && srcList.Ids[i].Cursor < parse.Tabs);
                }
            }
#endif
            switch (expr.OP)
            {
#if ENABLE_UPDATE_DELETE_LIMIT && !OMIT_SUBQUERY
            // The special operator TK_ROW means use the rowid for the first column in the FROM clause.  This is used by the LIMIT and ORDER BY
            // clause processing on UPDATE and DELETE statements.
            case TK.ROW:
            {
                SrcList srcList = nc.SrcList;
                Debug.Assert(srcList != null && srcList.Srcs == 1);
                SrcList.SrcListItem item = srcList.Ids[0];
                expr.OP       = TK.COLUMN;
                expr.Table    = item.Table;
                expr.TableId  = item.Cursor;
                expr.ColumnId = -1;
                expr.Aff      = AFF.INTEGER;
                break;
            }
#endif

            case TK.ID:      // A lone identifier is the name of a column.
            {
                return(LookupName(parse, null, null, expr.u.Token, nc, expr));
            }

            case TK.DOT:     // A table name and column name: ID.ID Or a database, table and column: ID.ID.ID
            {
                string columnName;
                string tableName;
                string dbName;
                // if (srcList == nullptr) break;
                Expr right = expr.Right;
                if (right.OP == TK.ID)
                {
                    dbName     = null;
                    tableName  = expr.Left.u.Token;
                    columnName = right.u.Token;
                }
                else
                {
                    Debug.Assert(right.OP == TK.DOT);
                    dbName     = expr.Left.u.Token;
                    tableName  = right.Left.u.Token;
                    columnName = right.Right.u.Token;
                }
                return(LookupName(parse, dbName, tableName, columnName, nc, expr));
            }

            case TK.CONST_FUNC:
            case TK.FUNCTION:                                              // Resolve function names
            {
                ExprList   list         = expr.x.List;                     // The argument list
                int        n            = (list != null ? list.Exprs : 0); // Number of arguments
                bool       noSuchFunc   = false;                           // True if no such function exists
                bool       wrongNumArgs = false;                           // True if wrong number of arguments
                bool       isAgg        = false;                           // True if is an aggregate function
                TEXTENCODE encode       = E.CTXENCODE(parse.Ctx);          // The database encoding

                C.ASSERTCOVERAGE(expr.OP == TK.CONST_FUNC);
                Debug.Assert(!E.ExprHasProperty(expr, EP.xIsSelect));
                string  id       = expr.u.Token;                                                     // The function name.
                int     idLength = id.Length;                                                        // Number of characters in function name
                FuncDef def      = Callback.FindFunction(parse.Ctx, id, idLength, n, encode, false); // Information about the function
                if (def == null)
                {
                    def = Callback.FindFunction(parse.Ctx, id, idLength, -2, encode, false);
                    if (def == null)
                    {
                        noSuchFunc = true;
                    }
                    else
                    {
                        wrongNumArgs = true;
                    }
                }
                else
                {
                    isAgg = (def.Func == null);
                }
#if !OMIT_AUTHORIZATION
                if (def != null)
                {
                    ARC auth = Auth.Check(parse, AUTH.FUNCTION, null, def.Name, null);         // Authorization to use the function
                    if (auth != ARC.OK)
                    {
                        if (auth == ARC.DENY)
                        {
                            parse.ErrorMsg("not authorized to use function: %s", def.Name);
                            nc.Errs++;
                        }
                        expr.OP = TK.NULL;
                        return(WRC.Prune);
                    }
                }
#endif
                if (isAgg && (nc.NCFlags & NC.AllowAgg) == 0)
                {
                    parse.ErrorMsg("misuse of aggregate function %.*s()", idLength, id);
                    nc.Errs++;
                    isAgg = false;
                }
                else if (noSuchFunc && !ctx.Init.Busy)
                {
                    parse.ErrorMsg("no such function: %.*s", idLength, id);
                    nc.Errs++;
                }
                else if (wrongNumArgs)
                {
                    parse.ErrorMsg("wrong number of arguments to function %.*s()", idLength, id);
                    nc.Errs++;
                }
                if (isAgg)
                {
                    nc.NCFlags &= ~NC.AllowAgg;
                }
                walker.WalkExprList(list);
                if (isAgg)
                {
                    NameContext nc2 = nc;
                    expr.OP  = TK.AGG_FUNCTION;
                    expr.OP2 = 0;
                    while (nc2 != null && !expr.FunctionUsesThisSrc(nc2.SrcList))
                    {
                        expr.OP2++;
                        nc2 = nc2.Next;
                    }
                    if (nc2 != null)
                    {
                        nc2.NCFlags |= NC.HasAgg;
                    }
                    nc.NCFlags |= NC.AllowAgg;
                }
                // FIX ME:  Compute pExpr->affinity based on the expected return type of the function
                return(WRC.Prune);
            }

#if !OMIT_SUBQUERY
            case TK.SELECT:
            case TK.EXISTS:
            {
                C.ASSERTCOVERAGE(expr.OP == TK.EXISTS);
                goto case TK.IN;
            }
#endif
            case TK.IN:
            {
                C.ASSERTCOVERAGE(expr.OP == TK.IN);
                if (E.ExprHasProperty(expr, EP.xIsSelect))
                {
                    int refs = nc.Refs;
#if !OMIT_CHECK
                    if ((nc.NCFlags & NC.IsCheck) != 0)
                    {
                        parse.ErrorMsg("subqueries prohibited in CHECK constraints");
                    }
#endif
                    walker.WalkSelect(expr.x.Select);
                    Debug.Assert(nc.Refs >= refs);
                    if (refs != nc.Refs)
                    {
                        E.ExprSetProperty(expr, EP.VarSelect);
                    }
                }
                break;
            }

#if !OMIT_CHECK
            case TK.VARIABLE:
            {
                if ((nc.NCFlags & NC.IsCheck) != 0)
                {
                    parse.ErrorMsg("parameters prohibited in CHECK constraints");
                }

                break;
            }
#endif
            }
            return(parse.Errs != 0 || parse.Ctx.MallocFailed ? WRC.Abort : WRC.Continue);
        }
Ejemplo n.º 42
0
    /*
    ** This is called to code the required FOR EACH ROW triggers for an operation
    ** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
    ** is given by the op paramater. The tr_tm parameter determines whether the
    ** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
    ** parameter pChanges is passed the list of columns being modified.
    **
    ** If there are no triggers that fire at the specified time for the specified
    ** operation on pTab, this function is a no-op.
    **
    ** The reg argument is the address of the first in an array of registers 
    ** that contain the values substituted for the new.* and old.* references
    ** in the trigger program. If N is the number of columns in table pTab
    ** (a copy of pTab.nCol), then registers are populated as follows:
    **
    **   Register       Contains
    **   ------------------------------------------------------
    **   reg+0          OLD.rowid
    **   reg+1          OLD.* value of left-most column of pTab
    **   ...            ...
    **   reg+N          OLD.* value of right-most column of pTab
    **   reg+N+1        NEW.rowid
    **   reg+N+2        OLD.* value of left-most column of pTab
    **   ...            ...
    **   reg+N+N+1      NEW.* value of right-most column of pTab
    **
    ** For ON DELETE triggers, the registers containing the NEW.* values will
    ** never be accessed by the trigger program, so they are not allocated or 
    ** populated by the caller (there is no data to populate them with anyway). 
    ** Similarly, for ON INSERT triggers the values stored in the OLD.* registers
    ** are never accessed, and so are not allocated by the caller. So, for an
    ** ON INSERT trigger, the value passed to this function as parameter reg
    ** is not a readable register, although registers (reg+N) through 
    ** (reg+N+N+1) are.
    **
    ** Parameter orconf is the default conflict resolution algorithm for the
    ** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump
    ** is the instruction that control should jump to if a trigger program
    ** raises an IGNORE exception.
    */
    static void sqlite3CodeRowTrigger(
    Parse pParse,        /* Parse context */
    Trigger pTrigger,    /* List of triggers on table pTab */
    int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
    ExprList pChanges,   /* Changes list for any UPDATE OF triggers */
    int tr_tm,           /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
    Table pTab,          /* The table to code triggers from */
    int reg,             /* The first in an array of registers (see above) */
    int orconf,          /* ON CONFLICT policy */
    int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
    )
    {
      Trigger p;         /* Used to iterate through pTrigger list */

      Debug.Assert( op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE );
      Debug.Assert( tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );
      Debug.Assert( ( op == TK_UPDATE ) == ( pChanges != null ) );

      for ( p = pTrigger; p != null; p = p.pNext )
      {

        /* Sanity checking:  The schema for the trigger and for the table are
        ** always defined.  The trigger must be in the same schema as the table
        ** or else it must be a TEMP trigger. */
        Debug.Assert( p.pSchema != null );
        Debug.Assert( p.pTabSchema != null );
        Debug.Assert( p.pSchema == p.pTabSchema
             || p.pSchema == pParse.db.aDb[1].pSchema );

        /* Determine whether we should code this trigger */
        if ( p.op == op
         && p.tr_tm == tr_tm
         && checkColumnOverlap( p.pColumns, pChanges ) != 0
        )
        {
          sqlite3CodeRowTriggerDirect( pParse, p, pTab, reg, orconf, ignoreJump );
        }
      }
    }
Ejemplo n.º 43
0
    /*
    ** Process an UPDATE statement.
    **
    **   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
    **          \_______/ \________/     \______/       \________________/
    *            onError   pTabList      pChanges             pWhere
    */
    static void sqlite3Update(
    Parse pParse,         /* The parser context */
    SrcList pTabList,     /* The table in which we should change things */
    ExprList pChanges,    /* Things to be changed */
    Expr pWhere,          /* The WHERE clause.  May be null */
    int onError           /* How to handle constraint errors */
    )
    {
      int i, j;                   /* Loop counters */
      Table pTab;                 /* The table to be updated */
      int addr = 0;               /* VDBE instruction address of the start of the loop */
      WhereInfo pWInfo;           /* Information about the WHERE clause */
      Vdbe v;                     /* The virtual database engine */
      Index pIdx;                 /* For looping over indices */
      int nIdx;                   /* Number of indices that need updating */
      int iCur;                   /* VDBE Cursor number of pTab */
      sqlite3 db;                 /* The database structure */
      int[] aRegIdx = null;       /* One register assigned to each index to be updated */
      int[] aXRef = null;         /* aXRef[i] is the index in pChanges.a[] of the
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
      bool chngRowid;             /* True if the record number is being changed */
      Expr pRowidExpr = null;     /* Expression defining the new record number */
      bool openAll = false;       /* True if all indices need to be opened */
      AuthContext sContext;       /* The authorization context */
      NameContext sNC;            /* The name-context to resolve expressions in */
      int iDb;                    /* Database containing the table being updated */
      int j1;                     /* Addresses of jump instructions */
      u8 okOnePass;               /* True for one-pass algorithm without the FIFO */

#if !SQLITE_OMIT_TRIGGER
      bool isView = false;         /* Trying to update a view */
      Trigger pTrigger;            /* List of triggers on pTab, if required */
#endif
      int iBeginAfterTrigger = 0;  /* Address of after trigger program */
      int iEndAfterTrigger = 0;    /* Exit of after trigger program */
      int iBeginBeforeTrigger = 0; /* Address of before trigger program */
      int iEndBeforeTrigger = 0;   /* Exit of before trigger program */
      u32 old_col_mask = 0;        /* Mask of OLD.* columns in use */
      u32 new_col_mask = 0;        /* Mask of NEW.* columns in use */

      int newIdx = -1;             /* index of trigger "new" temp table       */
      int oldIdx = -1;             /* index of trigger "old" temp table       */

      /* Register Allocations */
      int regRowCount = 0;         /* A count of rows changed */
      int regOldRowid;             /* The old rowid */
      int regNewRowid;             /* The new rowid */
      int regData;                 /* New data for the row */
      int regRowSet = 0;           /* Rowset of rows to be updated */

      sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) );
      db = pParse.db;
      if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
      {
        goto update_cleanup;
      }
      Debug.Assert( pTabList.nSrc == 1 );

      /* Locate the table which we want to update.
      */
      pTab = sqlite3SrcListLookup( pParse, pTabList );
      if ( pTab == null ) goto update_cleanup;
      iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );

      /* Figure out if we have any triggers and if the table being
      ** updated is a view
      */
#if !SQLITE_OMIT_TRIGGER
      int iDummy = 0;
      pTrigger = sqlite3TriggersExist( pParse, pTab, TK_UPDATE, pChanges, ref iDummy );
      isView = pTab.pSelect != null;
#else
const Trigger pTrigger = null;
#if !SQLITE_OMIT_VIEW
const bool isView = false;
#endif
#endif
#if SQLITE_OMIT_VIEW
//    # undef isView
const bool isView = false;
#endif

      if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
      {
        goto update_cleanup;
      }
      if ( sqlite3IsReadOnly( pParse, pTab, ( pTrigger != null ? 1 : 0 ) ) )
      {
        goto update_cleanup;
      }
      aXRef = new int[pTab.nCol];// sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol);
      //if ( aXRef == null ) goto update_cleanup;
      for ( i = 0 ; i < pTab.nCol ; i++ ) aXRef[i] = -1;

      /* If there are FOR EACH ROW triggers, allocate cursors for the
      ** special OLD and NEW tables
      */
      if ( pTrigger != null )
      {
        newIdx = pParse.nTab++;
        oldIdx = pParse.nTab++;
      }

      /* Allocate a cursors for the main database table and for all indices.
      ** The index cursors might not be used, but if they are used they
      ** need to occur right after the database cursor.  So go ahead and
      ** allocate enough space, just in case.
      */
      pTabList.a[0].iCursor = iCur = pParse.nTab++;
      for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
      {
        pParse.nTab++;
      }

      /* Initialize the name-context */
      sNC = new NameContext();// memset(&sNC, 0, sNC).Length;
      sNC.pParse = pParse;
      sNC.pSrcList = pTabList;

      /* Resolve the column names in all the expressions of the
      ** of the UPDATE statement.  Also find the column index
      ** for each column to be updated in the pChanges array.  For each
      ** column to be updated, make sure we have authorization to change
      ** that column.
      */
      chngRowid = false;
      for ( i = 0 ; i < pChanges.nExpr ; i++ )
      {
        if ( sqlite3ResolveExprNames( sNC, ref pChanges.a[i].pExpr ) != 0 )
        {
          goto update_cleanup;
        }
        for ( j = 0 ; j < pTab.nCol ; j++ )
        {
          if ( sqlite3StrICmp( pTab.aCol[j].zName, pChanges.a[i].zName ) == 0 )
          {
            if ( j == pTab.iPKey )
            {
              chngRowid = true;
              pRowidExpr = pChanges.a[i].pExpr;
            }
            aXRef[j] = i;
            break;
          }
        }
        if ( j >= pTab.nCol )
        {
          if ( sqlite3IsRowid( pChanges.a[i].zName ) )
          {
            chngRowid = true;
            pRowidExpr = pChanges.a[i].pExpr;
          }
          else
          {
            sqlite3ErrorMsg( pParse, "no such column: %s", pChanges.a[i].zName );
            goto update_cleanup;
          }
        }
#if !SQLITE_OMIT_AUTHORIZATION
{
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName,
pTab.aCol[j].zName, db.aDb[iDb].zName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){
aXRef[j] = -1;
}
}
#endif
      }

      /* Allocate memory for the array aRegIdx[].  There is one entry in the
      ** array for each index associated with table being updated.  Fill in
      ** the value with a register number for indices that are to be used
      ** and with zero for unused indices.
      */
      for ( nIdx = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, nIdx++ ) { }
      if ( nIdx > 0 )
      {
        aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx);
        if ( aRegIdx == null ) goto update_cleanup;
      }
      for ( j = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, j++ )
      {
        int reg;
        if ( chngRowid )
        {
          reg = ++pParse.nMem;
        }
        else
        {
          reg = 0;
          for ( i = 0 ; i < pIdx.nColumn ; i++ )
          {
            if ( aXRef[pIdx.aiColumn[i]] >= 0 )
            {
              reg = ++pParse.nMem;
              break;
            }
          }
        }
        aRegIdx[j] = reg;
      }

      /* Allocate a block of register used to store the change record
      ** sent to sqlite3GenerateConstraintChecks().  There are either
      ** one or two registers for holding the rowid.  One rowid register
      ** is used if chngRowid is false and two are used if chngRowid is
      ** true.  Following these are pTab.nCol register holding column
      ** data.
      */
      regOldRowid = regNewRowid = pParse.nMem + 1;
      pParse.nMem += pTab.nCol + 1;
      if ( chngRowid )
      {
        regNewRowid++;
        pParse.nMem++;
      }
      regData = regNewRowid + 1;


      /* Begin generating code.
      */
      v = sqlite3GetVdbe( pParse );
      if ( v == null ) goto update_cleanup;
      if ( pParse.nested == 0 ) sqlite3VdbeCountChanges( v );
      sqlite3BeginWriteOperation( pParse, 1, iDb );

#if !SQLITE_OMIT_VIRTUALTABLE
/* Virtual tables must be handled separately */
if ( IsVirtual( pTab ) )
{
updateVirtualTable( pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere );
pWhere = null;
pTabList = null;
goto update_cleanup;
}
#endif

      /* Start the view context
*/
#if !SQLITE_OMIT_AUTHORIZATION
if( isView ){
sqlite3AuthContextPush(pParse, sContext, pTab.zName);
}
#endif
      /* Generate the code for triggers.
*/
      if ( pTrigger != null )
      {
        int iGoto;

        /* Create pseudo-tables for NEW and OLD
        */
        sqlite3VdbeAddOp3( v, OP_OpenPseudo, oldIdx, 0, pTab.nCol );
        sqlite3VdbeAddOp3( v, OP_OpenPseudo, newIdx, 0, pTab.nCol );

        iGoto = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 );
        addr = sqlite3VdbeMakeLabel( v );
        iBeginBeforeTrigger = sqlite3VdbeCurrentAddr( v );
        if ( sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges,
        TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
        ref old_col_mask, ref new_col_mask ) != 0 )
        {
          goto update_cleanup;
        }
        iEndBeforeTrigger = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 );
        iBeginAfterTrigger = sqlite3VdbeCurrentAddr( v );
#if !SQLITE_OMIT_TRIGGER
        if ( sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab,
        newIdx, oldIdx, onError, addr, ref old_col_mask, ref new_col_mask ) != 0 )
        {
          goto update_cleanup;
        }
#endif
        iEndAfterTrigger = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 );
        sqlite3VdbeJumpHere( v, iGoto );
      }

      /* If we are trying to update a view, realize that view into
      ** a ephemeral table.
      */
#if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER)
      if ( isView )
      {
        sqlite3MaterializeView( pParse, pTab, pWhere, iCur );
      }
#endif

      /* Resolve the column names in all the expressions in the
** WHERE clause.
*/
      if ( sqlite3ResolveExprNames( sNC, ref pWhere ) != 0 )
      {
        goto update_cleanup;
      }

      /* Begin the database scan
      */
      sqlite3VdbeAddOp2( v, OP_Null, 0, regOldRowid );
      ExprList NullOrderby = null;
      pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED );
      if ( pWInfo == null ) goto update_cleanup;
      okOnePass = pWInfo.okOnePass;

      /* Remember the rowid of every item to be updated.
      */
      sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regOldRowid );
      if ( 0 == okOnePass )
      {
        regRowSet = ++pParse.nMem;
        sqlite3VdbeAddOp2( v, OP_RowSetAdd, regRowSet, regOldRowid );
      }

      /* End the database scan loop.
      */
      sqlite3WhereEnd( pWInfo );

      /* Initialize the count of updated rows
      */
      if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null )
      {
        regRowCount = ++pParse.nMem;
        sqlite3VdbeAddOp2( v, OP_Integer, 0, regRowCount );
      }

      if ( !isView )
      {
        /*
        ** Open every index that needs updating.  Note that if any
        ** index could potentially invoke a REPLACE conflict resolution
        ** action, then we need to open all indices because we might need
        ** to be deleting some records.
        */
        if ( 0 == okOnePass ) sqlite3OpenTable( pParse, iCur, iDb, pTab, OP_OpenWrite );
        if ( onError == OE_Replace )
        {
          openAll = true;
        }
        else
        {
          openAll = false;
          for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
          {
            if ( pIdx.onError == OE_Replace )
            {
              openAll = true;
              break;
            }
          }
        }
        for ( i = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, i++ )
        {
          if ( openAll || aRegIdx[i] > 0 )
          {
            KeyInfo pKey = sqlite3IndexKeyinfo( pParse, pIdx );
            sqlite3VdbeAddOp4( v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb,
            pKey, P4_KEYINFO_HANDOFF );
            Debug.Assert( pParse.nTab > iCur + i + 1 );
          }
        }
      }

      /* Jump back to this point if a trigger encounters an IGNORE constraint. */
      if ( pTrigger != null )
      {
        sqlite3VdbeResolveLabel( v, addr );
      }

      /* Top of the update loop */
      if ( okOnePass != 0 )
      {
        int a1 = sqlite3VdbeAddOp1( v, OP_NotNull, regOldRowid );
        addr = sqlite3VdbeAddOp0( v, OP_Goto );
        sqlite3VdbeJumpHere( v, a1 );
      }
      else
      {
        addr = sqlite3VdbeAddOp3( v, OP_RowSetRead, regRowSet, 0, regOldRowid );
      }

      if ( pTrigger != null )
      {
        int regRowid;
        int regRow;
        int regCols;

        /* Make cursor iCur point to the record that is being updated.
        */
        sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid );

        /* Generate the OLD table
        */
        regRowid = sqlite3GetTempReg( pParse );
        regRow = sqlite3GetTempReg( pParse );
        sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regRowid );
        if ( old_col_mask == 0 )
        {
          sqlite3VdbeAddOp2( v, OP_Null, 0, regRow );
        }
        else
        {
          sqlite3VdbeAddOp2( v, OP_RowData, iCur, regRow );
        }
        sqlite3VdbeAddOp3( v, OP_Insert, oldIdx, regRow, regRowid );

        /* Generate the NEW table
        */
        if ( chngRowid )
        {
          sqlite3ExprCodeAndCache( pParse, pRowidExpr, regRowid );
          sqlite3VdbeAddOp1( v, OP_MustBeInt, regRowid );
        }
        else
        {
          sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regRowid );
        }
        regCols = sqlite3GetTempRange( pParse, pTab.nCol );
        for ( i = 0 ; i < pTab.nCol ; i++ )
        {
          if ( i == pTab.iPKey )
          {
            sqlite3VdbeAddOp2( v, OP_Null, 0, regCols + i );
            continue;
          }
          j = aXRef[i];
          if ( ( i < 32 && ( new_col_mask & ( (u32)1 << i ) ) != 0 ) || new_col_mask == 0xffffffff )
          {
            if ( j < 0 )
            {
              sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regCols + i );
              sqlite3ColumnDefault( v, pTab, i, -1 );
            }
            else
            {
              sqlite3ExprCodeAndCache( pParse, pChanges.a[j].pExpr, regCols + i );
            }
          }
          else
          {
            sqlite3VdbeAddOp2( v, OP_Null, 0, regCols + i );
          }
        }
        sqlite3VdbeAddOp3( v, OP_MakeRecord, regCols, pTab.nCol, regRow );
        if ( !isView )
        {
          sqlite3TableAffinityStr( v, pTab );
          sqlite3ExprCacheAffinityChange( pParse, regCols, pTab.nCol );
        }
        sqlite3ReleaseTempRange( pParse, regCols, pTab.nCol );
        /* if( pParse.nErr ) goto update_cleanup; */
        sqlite3VdbeAddOp3( v, OP_Insert, newIdx, regRow, regRowid );
        sqlite3ReleaseTempReg( pParse, regRowid );
        sqlite3ReleaseTempReg( pParse, regRow );

        sqlite3VdbeAddOp2( v, OP_Goto, 0, iBeginBeforeTrigger );
        sqlite3VdbeJumpHere( v, iEndBeforeTrigger );
      }

      if ( !isView )
      {

        /* Loop over every record that needs updating.  We have to load
        ** the old data for each record to be updated because some columns
        ** might not change and we will need to copy the old value.
        ** Also, the old data is needed to delete the old index entries.
        ** So make the cursor point at the old record.
        */
        sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid );

        /* If the record number will change, push the record number as it
        ** will be after the update. (The old record number is currently
        ** on top of the stack.)
        */
        if ( chngRowid )
        {
          sqlite3ExprCode( pParse, pRowidExpr, regNewRowid );
          sqlite3VdbeAddOp1( v, OP_MustBeInt, regNewRowid );
        }

        /* Compute new data for this record.
        */
        for ( i = 0 ; i < pTab.nCol ; i++ )
        {
          if ( i == pTab.iPKey )
          {
            sqlite3VdbeAddOp2( v, OP_Null, 0, regData + i );
            continue;
          }
          j = aXRef[i];
          if ( j < 0 )
          {
            sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regData + i );
            sqlite3ColumnDefault( v, pTab, i, regData + i );
          }
          else
          {
            sqlite3ExprCode( pParse, pChanges.a[j].pExpr, regData + i );
          }
        }

        /* Do constraint checks
        */
        iDummy = 0;
        sqlite3GenerateConstraintChecks( pParse, pTab, iCur, regNewRowid,
           aRegIdx, chngRowid, true,
           onError, addr, ref iDummy );

        /* Delete the old indices for the current record.
        */
        j1 = sqlite3VdbeAddOp3( v, OP_NotExists, iCur, 0, regOldRowid );
        sqlite3GenerateRowIndexDelete( pParse, pTab, iCur, aRegIdx );

        /* If changing the record number, delete the old record.
        */
        if ( chngRowid )
        {
          sqlite3VdbeAddOp2( v, OP_Delete, iCur, 0 );
        }
        sqlite3VdbeJumpHere( v, j1 );

        /* Create the new index entries and the new record.
        */
        sqlite3CompleteInsertion( pParse, pTab, iCur, regNewRowid,
        aRegIdx, true, -1, false, false );
      }

      /* Increment the row counter
      */
      if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null )
      {
        sqlite3VdbeAddOp2( v, OP_AddImm, regRowCount, 1 );
      }

      /* If there are triggers, close all the cursors after each iteration
      ** through the loop.  The fire the after triggers.
      */
      if ( pTrigger != null )
      {
        sqlite3VdbeAddOp2( v, OP_Goto, 0, iBeginAfterTrigger );
        sqlite3VdbeJumpHere( v, iEndAfterTrigger );
      }

      /* Repeat the above with the next record to be updated, until
      ** all record selected by the WHERE clause have been updated.
      */
      sqlite3VdbeAddOp2( v, OP_Goto, 0, addr );
      sqlite3VdbeJumpHere( v, addr );

      /* Close all tables */
      for ( i = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, i++ )
      {
        if ( openAll || aRegIdx[i] > 0 )
        {
          sqlite3VdbeAddOp2( v, OP_Close, iCur + i + 1, 0 );
        }
      }
      sqlite3VdbeAddOp2( v, OP_Close, iCur, 0 );
      if ( pTrigger != null )
      {
        sqlite3VdbeAddOp2( v, OP_Close, newIdx, 0 );
        sqlite3VdbeAddOp2( v, OP_Close, oldIdx, 0 );
      }

      /* Update the sqlite_sequence table by storing the content of the
      ** maximum rowid counter values recorded while inserting into
      ** autoincrement tables.
      */
      if ( pParse.nested == 0 && pParse.trigStack == null )
      {
        sqlite3AutoincrementEnd( pParse );
      }

      /*
      ** Return the number of rows that were changed. If this routine is
      ** generating code because of a call to sqlite3NestedParse(), do not
      ** invoke the callback function.
      */
      if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null && pParse.nested == 0 )
      {
        sqlite3VdbeAddOp2( v, OP_ResultRow, regRowCount, 1 );
        sqlite3VdbeSetNumCols( v, 1 );
        sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC );
      }

update_cleanup:
#if !SQLITE_OMIT_AUTHORIZATION
sqlite3AuthContextPop(sContext);
#endif
      //sqlite3DbFree( db, ref  aRegIdx );
      //sqlite3DbFree( db, ref  aXRef );
      sqlite3SrcListDelete( db, ref pTabList );
      sqlite3ExprListDelete( db, ref pChanges );
      sqlite3ExprDelete( db, ref pWhere );
      return;
    }
Ejemplo n.º 44
0
 static TriggerStep sqlite3TriggerInsertStep( sqlite3 db, Token pTableName, IdList pColumn, ExprList pEList, int null_5, u8 orconf )
 {
   return sqlite3TriggerInsertStep( db, pTableName, pColumn, pEList, null, orconf );
 }
Ejemplo n.º 45
0
        public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, Select select, OE orconf)
        {
            Debug.Assert(list == null || select == null);
            Debug.Assert(list != null || select != null || ctx.MallocFailed);
            TriggerStep triggerStep = TriggerStepAllocate(ctx, TK.INSERT, tableName);

            if (triggerStep != null)
            {
                triggerStep.Select   = Select.Dup(ctx, select, E.EXPRDUP_REDUCE);
                triggerStep.IdList   = column;
                triggerStep.ExprList = Expr.ListDup(ctx, list, E.EXPRDUP_REDUCE);
                triggerStep.Orconf   = orconf;
            }
            else
            {
                Expr.IdListDelete(ctx, ref column);
            }
            Expr.ListDelete(ctx, ref list);
            Select.Delete(ctx, ref select);
            return(triggerStep);
        }
Ejemplo n.º 46
0
    /*
    ** Construct a trigger step that implements an UPDATE statement and return
    ** a pointer to that trigger step.  The parser calls this routine when it
    ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
    */
    static TriggerStep sqlite3TriggerUpdateStep(
    sqlite3 db,         /* The database connection */
    Token pTableName,   /* Name of the table to be updated */
    ExprList pEList,    /* The SET clause: list of column and new values */
    Expr pWhere,        /* The WHERE clause */
    u8 orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
    )
    {
      TriggerStep pTriggerStep;

      pTriggerStep = triggerStepAllocate( db, TK_UPDATE, pTableName );
      //if ( pTriggerStep != null )
      //{
      pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE );
      pTriggerStep.pWhere = sqlite3ExprDup( db, pWhere, EXPRDUP_REDUCE );
      pTriggerStep.orconf = orconf;
      //}
      sqlite3ExprListDelete( db, ref pEList );
      sqlite3ExprDelete( db, ref pWhere );
      return pTriggerStep;
    }
Ejemplo n.º 47
0
        static void GenerateColumnTypes(Parse parse, SrcList tabList, ExprList list)
        {
#if !OMIT_DECLTYPE
            Vdbe v = parse.V;
            NameContext sNC = new NameContext();
            sNC.SrcList = tabList;
            sNC.Parse = parse;
            for (int i = 0; i < list.Exprs; i++)
            {
                Expr p = list.Ids[i].Expr;
                string typeName;
#if ENABLE_COLUMN_METADATA
                string origDbName = null;
                string origTableName = null;
                string origColumnName = null;
                typeName = ColumnType(sNC, p, ref origDbName, ref origTableName, ref origColumnName);

                // The vdbe must make its own copy of the column-type and other column specific strings, in case the schema is reset before this
                // virtual machine is deleted.
                v.SetColName(i, COLNAME_DATABASE, origDbName, C.DESTRUCTOR_TRANSIENT);
                v.SetColName(i, COLNAME_TABLE, origTableName, C.DESTRUCTOR_TRANSIENT);
                v.SetColName(i, COLNAME_COLUMN, origColumnName, C.DESTRUCTOR_TRANSIENT);
#else
                string dummy1 = null;
                typeName = ColumnType(sNC, p, ref dummy1, ref dummy1, ref dummy1);
#endif
                v.SetColName(i, COLNAME_DECLTYPE, typeName, C.DESTRUCTOR_TRANSIENT);
            }
#endif
        }
Ejemplo n.º 48
0
		/*
		** Process an UPDATE statement.
		**
		**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
		**          \_______/ \________/     \______/       \________________/
		*            onError   pTabList      pChanges             pWhere
		*/
		static void sqlite3Update(
		Parse pParse,         /* The parser context */
		SrcList pTabList,     /* The table in which we should change things */
		ExprList pChanges,    /* Things to be changed */
		Expr pWhere,          /* The WHERE clause.  May be null */
		int onError           /* How to handle constraint errors */
		)
		{
			int i, j;                   /* Loop counters */
			Table pTab;                 /* The table to be updated */
			int addr = 0;               /* VDBE instruction address of the start of the loop */
			WhereInfo pWInfo;           /* Information about the WHERE clause */
			Vdbe v;                     /* The virtual database engine */
			Index pIdx;                 /* For looping over indices */
			int nIdx;                   /* Number of indices that need updating */
			int iCur;                   /* VDBE Cursor number of pTab */
			sqlite3 db;                 /* The database structure */
			int[] aRegIdx = null;       /* One register assigned to each index to be updated */
			int[] aXRef = null;         /* aXRef[i] is the index in pChanges.a[] of the
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
			bool chngRowid;             /* True if the record number is being changed */
			Expr pRowidExpr = null;     /* Expression defining the new record number */
			bool openAll = false;       /* True if all indices need to be opened */
			AuthContext sContext;       /* The authorization context */
			NameContext sNC;            /* The name-context to resolve expressions in */
			int iDb;                    /* Database containing the table being updated */
			bool okOnePass;             /* True for one-pass algorithm without the FIFO */
			bool hasFK;                 /* True if foreign key processing is required */

#if !SQLITE_OMIT_TRIGGER
			bool isView;            /* True when updating a view (INSTEAD OF trigger) */
			Trigger pTrigger;      /* List of triggers on pTab, if required */
			int tmask = 0;         /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
#endif
			int newmask;           /* Mask of NEW.* columns accessed by BEFORE triggers */

			/* Register Allocations */
			int regRowCount = 0;         /* A count of rows changed */
			int regOldRowid;             /* The old rowid */
			int regNewRowid;             /* The new rowid */
			int regNew;
			int regOld = 0;
			int regRowSet = 0;           /* Rowset of rows to be updated */

			sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) );
			db = pParse.db;
			if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */ )
			{
				goto update_cleanup;
			}
			Debug.Assert(pTabList.nSrc == 1);

			/* Locate the table which we want to update.
			*/
			pTab = sqlite3SrcListLookup(pParse, pTabList);
			if (pTab == null)
				goto update_cleanup;
			iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);

			/* Figure out if we have any triggers and if the table being
			** updated is a view.
			*/
#if !SQLITE_OMIT_TRIGGER
			pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, out tmask);
			isView = pTab.pSelect != null;
			Debug.Assert(pTrigger != null || tmask == 0);
#else
	  const Trigger pTrigger = null;//# define pTrigger 0
	  const int tmask = 0;          //# define tmask 0
#endif
#if SQLITE_OMIT_TRIGGER || SQLITE_OMIT_VIEW
//    # undef isView
	  const bool isView = false;    //# define isView 0
#endif

			if (sqlite3ViewGetColumnNames(pParse, pTab) != 0)
			{
				goto update_cleanup;
			}
			if (sqlite3IsReadOnly(pParse, pTab, tmask))
			{
				goto update_cleanup;
			}
			aXRef = new int[pTab.nCol];// sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol);
			//if ( aXRef == null ) goto update_cleanup;
			for (i = 0; i < pTab.nCol; i++)
				aXRef[i] = -1;

			/* Allocate a cursors for the main database table and for all indices.
			** The index cursors might not be used, but if they are used they
			** need to occur right after the database cursor.  So go ahead and
			** allocate enough space, just in case.
			*/
			pTabList.a[0].iCursor = iCur = pParse.nTab++;
			for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
			{
				pParse.nTab++;
			}

			/* Initialize the name-context */
			sNC = new NameContext();// memset(&sNC, 0, sNC).Length;
			sNC.pParse = pParse;
			sNC.pSrcList = pTabList;

			/* Resolve the column names in all the expressions of the
			** of the UPDATE statement.  Also find the column index
			** for each column to be updated in the pChanges array.  For each
			** column to be updated, make sure we have authorization to change
			** that column.
			*/
			chngRowid = false;
			for (i = 0; i < pChanges.nExpr; i++)
			{
				if (sqlite3ResolveExprNames(sNC, ref pChanges.a[i].pExpr) != 0)
				{
					goto update_cleanup;
				}
				for (j = 0; j < pTab.nCol; j++)
				{
					if (pTab.aCol[j].zName.Equals(pChanges.a[i].zName, StringComparison.InvariantCultureIgnoreCase))
					{
						if (j == pTab.iPKey)
						{
							chngRowid = true;
							pRowidExpr = pChanges.a[i].pExpr;
						}
						aXRef[j] = i;
						break;
					}
				}
				if (j >= pTab.nCol)
				{
					if (sqlite3IsRowid(pChanges.a[i].zName))
					{
						chngRowid = true;
						pRowidExpr = pChanges.a[i].pExpr;
					}
					else
					{
						sqlite3ErrorMsg(pParse, "no such column: %s", pChanges.a[i].zName);
						pParse.checkSchema = 1;
						goto update_cleanup;
					}
				}
#if !SQLITE_OMIT_AUTHORIZATION
{
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName,
pTab.aCol[j].zName, db.aDb[iDb].zName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){
aXRef[j] = -1;
}
}
#endif
			}

			hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid ? 1 : 0) != 0;

			/* Allocate memory for the array aRegIdx[].  There is one entry in the
			** array for each index associated with table being updated.  Fill in
			** the value with a register number for indices that are to be used
			** and with zero for unused indices.
			*/
			for (nIdx = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, nIdx++)
			{
			}
			if (nIdx > 0)
			{
				aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx);
				if (aRegIdx == null)
					goto update_cleanup;
			}
			for (j = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, j++)
			{
				int reg;
				if (hasFK || chngRowid)
				{
					reg = ++pParse.nMem;
				}
				else
				{
					reg = 0;
					for (i = 0; i < pIdx.nColumn; i++)
					{
						if (aXRef[pIdx.aiColumn[i]] >= 0)
						{
							reg = ++pParse.nMem;
							break;
						}
					}
				}
				aRegIdx[j] = reg;
			}

			/* Begin generating code. */
			v = sqlite3GetVdbe(pParse);
			if (v == null)
				goto update_cleanup;
			if (pParse.nested == 0)
				sqlite3VdbeCountChanges(v);
			sqlite3BeginWriteOperation(pParse, 1, iDb);

#if !SQLITE_OMIT_VIRTUALTABLE
			/* Virtual tables must be handled separately */
			if (IsVirtual(pTab))
			{
				updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
								   pWhere, onError);
				pWhere = null;
				pTabList = null;
				goto update_cleanup;
			}
#endif

			/* Allocate required registers. */
			regOldRowid = regNewRowid = ++pParse.nMem;
			if (pTrigger != null || hasFK)
			{
				regOld = pParse.nMem + 1;
				pParse.nMem += pTab.nCol;
			}
			if (chngRowid || pTrigger != null || hasFK)
			{
				regNewRowid = ++pParse.nMem;
			}
			regNew = pParse.nMem + 1;
			pParse.nMem += pTab.nCol;

			/* Start the view context. */
			if (isView)
			{
				sqlite3AuthContextPush(pParse, sContext, pTab.zName);
			}

			/* If we are trying to update a view, realize that view into
			** a ephemeral table.
			*/
#if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER)
			if (isView)
			{
				sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
			}
#endif

			/* Resolve the column names in all the expressions in the
** WHERE clause.
*/
			if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0)
			{
				goto update_cleanup;
			}

			/* Begin the database scan
			*/
			sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
			ExprList NullOrderby = null;
			pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED);
			if (pWInfo == null)
				goto update_cleanup;
			okOnePass = pWInfo.okOnePass != 0;

			/* Remember the rowid of every item to be updated.
			*/
			sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
			if (!okOnePass)
			{
				regRowSet = ++pParse.nMem;
				sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
			}

			/* End the database scan loop.
			*/
			sqlite3WhereEnd(pWInfo);

			/* Initialize the count of updated rows
			*/
			if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab)
			{
				regRowCount = ++pParse.nMem;
				sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
			}

			if (!isView)
			{
				/*
				** Open every index that needs updating.  Note that if any
				** index could potentially invoke a REPLACE conflict resolution
				** action, then we need to open all indices because we might need
				** to be deleting some records.
				*/
				if (!okOnePass)
					sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
				if (onError == OE_Replace)
				{
					openAll = true;
				}
				else
				{
					openAll = false;
					for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
					{
						if (pIdx.onError == OE_Replace)
						{
							openAll = true;
							break;
						}
					}
				}
				for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++)
				{
					if (openAll || aRegIdx[i] > 0)
					{
						KeyInfo pKey = sqlite3IndexKeyinfo(pParse, pIdx);
						sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb,
						pKey, P4_KEYINFO_HANDOFF);
						Debug.Assert(pParse.nTab > iCur + i + 1);
					}
				}
			}

			/* Top of the update loop */
			if (okOnePass)
			{
				int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
				addr = sqlite3VdbeAddOp0(v, OP_Goto);
				sqlite3VdbeJumpHere(v, a1);
			}
			else
			{
				addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
			}

			/* Make cursor iCur point to the record that is being updated. If
			** this record does not exist for some reason (deleted by a trigger,
			** for example, then jump to the next iteration of the RowSet loop.  */
			sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);

			/* If the record number will change, set register regNewRowid to
			** contain the new value. If the record number is not being modified,
			** then regNewRowid is the same register as regOldRowid, which is
			** already populated.  */
			Debug.Assert(chngRowid || pTrigger != null || hasFK || regOldRowid == regNewRowid);
			if (chngRowid)
			{
				sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
				sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
			}

			/* If there are triggers on this table, populate an array of registers 
			** with the required old.* column data.  */
			if (hasFK || pTrigger != null)
			{
				u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
				oldmask |= sqlite3TriggerColmask(pParse,
					pTrigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onError
				);
				for (i = 0; i < pTab.nCol; i++)
				{
					if (aXRef[i] < 0 || oldmask == 0xffffffff || (i < 32 && 0 != (oldmask & (1 << i))))
					{
						sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld + i);
					}
					else
					{
						sqlite3VdbeAddOp2(v, OP_Null, 0, regOld + i);
					}
				}
				if (chngRowid == false)
				{
					sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
				}
			}

			/* Populate the array of registers beginning at regNew with the new
			** row data. This array is used to check constaints, create the new
			** table and index records, and as the values for any new.* references
			** made by triggers.
			**
			** If there are one or more BEFORE triggers, then do not populate the
			** registers associated with columns that are (a) not modified by
			** this UPDATE statement and (b) not accessed by new.* references. The
			** values for registers not modified by the UPDATE must be reloaded from 
			** the database after the BEFORE triggers are fired anyway (as the trigger 
			** may have modified them). So not loading those that are not going to
			** be used eliminates some redundant opcodes.
			*/
			newmask = (int)sqlite3TriggerColmask(
				pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
			);
			for (i = 0; i < pTab.nCol; i++)
			{
				if (i == pTab.iPKey)
				{
					sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i);
				}
				else
				{
					j = aXRef[i];
					if (j >= 0)
					{
						sqlite3ExprCode(pParse, pChanges.a[j].pExpr, regNew + i);
					}
					else if (0 == (tmask & TRIGGER_BEFORE) || i > 31 || (newmask & (1 << i)) != 0)
					{
						/* This branch loads the value of a column that will not be changed 
						** into a register. This is done if there are no BEFORE triggers, or
						** if there are one or more BEFORE triggers that use this value via
						** a new.* reference in a trigger program.
						*/
						testcase(i == 31);
						testcase(i == 32);
						sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i);
						sqlite3ColumnDefault(v, pTab, i, regNew + i);
					}
				}
			}

			/* Fire any BEFORE UPDATE triggers. This happens before constraints are
			** verified. One could argue that this is wrong.
			*/
			if ((tmask & TRIGGER_BEFORE) != 0)
			{
				sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab.nCol);
				sqlite3TableAffinityStr(v, pTab);
				sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
					TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);

				/* The row-trigger may have deleted the row being updated. In this
				** case, jump to the next row. No updates or AFTER triggers are 
				** required. This behaviour - what happens when the row being updated
				** is deleted or renamed by a BEFORE trigger - is left undefined in the
				** documentation.
				*/
				sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);

				/* If it did not delete it, the row-trigger may still have modified 
				** some of the columns of the row being updated. Load the values for 
				** all columns not modified by the update statement into their 
				** registers in case this has happened.
				*/
				for (i = 0; i < pTab.nCol; i++)
				{
					if (aXRef[i] < 0 && i != pTab.iPKey)
					{
						sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i);
						sqlite3ColumnDefault(v, pTab, i, regNew + i);
					}
				}
			}

			if (!isView)
			{
				int j1;                       /* Address of jump instruction */

				/* Do constraint checks. */
				int iDummy;
				sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
					  aRegIdx, (chngRowid ? regOldRowid : 0), true, onError, addr, out iDummy);

				/* Do FK constraint checks. */
				if (hasFK)
				{
					sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
				}

				/* Delete the index entries associated with the current record.  */
				j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
				sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);

				/* If changing the record number, delete the old record.  */
				if (hasFK || chngRowid)
				{
					sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
				}
				sqlite3VdbeJumpHere(v, j1);

				if (hasFK)
				{
					sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
				}

				/* Insert the new index entries and the new record. */
				sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, true, false, false);

				/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
				** handle rows (possibly in other tables) that refer via a foreign key
				** to the row just updated. */
				if (hasFK)
				{
					sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
				}
			}

			/* Increment the row counter 
			*/
			if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab)
			{
				sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
			}

			sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
				TRIGGER_AFTER, pTab, regOldRowid, onError, addr);

			/* Repeat the above with the next record to be updated, until
			** all record selected by the WHERE clause have been updated.
			*/
			sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
			sqlite3VdbeJumpHere(v, addr);

			/* Close all tables */
			for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++)
			{
				if (openAll || aRegIdx[i] > 0)
				{
					sqlite3VdbeAddOp2(v, OP_Close, iCur + i + 1, 0);
				}
			}
			sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);

			/* Update the sqlite_sequence table by storing the content of the
			** maximum rowid counter values recorded while inserting into
			** autoincrement tables.
			*/
			if (pParse.nested == 0 && pParse.pTriggerTab == null)
			{
				sqlite3AutoincrementEnd(pParse);
			}

			/*
			** Return the number of rows that were changed. If this routine is 
			** generating code because of a call to sqlite3NestedParse(), do not
			** invoke the callback function.
			*/
			if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab && 0 == pParse.nested)
			{
				sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
				sqlite3VdbeSetNumCols(v, 1);
				sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
			}

		update_cleanup:
#if !SQLITE_OMIT_AUTHORIZATION
sqlite3AuthContextPop(sContext);
#endif
			sqlite3DbFree(db, ref aRegIdx);
			sqlite3DbFree(db, ref aXRef);
			sqlite3SrcListDelete(db, ref pTabList);
			sqlite3ExprListDelete(db, ref pChanges);
			sqlite3ExprDelete(db, ref pWhere);
			return;
		}
Ejemplo n.º 49
0
 public override void GetUsedIdents(ScopeChecker.UsedIdents usedIdents)
 {
     ExprList.ForEach(cmd => cmd.GetUsedIdents(usedIdents));
     usedIdents.AddProcFuncIdent(Ident);
 }
Ejemplo n.º 50
0
/*
** Generate code for an UPDATE of a virtual table.
**
** The strategy is that we create an ephemerial table that contains
** for each row to be changed:
**
**   (A)  The original rowid of that row.
**   (B)  The revised rowid for the row. (note1)
**   (C)  The content of every column in the row.
**
** Then we loop over this ephemeral table and for each row in
** the ephermeral table call VUpdate.
**
** When finished, drop the ephemeral table.
**
** (note1) Actually, if we know in advance that (A) is always the same
** as (B) we only store (A), then duplicate (A) when pulling
** it out of the ephemeral table before calling VUpdate.
*/
static void updateVirtualTable(
Parse pParse,       /* The parsing context */
SrcList pSrc,       /* The virtual table to be modified */
Table pTab,         /* The virtual table */
ExprList pChanges,  /* The columns to change in the UPDATE statement */
Expr pRowid,        /* Expression used to recompute the rowid */
int aXRef,          /* Mapping from columns of pTab to entries in pChanges */
Expr pWhere         /* WHERE clause of the UPDATE statement */
)
{
Vdbe v = pParse.pVdbe;  /* Virtual machine under construction */
ExprList pEList = 0;     /* The result set of the SELECT statement */
Select pSelect = 0;      /* The SELECT statement */
Expr pExpr;              /* Temporary expression */
int ephemTab;             /* Table holding the result of the SELECT */
int i;                    /* Loop counter */
int addr;                 /* Address of top of loop */
int iReg;                 /* First register in set passed to OP_VUpdate */
sqlite3 db = pParse.db; /* Database connection */
const char *pVTab = (const char*)sqlite3GetVTable(db, pTab);
SelectDest dest;

/* Construct the SELECT statement that will find the new values for
** all updated rows.
*/
pEList = sqlite3ExprListAppend(pParse, 0,
sqlite3CreateIdExpr(pParse, "_rowid_"));
if( pRowid ){
pEList = sqlite3ExprListAppend(pParse, pEList,
sqlite3ExprDup(db, pRowid,0), 0);
}
Debug.Assert( pTab.iPKey<0 );
for(i=0; i<pTab.nCol; i++){
if( aXRef[i]>=0 ){
pExpr = sqlite3ExprDup(db, pChanges.a[aXRef[i]].pExpr,0);
}else{
pExpr = sqlite3CreateIdExpr(pParse, pTab.aCol[i].zName);
}
pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
}
pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);

/* Create the ephemeral table into which the update results will
** be stored.
*/
Debug.Assert( v );
ephemTab = pParse.nTab++;
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab.nCol+1+(pRowid!=0));

/* fill the ephemeral table
*/
sqlite3SelectDestInit(dest, SRT_Table, ephemTab);
sqlite3Select(pParse, pSelect, ref dest);

/* Generate code to scan the ephemeral table and call VUpdate. */
iReg = ++pParse.nMem;
pParse.nMem += pTab.nCol+1;
addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
sqlite3VdbeAddOp3(v, OP_Column,  ephemTab, 0, iReg);
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid
1:0), iReg+1);
for(i=0; i<pTab.nCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
}
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab.nCol+2, iReg, pVTab, P4_VTAB);
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
sqlite3VdbeJumpHere(v, addr);
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);

/* Cleanup */
sqlite3SelectDelete(pSelect);
}
Ejemplo n.º 51
0
 public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, int null_5, OE orconf)
 {
     return(TriggerInsertStep(ctx, tableName, column, list, null, orconf));
 }