public async Task <IActionResult> Index() { var db = new NameContext(); db.Add <Name>(new Name { FirstName = "Trausti Thor", LastName = "Johannsson" }); db.Add <Name>(new Name { FirstName = "Elsa Þóra", LastName = "Eggerstdóttir" }); db.Add <Name>(new Name { FirstName = "Ósk", LastName = "Traustadóttir" }); db.Add <Name>(new Name { FirstName = "Íris Rós", LastName = "Traustadóttir" }); await db.SaveChangesAsync(); return(Ok()); }
/// <inheritdoc/> public string ToDelimitedString() { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture; string separator = IsSubcomponent ? Configuration.SubcomponentSeparator : Configuration.ComponentSeparator; return(string.Format( culture, StringHelper.StringFormatSequence(0, 15, separator), FamilyName?.ToDelimitedString(), GivenName, SecondAndFurtherGivenNamesOrInitialsThereof, Suffix, Prefix, Degree, NameTypeCode, NameRepresentationCode, NameContext?.ToDelimitedString(), NameValidityRange?.ToDelimitedString(), NameAssemblyOrder, EffectiveDate.HasValue ? EffectiveDate.Value.ToString(Consts.DateTimeFormatPrecisionSecond, culture) : null, ExpirationDate.HasValue ? ExpirationDate.Value.ToString(Consts.DateTimeFormatPrecisionSecond, culture) : null, ProfessionalSuffix, CalledBy ).TrimEnd(separator.ToCharArray())); }
internal virtual void AssertCanAppend(DomNode node, DomNode willReplace) { if (node.IsElement || node.IsAttribute) { NameContext.DemandValidName(nameof(node), node); } }
/// <inheritdoc/> public string ToDelimitedString() { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture; string separator = IsSubcomponent ? Configuration.SubcomponentSeparator : Configuration.ComponentSeparator; return(string.Format( culture, StringHelper.StringFormatSequence(0, 18, separator), PersonIdentifier, FamilyName?.ToDelimitedString(), GivenName, SecondAndFurtherGivenNamesOrInitialsThereof, Suffix, Prefix, Degree, SourceTable, AssigningAuthority?.ToDelimitedString(), NameTypeCode, IdentifierCheckDigit, CheckDigitScheme, IdentifierTypeCode, AssigningFacility?.ToDelimitedString(), NameRepresentationCode, NameContext?.ToDelimitedString(), NameValidityRange?.ToDelimitedString(), NameAssemblyOrder ).TrimEnd(separator.ToCharArray())); }
/// <inheritdoc/> public string ToDelimitedString() { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture; string separator = IsSubcomponent ? Configuration.SubcomponentSeparator : Configuration.ComponentSeparator; return(string.Format( culture, StringHelper.StringFormatSequence(0, 23, separator), PersonIdentifier, FamilyName?.ToDelimitedString(), GivenName, SecondAndFurtherGivenNamesOrInitialsThereof, Suffix, Prefix, Degree, SourceTable, AssigningAuthority?.ToDelimitedString(), NameTypeCode, IdentifierCheckDigit, CheckDigitScheme, IdentifierTypeCode, AssigningFacility?.ToDelimitedString(), NameRepresentationCode, NameContext?.ToDelimitedString(), NameValidityRange?.ToDelimitedString(), NameAssemblyOrder, EffectiveDate.HasValue ? EffectiveDate.Value.ToString(Consts.DateTimeFormatPrecisionSecond, culture) : null, ExpirationDate.HasValue ? ExpirationDate.Value.ToString(Consts.DateTimeFormatPrecisionSecond, culture) : null, ProfessionalSuffix, AssigningJurisdiction?.ToDelimitedString(), AssigningAgencyOrDepartment?.ToDelimitedString() ).TrimEnd(separator.ToCharArray())); }
protected virtual void RenderName(HtmlTextWriter output, string id, NameContext context) { Assert.ArgumentNotNull(output, "output"); Assert.ArgumentNotNullOrEmpty(id, "id"); if (!context.Editable) { output.Write("<span class='header-title'>"); output.Write(context.Name); output.Write("</span>"); } else { output.Write("<a href='#' class='header-title'>"); output.Write(StringUtil.EscapeQuote(context.Name)); output.Write("</a>"); string str = "onkeydown='javascript:return Sitecore.CollapsiblePanel.editNameChanging(this, event);'"; string str2 = string.IsNullOrEmpty(context.OnNameChanging) ? string.Empty : ("onkeyup=\"" + context.OnNameChanging + "\""); string str3 = string.IsNullOrEmpty(context.OnNameChanged) ? string.Empty : ("onchange=\"" + context.OnNameChanged + "\""); output.Write( "<input type='text' {0} {1} id='{2}_name' name='{2}_name' data-meta-id='{2}' data-validation-msg=\"{3}\" style='display:none' class='header-title-edit' value=\"{4}\" {5} />", new object[] { str2, str3, id, Translate.Text("The name cannot be blank."), StringUtil.EscapeQuote(context.Name), str }); } }
// Methods public string Render(string id, string panelHtml, bool draggable, NameContext nameContext, ActionsContext actionsContext) { Assert.ArgumentNotNullOrEmpty(id, "id"); Assert.ArgumentNotNullOrEmpty(panelHtml, "panelHtml"); var output = new HtmlTextWriter(new StringWriter()); output.Write("<div id='{0}' class='{1}'>", id, CssClass); output.Write("<div id='{0}_header' class='panel-header'>", id); output.Write("<div class='header-actions'>"); output.Write("<a href='#'>"); output.Write("<div class='icon-expand'></div>"); output.Write("</a>"); output.Write("</div>"); RenderActions(output, id, actionsContext); if (draggable) { output.Write("<img src='/sitecore modules/shell/analytics/images/draghandle9x15.png' class='drag-handle' />"); } RenderName(output, id, nameContext); output.Write("</div>"); output.Write("<div id='{0}_panel' class='panel'>", id); output.Write(panelHtml); output.Write("</div>"); output.Write("</div>"); return(output.InnerWriter.ToString()); }
public NameAnalyser(NameContext context, NameAnalysisOptions options) { if (context == null) throw new ArgumentNullException("context"); if (options == null) throw new ArgumentNullException("options"); this.context = context; this.options = options; }
public virtual string ToString(char separator) { var ns = NextSeparator(separator); return ($"{FamilyName}{separator}{GivenName}{separator}{SecondGivenNamesOrInitials}" + $"{separator}{Suffix}{separator}{Prefix}{separator}{Degree.BestValue}{separator}" + $"{NameTypeCode.BestValue}{separator}{NameRepresentationCode.BestValue}{separator}" + $"{NameContext?.ToString(ns)}{separator}{NameAssemblyOrder?.BestValue}" .TrimEnd(separator)); }
public override string VisitName(NameContext context) { if (context.TEXT() != null) { return(Text(context.TEXT())); } return(context.RANDOMNAME() != null ? Generator.FullName : "Nameless"); }
public static void ResolveSelectNames(Parse parse, Select p, NameContext outerNC) { Debug.Assert(p != null); Walker w = new Walker(); w.ExprCallback = ResolveExprStep; w.SelectCallback = ResolveSelectStep; w.Parse = parse; w.u.NC = outerNC; w.WalkSelect(p); }
static bool ResolveOrderGroupBy(NameContext nc, Select select, ExprList orderBy, string type) { if (orderBy == null) { return(false); } int result = select.EList.Exprs; // Number of terms in the result set Parse parse = nc.Parse; // Parsing context int i; ExprList.ExprListItem item; // A term of the ORDER BY clause for (i = 0; i < orderBy.Exprs; i++) { item = orderBy.Ids[i]; Expr expr = item.Expr; int colId = ResolveAsName(parse, select.EList, expr); // Column number if (colId > 0) { // If an AS-name match is found, mark this ORDER BY column as being a copy of the iCol-th result-set column. The subsequent call to // sqlite3ResolveOrderGroupBy() will convert the expression to a copy of the iCol-th result-set expression. item.OrderByCol = (ushort)colId; continue; } if (expr.SkipCollate().IsInteger(ref colId)) { // The ORDER BY term is an integer constant. Again, set the column number so that sqlite3ResolveOrderGroupBy() will convert the // order-by term to a copy of the result-set expression if (colId < 1 || colId > 0xffff) { ResolveOutOfRangeError(parse, type, i + 1, result); return(true); } item.OrderByCol = (ushort)colId; continue; } // Otherwise, treat the ORDER BY term as an ordinary expression item.OrderByCol = 0; if (Walker.ResolveExprNames(nc, ref expr)) { return(true); } for (int j = 0; j < select.EList.Exprs; j++) { if (Expr.Compare(expr, select.EList.Ids[j].Expr) == 0) { item.OrderByCol = (ushort)(j + 1); } } } return(Walker.ResolveOrderGroupBy(parse, select, orderBy, type)); }
public override string ToString(char separator) { var ns = NextSeparator(separator); return ($"{ID}{separator}{FamilyName}{separator}{GivenName}{separator}{SecondGivenNamesOrInitials}" + $"{separator}{Suffix}{separator}{Prefix}{separator}{Degree}{separator}" + $"{SourceTable}{separator}{AssigningAuthority?.ToString(ns)}{separator}" + $"{NameTypeCode}{separator}{IdentifierCheckDigit}{separator}{CheckDigitScheme}{separator}" + $"{IdentifierTypeCode}{separator}{AssigningFacility?.ToString(ns)}{separator}" + $"{NameRepresentationCode}{separator}{NameContext?.ToString(ns)}{separator}" + $"{NameValidityRange?.ToString(ns)}{separator}{NameAssemblyOrder}" .TrimEnd(separator)); }
public NameAnalyser(NameContext context, NameAnalysisOptions options) { if (context == null) { throw new ArgumentNullException("context"); } if (options == null) { throw new ArgumentNullException("options"); } this.context = context; this.options = options; }
public override object VisitLine(SpeakParser.LineContext context) { NameContext name = context.name(); OpinionContext opinion = context.opinion(); SpeakLine line = new SpeakLine() { Person = name.GetText(), Text = opinion.GetText().Trim('"') }; Lines.Add(line); return(line); }
static void CodeAttach(Parse parse, AUTH type, FuncDef func, Expr authArg, Expr filename, Expr dbName, Expr key) { Context ctx = parse.Ctx; NameContext sName; sName = new NameContext(); sName.Parse = parse; if (ResolveAttachExpr(sName, filename) != RC.OK || ResolveAttachExpr(sName, dbName) != RC.OK || ResolveAttachExpr(sName, key) != RC.OK) { parse.Errs++; goto attach_end; } #if !OMIT_AUTHORIZATION if (authArg != null) { string authArgToken = (authArg.OP == TK.STRING ? authArg.u.Token : null); ARC arc = Auth.Check(parse, type, authArgToken, null, null); if (arc != ARC.OK) { goto attach_end; } } #endif Vdbe v = parse.GetVdbe(); int regArgs = Expr.GetTempRange(parse, 4); Expr.Code(parse, filename, regArgs); Expr.Code(parse, dbName, regArgs + 1); Expr.Code(parse, key, regArgs + 2); Debug.Assert(v != null || ctx.MallocFailed); if (v != null) { v.AddOp3(OP.Function, 0, regArgs + 3 - func.Args, regArgs + 3); Debug.Assert(func.Args == -1 || (func.Args & 0xff) == func.Args); v.ChangeP5((byte)(func.Args)); v.ChangeP4(-1, func, Vdbe.P4T.FUNCDEF); // Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this statement only). For DETACH, set it to false (expire all existing statements). v.AddOp1(OP.Expire, (type == AUTH.ATTACH ? 1 : 0)); } attach_end: Expr.Delete(ctx, ref filename); Expr.Delete(ctx, ref dbName); Expr.Delete(ctx, ref key); }
public override object VisitName(NameContext context) { var obj = ""; foreach (var i in range(0, context.id().Length - 1, 1, true, true)) { var id = (Result)(Visit(context.id(i))); if (i == 0) { obj += id.text; } else { obj += "." + id.text; } } return(obj); }
public override object VisitName([NotNull] NameContext context) { var obj = ""; for (int i = 0; i < context.id().Length; i++) { var id = (Result)Visit(context.id(i)); if (i == 0) { obj += "" + id.text; } else { obj += "." + id.text; } } return(obj); }
/// <summary> /// Disposes of the database context if possible and then initialises a new instance. /// </summary> private void InitContext() { // if the context is not null and RecycleContext is enabled, // dispose of the current context if (this.context != null && this.RecycleContext) { this.context.Dispose(); this.context = null; } // initialise the database context if (this.context == null) { this.context = new NameContext(); this.context.Configuration.ValidateOnSaveEnabled = false; this.context.Configuration.AutoDetectChangesEnabled = false; } }
static void Main(string[] args) { if (args.Length == 0) { Console.WriteLine("Generate basic code files."); } else if (args[0][0] == '/') { Console.WriteLine("There is no flags."); } else { var ctx = new NameContext(args); var fg = new FileGenerator(ctx); fg.CreateFolder(); fg.CreateFiles(); } }
public override object VisitName(NameContext context) { var obj = ""; foreach (var(i, v) in context.id().WithIndex()) { var id = ((Result)Visit(v)); if (i == 0) { obj += id.text; } else { obj += "." + id.text; } } return(obj); }
static RC ResolveAttachExpr(NameContext name, Expr expr) { RC rc = RC.OK; if (expr != null) { if (expr.OP != TK.ID) { rc = sqlite3ResolveExprNames(name, ref expr); if (rc == RC.OK && !expr.IsConstant()) { name.Parse.ErrorMsg("invalid name: \"%s\"", expr.u.Token); return RC.ERROR; } } else expr.OP = TK.STRING; } return rc; }
public override string TransformName(IMetaInfo info, string name, NameContext context) { var prefix = string.Empty; var suffix = string.Empty; var forBlueprint = true; if (info is TypeInfo typeInfo) { var asBaseClass = false; typeInfo.ForAttribute <UnrealTypeAttributeBase>(attr => { forBlueprint = attr.ForBlueprint; prefix = forBlueprint ? "U" : "F"; asBaseClass = attr.AsBaseClass; }); typeInfo.ForAttribute <UnrealActorAttribute>(attr => { prefix = "A"; suffix = "Actor"; }); typeInfo.ForAttribute <UnrealComponentAttribute>(attr => { suffix = "Component"; }); typeInfo.ForAttribute <UnrealStructAttribute>(attr => { prefix = "F"; }); if (asBaseClass) { suffix += "Base"; } } return($"{prefix}{name.ToPascalCase()}{suffix}"); }
public static bool ResolveExprNames(NameContext nc, ref Expr expr) { if (expr == null) { return(false); } #if MAX_EXPR_DEPTH { Parse parse = nc.Parse; if (Expr.CheckHeight(parse, expr.Height + nc.Parse.Height) != 0) { return(true); } parse.Height += expr.Height; } #endif bool savedHasAgg = ((nc.NCFlags & NC.HasAgg) != 0); nc.NCFlags &= ~NC.HasAgg; Walker w = new Walker(); w.ExprCallback = ResolveExprStep; w.SelectCallback = ResolveSelectStep; w.Parse = nc.Parse; w.u.NC = nc; w.WalkExpr(expr); #if MAX_EXPR_DEPTH nc.Parse.Height -= expr.Height; #endif if (nc.Errs > 0 || w.Parse.Errs > 0) { E.ExprSetProperty(expr, EP.Error); } if ((nc.NCFlags & NC.HasAgg) != 0) { E.ExprSetProperty(expr, EP.Agg); } else if (savedHasAgg) { nc.NCFlags |= NC.HasAgg; } return(E.ExprHasProperty(expr, EP.Error)); }
/* ** 2003 April 6 ** ** 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 code used to implement the ATTACH and DETACH commands. ************************************************************************* ** 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: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 ** ************************************************************************* */ //#include "sqliteInt.h" #if !SQLITE_OMIT_ATTACH /* ** Resolve an expression that was part of an ATTACH or DETACH statement. This ** is slightly different from resolving a normal SQL expression, because simple ** identifiers are treated as strings, not possible column names or aliases. ** ** i.e. if the parser sees: ** ** ATTACH DATABASE abc AS def ** ** it treats the two expressions as literal strings 'abc' and 'def' instead of ** looking for columns of the same name. ** ** This only applies to the root node of pExpr, so the statement: ** ** ATTACH DATABASE abc||def AS 'db2' ** ** will fail because neither abc or def can be resolved. */ static int resolveAttachExpr( NameContext pName, Expr pExpr ) { int rc = SQLITE_OK; if ( pExpr != null ) { if ( pExpr.op != TK_ID ) { rc = sqlite3ResolveExprNames( pName, ref pExpr ); if ( rc == SQLITE_OK && sqlite3ExprIsConstant( pExpr ) == 0 ) { sqlite3ErrorMsg( pName.pParse, "invalid name: \"%s\"", pExpr.u.zToken ); return SQLITE_ERROR; } } else { pExpr.op = TK_STRING; } } return rc; }
public NameContext name() { NameContext _localctx = new NameContext(Context, State); EnterRule(_localctx, 4, RULE_name); try { EnterOuterAlt(_localctx, 1); { State = 19; Match(WORD); } } catch (RecognitionException re) { _localctx.exception = re; ErrorHandler.ReportError(this, re); ErrorHandler.Recover(this, re); } finally { ExitRule(); } return(_localctx); }
static RC ResolveAttachExpr(NameContext name, Expr expr) { RC rc = RC.OK; if (expr != null) { if (expr.OP != TK.ID) { rc = sqlite3ResolveExprNames(name, ref expr); if (rc == RC.OK && !expr.IsConstant()) { name.Parse.ErrorMsg("invalid name: \"%s\"", expr.u.Token); return(RC.ERROR); } } else { expr.OP = TK.STRING; } } return(rc); }
/* ** 2003 April 6 ** ** 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 code used to implement the ATTACH and DETACH commands. ** ** $Id: attach.c,v 1.93 2009/05/31 21:21:41 drh Exp $ ** ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** $Header$ ************************************************************************* */ //#include "sqliteInt.h" #if !SQLITE_OMIT_ATTACH /* ** Resolve an expression that was part of an ATTACH or DETACH statement. This ** is slightly different from resolving a normal SQL expression, because simple ** identifiers are treated as strings, not possible column names or aliases. ** ** i.e. if the parser sees: ** ** ATTACH DATABASE abc AS def ** ** it treats the two expressions as literal strings 'abc' and 'def' instead of ** looking for columns of the same name. ** ** This only applies to the root node of pExpr, so the statement: ** ** ATTACH DATABASE abc||def AS 'db2' ** ** will fail because neither abc or def can be resolved. */ static int resolveAttachExpr(NameContext pName, Expr pExpr) { int rc = SQLITE_OK; if (pExpr != null) { if (pExpr.op != TK_ID) { rc = sqlite3ResolveExprNames(pName, ref pExpr); if (rc == SQLITE_OK && sqlite3ExprIsConstant(pExpr) == 0) { sqlite3ErrorMsg(pName.pParse, "invalid name: \"%s\"", pExpr.u.zToken); return(SQLITE_ERROR); } } else { pExpr.op = TK_STRING; } } return(rc); }
static int ResolveOrderByTermToExprList(Parse parse, Select select, Expr expr) { int i = 0; Debug.Assert(!expr.IsInteger(ref i)); ExprList list = select.EList; // The columns of the result set // Resolve all names in the ORDER BY term expression NameContext nc = new NameContext(); // Name context for resolving pE nc.Parse = parse; nc.SrcList = select.Src; nc.EList = list; nc.NCFlags = NC.AllowAgg; nc.Errs = 0; Context ctx = parse.Ctx; // Database connection byte savedSuppErr = ctx.SuppressErr; // Saved value of db->suppressErr ctx.SuppressErr = 1; bool r = Walker.ResolveExprNames(nc, ref expr); ctx.SuppressErr = savedSuppErr; if (r) { return(0); } // Try to match the ORDER BY expression against an expression in the result set. Return an 1-based index of the matching result-set entry. for (i = 0; i < list.Exprs; i++) { if (Expr.Compare(list.Ids[i].Expr, expr) < 2) { return(i + 1); } } // If no match, return 0. return(0); }
/* ** Resolve all names in all expressions of a SELECT and in all ** decendents of the SELECT, including compounds off of p.pPrior, ** subqueries in expressions, and subqueries used as FROM clause ** terms. ** ** See sqlite3ResolveExprNames() for a description of the kinds of ** transformations that occur. ** ** All SELECT statements should have been expanded using ** sqlite3SelectExpand() prior to invoking this routine. */ static void sqlite3ResolveSelectNames( Parse pParse, /* The parser context */ Select p, /* The SELECT statement being coded. */ NameContext pOuterNC /* Name context for parent SELECT statement */ ) { Walker w = new Walker(); Debug.Assert( p != null ); w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.pParse = pParse; w.u.pNC = pOuterNC; sqlite3WalkSelect( w, p ); }
/* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up ** that name in the set of source tables in pSrcList and make the pExpr ** expression node refer back to that source column. The following changes ** are made to pExpr: ** ** pExpr->iDb Set the index in db->aDb[] of the database X ** (even if X is implied). ** pExpr->iTable Set to the cursor number for the table obtained ** from pSrcList. ** pExpr->pTab Points to the Table structure of X.Y (even if ** X and/or Y are implied.) ** pExpr->iColumn Set to the column number within the table. ** pExpr->op Set to TK_COLUMN. ** pExpr->pLeft Any expression this points to is deleted ** pExpr->pRight Any expression this points to is deleted. ** ** The zDb variable is the name of the database (the "X"). This value may be ** NULL meaning that name is of the form Y.Z or Z. Any available database ** can be used. The zTable variable is the name of the table (the "Y"). This ** value can be NULL if zDb is also NULL. If zTable is NULL it ** means that the form of the name is Z and that columns from any table ** can be used. ** ** If the name cannot be resolved unambiguously, leave an error message ** in pParse and return WRC_Abort. Return WRC_Prune on success. */ static int lookupName( Parse pParse, /* The parsing context */ string zDb, /* Name of the database containing table, or NULL */ string zTab, /* Name of table containing column, or NULL */ string zCol, /* Name of the column. */ NameContext pNC, /* The name context used to resolve the name */ Expr pExpr /* Make this EXPR node point to the selected column */ ) { int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ int cntTab = 0; /* Number of matching table names */ sqlite3 db = pParse.db; /* The database connection */ SrcList_item pItem; /* Use for looping over pSrcList items */ SrcList_item pMatch = null; /* The matching pSrcList item */ NameContext pTopNC = pNC; /* First namecontext in the list */ Schema pSchema = null; /* Schema of the expression */ int isTrigger = 0; Debug.Assert( pNC != null ); /* the name context cannot be NULL. */ Debug.Assert( zCol != null ); /* The Z in X.Y.Z cannot be NULL */ Debug.Assert( !ExprHasAnyProperty( pExpr, EP_TokenOnly | EP_Reduced ) ); /* Initialize the node to no-match */ pExpr.iTable = -1; pExpr.pTab = null; ExprSetIrreducible( pExpr ); /* Start at the inner-most context and move outward until a match is found */ while ( pNC != null && cnt == 0 ) { ExprList pEList; SrcList pSrcList = pNC.pSrcList; if ( pSrcList != null ) { for ( i = 0; i < pSrcList.nSrc; i++ )//, pItem++ ) { pItem = pSrcList.a[i]; Table pTab; int iDb; Column pCol; pTab = pItem.pTab; Debug.Assert( pTab != null && pTab.zName != null ); iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); Debug.Assert( pTab.nCol > 0 ); if ( zTab != null ) { if ( pItem.zAlias != null ) { string zTabName = pItem.zAlias; if ( !zTabName.Equals( zTab, StringComparison.OrdinalIgnoreCase ) ) continue; } else { string zTabName = pTab.zName; if ( NEVER( zTabName == null ) || !zTabName.Equals( zTab ,StringComparison.OrdinalIgnoreCase ) ) { continue; } if ( zDb != null && !db.aDb[iDb].zName.Equals( zDb ,StringComparison.OrdinalIgnoreCase ) ) { continue; } } } if ( 0 == ( cntTab++ ) ) { pExpr.iTable = pItem.iCursor; pExpr.pTab = pTab; pSchema = pTab.pSchema; pMatch = pItem; } for ( j = 0; j < pTab.nCol; j++ )//, pCol++ ) { pCol = pTab.aCol[j]; if ( pCol.zName.Equals( zCol, StringComparison.OrdinalIgnoreCase ) ) { IdList pUsing; cnt++; pExpr.iTable = pItem.iCursor; pExpr.pTab = pTab; pMatch = pItem; pSchema = pTab.pSchema; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr.iColumn = (short)( j == pTab.iPKey ? -1 : j ); if ( i < pSrcList.nSrc - 1 ) { if ( ( pSrcList.a[i + 1].jointype & JT_NATURAL ) != 0 )// pItem[1].jointype { /* If this match occurred in the left table of a natural join, ** then skip the right table to avoid a duplicate match */ //pItem++; i++; } else if ( ( pUsing = pSrcList.a[i + 1].pUsing ) != null )//pItem[1].pUsing { /* If this match occurs on a column that is in the USING clause ** of a join, skip the search of the right table of the join ** to avoid a duplicate match there. */ int k; for ( k = 0; k < pUsing.nId; k++ ) { if ( pUsing.a[k].zName.Equals( zCol ,StringComparison.OrdinalIgnoreCase ) ) { //pItem++; i++; break; } } } } break; } } } } #if !SQLITE_OMIT_TRIGGER /* If we have not already resolved the name, then maybe ** it is a new.* or old.* trigger argument reference */ if ( zDb == null && zTab != null && cnt == 0 && pParse.pTriggerTab != null ) { int op = pParse.eTriggerOp; Table pTab = null; Debug.Assert( op == TK_DELETE || op == TK_UPDATE || op == TK_INSERT ); if ( op != TK_DELETE && "new".Equals( zTab ,StringComparison.OrdinalIgnoreCase ) ) { pExpr.iTable = 1; pTab = pParse.pTriggerTab; } else if ( op != TK_INSERT && "old".Equals( zTab ,StringComparison.OrdinalIgnoreCase ) ) { pExpr.iTable = 0; pTab = pParse.pTriggerTab; } if ( pTab != null ) { int iCol; pSchema = pTab.pSchema; cntTab++; for ( iCol = 0; iCol < pTab.nCol; iCol++ ) { Column pCol = pTab.aCol[iCol]; if ( pCol.zName.Equals( zCol ,StringComparison.OrdinalIgnoreCase ) ) { if ( iCol == pTab.iPKey ) { iCol = -1; } break; } } if ( iCol >= pTab.nCol && sqlite3IsRowid( zCol ) ) { iCol = -1; /* IMP: R-44911-55124 */ } if ( iCol < pTab.nCol ) { cnt++; if ( iCol < 0 ) { pExpr.affinity = SQLITE_AFF_INTEGER; } else if ( pExpr.iTable == 0 ) { testcase( iCol == 31 ); testcase( iCol == 32 ); pParse.oldmask |= ( iCol >= 32 ? 0xffffffff : ( ( (u32)1 ) << iCol ) ); } else { testcase( iCol == 31 ); testcase( iCol == 32 ); pParse.newmask |= ( iCol >= 32 ? 0xffffffff : ( ( (u32)1 ) << iCol ) ); } pExpr.iColumn = (i16)iCol; pExpr.pTab = pTab; isTrigger = 1; } } } #endif //* !SQLITE_OMIT_TRIGGER) */ /* ** Perhaps the name is a reference to the ROWID */ if ( cnt == 0 && cntTab == 1 && sqlite3IsRowid( zCol ) ) { cnt = 1; pExpr.iColumn = -1; /* IMP: R-44911-55124 */ pExpr.affinity = SQLITE_AFF_INTEGER; } /* ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z ** might refer to an result-set alias. This happens, for example, when ** we are resolving names in the WHERE clause of the following command: ** ** SELECT a+b AS x FROM table WHERE x<10; ** ** In cases like this, replace pExpr with a copy of the expression that ** forms the result set entry ("a+b" in the example) and return immediately. ** Note that the expression in the result set should have already been ** resolved by the time the WHERE clause is resolved. */ if ( cnt == 0 && ( pEList = pNC.pEList ) != null && zTab == null ) { for ( j = 0; j < pEList.nExpr; j++ ) { string zAs = pEList.a[j].zName; if ( zAs != null && zAs.Equals( zCol ,StringComparison.OrdinalIgnoreCase ) ) { Expr pOrig; Debug.Assert( pExpr.pLeft == null && pExpr.pRight == null ); Debug.Assert( pExpr.x.pList == null ); Debug.Assert( pExpr.x.pSelect == null ); pOrig = pEList.a[j].pExpr; if ( 0 == pNC.allowAgg && ExprHasProperty( pOrig, EP_Agg ) ) { sqlite3ErrorMsg( pParse, "misuse of aliased aggregate %s", zAs ); return WRC_Abort; } resolveAlias( pParse, pEList, j, pExpr, "" ); cnt = 1; pMatch = null; Debug.Assert( zTab == null && zDb == null ); goto lookupname_end; } } } /* Advance to the next name context. The loop will exit when either ** we have a match (cnt>0) or when we run out of name contexts. */ if ( cnt == 0 ) { pNC = pNC.pNext; } } /* ** If X and Y are NULL (in other words if only the column name Z is ** supplied) and the value of Z is enclosed in double-quotes, then ** Z is a string literal if it doesn't match any column names. In that ** case, we need to return right away and not make any changes to ** pExpr. ** ** Because no reference was made to outer contexts, the pNC.nRef ** fields are not changed in any context. */ if ( cnt == 0 && zTab == null && ExprHasProperty( pExpr, EP_DblQuoted ) ) { pExpr.op = TK_STRING; pExpr.pTab = null; return WRC_Prune; } /* ** cnt==0 means there was not match. cnt>1 means there were two or ** more matches. Either way, we have an error. */ if ( cnt != 1 ) { string zErr; zErr = cnt == 0 ? "no such column" : "ambiguous column name"; if ( zDb != null ) { sqlite3ErrorMsg( pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol ); } else if ( zTab != null ) { sqlite3ErrorMsg( pParse, "%s: %s.%s", zErr, zTab, zCol ); } else { sqlite3ErrorMsg( pParse, "%s: %s", zErr, zCol ); } pParse.checkSchema = 1; pTopNC.nErr++; } /* If a column from a table in pSrcList is referenced, then record ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the ** column number is greater than the number of bits in the bitmask ** then set the high-order bit of the bitmask. */ if ( pExpr.iColumn >= 0 && pMatch != null ) { int n = pExpr.iColumn; testcase( n == BMS - 1 ); if ( n >= BMS ) { n = BMS - 1; } Debug.Assert( pMatch.iCursor == pExpr.iTable ); pMatch.colUsed |= ( (Bitmask)1 ) << n; } /* Clean up and return */ sqlite3ExprDelete( db, ref pExpr.pLeft ); pExpr.pLeft = null; sqlite3ExprDelete( db, ref pExpr.pRight ); pExpr.pRight = null; pExpr.op = (u8)( isTrigger != 0 ? TK_TRIGGER : TK_COLUMN ); lookupname_end: if ( cnt == 1 ) { Debug.Assert( pNC != null ); sqlite3AuthRead( pParse, pExpr, pSchema, pNC.pSrcList ); /* Increment the nRef value on all name contexts from TopNC up to ** the point where the name matched. */ for ( ; ; ) { Debug.Assert( pTopNC != null ); pTopNC.nRef++; if ( pTopNC == pNC ) break; pTopNC = pTopNC.pNext; } return WRC_Prune; } else { return WRC_Abort; } }
/* ** This function is called to generate code executed when a row is deleted ** from the parent table of foreign key constraint pFKey and, if pFKey is ** deferred, when a row is inserted into the same table. When generating ** code for an SQL UPDATE operation, this function may be called twice - ** once to "delete" the old row and once to "insert" the new row. ** ** The code generated by this function scans through the rows in the child ** table that correspond to the parent table row being deleted or inserted. ** For each child row found, one of the following actions is taken: ** ** Operation | FK type | Action taken ** -------------------------------------------------------------------------- ** DELETE immediate Increment the "immediate constraint counter". ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, ** throw a "foreign key constraint failed" exception. ** ** INSERT immediate Decrement the "immediate constraint counter". ** ** DELETE deferred Increment the "deferred constraint counter". ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, ** throw a "foreign key constraint failed" exception. ** ** INSERT deferred Decrement the "deferred constraint counter". ** ** These operations are identified in the comment at the top of this file ** (fkey.c) as "I.2" and "D.2". */ static void fkScanChildren( Parse pParse, /* Parse context */ SrcList pSrc, /* SrcList containing the table to scan */ Table pTab, Index pIdx, /* Foreign key index */ FKey pFKey, /* Foreign key relationship */ int[] aiCol, /* Map from pIdx cols to child table cols */ int regData, /* Referenced table data starts here */ int nIncr /* Amount to increment deferred counter by */ ) { sqlite3 db = pParse.db; /* Database handle */ int i; /* Iterator variable */ Expr pWhere = null; /* WHERE clause to scan with */ NameContext sNameContext; /* Context used to resolve WHERE clause */ WhereInfo pWInfo; /* Context used by sqlite3WhereXXX() */ int iFkIfZero = 0; /* Address of OP_FkIfZero */ Vdbe v = sqlite3GetVdbe( pParse ); Debug.Assert( null == pIdx || pIdx.pTable == pTab ); if ( nIncr < 0 ) { iFkIfZero = sqlite3VdbeAddOp2( v, OP_FkIfZero, pFKey.isDeferred, 0 ); } /* Create an Expr object representing an SQL expression like: ** ** <parent-key1> = <child-key1> AND <parent-key2> = <child-key2> ... ** ** The collation sequence used for the comparison should be that of ** the parent key columns. The affinity of the parent key column should ** be applied to each child key value before the comparison takes place. */ for ( i = 0; i < pFKey.nCol; i++ ) { Expr pLeft; /* Value from parent table row */ Expr pRight; /* Column ref to child table */ Expr pEq; /* Expression (pLeft = pRight) */ int iCol; /* Index of column in child table */ string zCol; /* Name of column in child table */ pLeft = sqlite3Expr( db, TK_REGISTER, null ); if ( pLeft != null ) { /* Set the collation sequence and affinity of the LHS of each TK_EQ ** expression to the parent key column defaults. */ if ( pIdx != null ) { Column pCol; iCol = pIdx.aiColumn[i]; pCol = pTab.aCol[iCol]; if ( pTab.iPKey == iCol ) iCol = -1; pLeft.iTable = regData + iCol + 1; pLeft.affinity = pCol.affinity; pLeft.pColl = sqlite3LocateCollSeq( pParse, pCol.zColl ); } else { pLeft.iTable = regData; pLeft.affinity = SQLITE_AFF_INTEGER; } } iCol = aiCol != null ? aiCol[i] : pFKey.aCol[0].iFrom; Debug.Assert( iCol >= 0 ); zCol = pFKey.pFrom.aCol[iCol].zName; pRight = sqlite3Expr( db, TK_ID, zCol ); pEq = sqlite3PExpr( pParse, TK_EQ, pLeft, pRight, 0 ); pWhere = sqlite3ExprAnd( db, pWhere, pEq ); } /* If the child table is the same as the parent table, and this scan ** is taking place as part of a DELETE operation (operation D.2), omit the ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE ** clause, where $rowid is the rowid of the row being deleted. */ if ( pTab == pFKey.pFrom && nIncr > 0 ) { Expr pEq; /* Expression (pLeft = pRight) */ Expr pLeft; /* Value from parent table row */ Expr pRight; /* Column ref to child table */ pLeft = sqlite3Expr( db, TK_REGISTER, null ); pRight = sqlite3Expr( db, TK_COLUMN, null ); if ( pLeft != null && pRight != null ) { pLeft.iTable = regData; pLeft.affinity = SQLITE_AFF_INTEGER; pRight.iTable = pSrc.a[0].iCursor; pRight.iColumn = -1; } pEq = sqlite3PExpr( pParse, TK_NE, pLeft, pRight, 0 ); pWhere = sqlite3ExprAnd( db, pWhere, pEq ); } /* Resolve the references in the WHERE clause. */ sNameContext = new NameContext();// memset( &sNameContext, 0, sizeof( NameContext ) ); sNameContext.pSrcList = pSrc; sNameContext.pParse = pParse; sqlite3ResolveExprNames( sNameContext, ref pWhere ); /* Create VDBE to loop through the entries in pSrc that match the WHERE ** clause. If the constraint is not deferred, throw an exception for ** each row found. Otherwise, for deferred constraints, increment the ** deferred constraint counter by nIncr for each row selected. */ ExprList elDummy = null; pWInfo = sqlite3WhereBegin( pParse, pSrc, pWhere, ref elDummy, 0 ); if ( nIncr > 0 && pFKey.isDeferred == 0 ) { sqlite3ParseToplevel( pParse ).mayAbort = 1; } sqlite3VdbeAddOp2( v, OP_FkCounter, pFKey.isDeferred, nIncr ); if ( pWInfo != null ) { sqlite3WhereEnd( pWInfo ); } /* Clean up the WHERE clause constructed above. */ sqlite3ExprDelete( db, ref pWhere ); if ( iFkIfZero != 0 ) { sqlite3VdbeJumpHere( v, iFkIfZero ); } }
/* ** This routine walks an expression tree and resolves references to ** table columns and result-set columns. At the same time, do error ** checking on function usage and set a flag if any aggregate functions ** are seen. ** ** To resolve table columns references we look for nodes (or subtrees) of the ** form X.Y.Z or Y.Z or just Z where ** ** X: The name of a database. Ex: "main" or "temp" or ** the symbolic name assigned to an ATTACH-ed database. ** ** Y: The name of a table in a FROM clause. Or in a trigger ** one of the special names "old" or "new". ** ** Z: The name of a column in table Y. ** ** The node at the root of the subtree is modified as follows: ** ** Expr.op Changed to TK_COLUMN ** Expr.pTab Points to the Table object for X.Y ** Expr.iColumn The column index in X.Y. -1 for the rowid. ** Expr.iTable The VDBE cursor number for X.Y ** ** ** To resolve result-set references, look for expression nodes of the ** form Z (with no X and Y prefix) where the Z matches the right-hand ** size of an AS clause in the result-set of a SELECT. The Z expression ** is replaced by a copy of the left-hand side of the result-set expression. ** Table-name and function resolution occurs on the substituted expression ** tree. For example, in: ** ** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY x; ** ** The "x" term of the order by is replaced by "a+b" to render: ** ** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY a+b; ** ** Function calls are checked to make sure that the function is ** defined and that the correct number of arguments are specified. ** If the function is an aggregate function, then the pNC.hasAgg is ** set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION. ** If an expression contains aggregate functions then the EP_Agg ** property on the expression is set. ** ** An error message is left in pParse if anything is amiss. The number ** if errors is returned. */ static int sqlite3ResolveExprNames( NameContext pNC, /* Namespace to resolve expressions in. */ ref Expr pExpr /* The expression to be analyzed. */ ) { u8 savedHasAgg; Walker w = new Walker(); if ( pExpr == null ) return 0; #if SQLITE_MAX_EXPR_DEPTH//>0 { Parse pParse = pNC.pParse; if( sqlite3ExprCheckHeight(pParse, pExpr.nHeight+pNC.pParse.nHeight) ){ return 1; } pParse.nHeight += pExpr.nHeight; } #endif savedHasAgg = pNC.hasAgg; pNC.hasAgg = 0; w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.pParse = pNC.pParse; w.u.pNC = pNC; sqlite3WalkExpr( w, ref pExpr ); #if SQLITE_MAX_EXPR_DEPTH//>0 pNC.pParse.nHeight -= pExpr.nHeight; #endif if ( pNC.nErr > 0 || w.pParse.nErr > 0 ) { ExprSetProperty( pExpr, EP_Error ); } if ( pNC.hasAgg != 0 ) { ExprSetProperty( pExpr, EP_Agg ); } else if ( savedHasAgg != 0 ) { pNC.hasAgg = 1; } return ExprHasProperty( pExpr, EP_Error ) ? 1 : 0; }
static string ColumnType(NameContext nc, Expr expr, ref string originDbNameOut, ref string originTableNameOut, ref string originColumnNameOut) { string typeName = null; string originDbName = null; string originTableName = null; string originColumnName = null; int j; if (C._NEVER(expr == null) || nc.SrcList == null) return null; switch (expr.OP) { case TK.AGG_COLUMN: case TK.COLUMN: { // The expression is a column. Locate the table the column is being extracted from in NameContext.pSrcList. This table may be real // database table or a subquery. Table table = null; // Table structure column is extracted from Select s = null; // Select the column is extracted from int colId = expr.ColumnIdx; // Index of column in pTab C.ASSERTCOVERAGE(expr.OP == TK.AGG_COLUMN); C.ASSERTCOVERAGE(expr.OP == TK.COLUMN); while (nc != null && table == null) { SrcList tabList = nc.SrcList; for (j = 0; j < tabList.Srcs && tabList.Ids[j].Cursor != expr.TableId; j++) ; if (j < tabList.Srcs) { table = tabList.Ids[j].Table; s = tabList.Ids[j].Select; } else nc = nc.Next; } if (table == null) { // At one time, code such as "SELECT new.x" within a trigger would cause this condition to run. Since then, we have restructured how // trigger code is generated and so this condition is no longer possible. However, it can still be true for statements like // the following: // // CREATE TABLE t1(col INTEGER); // SELECT (SELECT t1.col) FROM FROM t1; // // when columnType() is called on the expression "t1.col" in the sub-select. In this case, set the column type to NULL, even // though it should really be "INTEGER". // // This is not a problem, as the column type of "t1.col" is never used. When columnType() is called on the expression // "(SELECT t1.col)", the correct type is returned (see the TK_SELECT branch below. break; } Debug.Assert(table != null && expr.Table == table); if (s != null) { // The "table" is actually a sub-select or a view in the FROM clause of the SELECT statement. Return the declaration type and origin // data for the result-set column of the sub-select. if (colId >= 0 && C._ALWAYS(colId < s.EList.Exprs)) { // If colId is less than zero, then the expression requests the rowid of the sub-select or view. This expression is legal (see // test case misc2.2.2) - it always evaluates to NULL. NameContext sNC = new NameContext(); Expr p = s.EList.Ids[colId].Expr; sNC.SrcList = s.Src; sNC.Next = nc; sNC.Parse = nc.Parse; typeName = ColumnType(sNC, p, ref originDbName, ref originTableName, ref originColumnName); } } else if (C._ALWAYS(table.Schema)) { // A real table Debug.Assert(s == null); if (colId < 0) colId = table.PKey; Debug.Assert(colId == -1 || (colId >= 0 && colId < table.Cols.length)); if (colId < 0) { typeName = "INTEGER"; originColumnName = "rowid"; } else { typeName = table.Cols[colId].Type; originColumnName = table.Cols[colId].Name; } originTableName = table.Name; if (nc.Parse != null) { Context ctx = nc.Parse.Ctx; int db = Prepare.SchemaToIndex(ctx, table.Schema); originDbName = ctx.DBs[db].Name; } } break; } #if !OMIT_SUBQUERY case TK.SELECT: { // The expression is a sub-select. Return the declaration type and origin info for the single column in the result set of the SELECT statement. NameContext sNC = new NameContext(); Select s = expr.x.Select; Expr p = s.EList.Ids[0].Expr; Debug.Assert(E.ExprHasProperty(expr, EP.xIsSelect)); sNC.SrcList = s.Src; sNC.Next = nc; sNC.Parse = nc.Parse; typeName = ColumnType(sNC, p, ref originDbName, ref originTableName, ref originColumnName); break; } #endif } originDbNameOut = originDbName; originTableNameOut = originTableName; originColumnNameOut = originColumnName; return typeName; }
/* ** 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 */ int oldIdx = -1; /* VdbeCursor for the OLD table of AFTER triggers */ 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 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 */ 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; 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); /* Allocate a cursor used to store the old.* data for a trigger. */ if (pTrigger != null) { oldIdx = pParse.nTab++; } /* 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, pTrigger != null ? 1 : 0, iDb); #if !SQLITE_OMIT_TRIGGER if (pTrigger != null) { int orconf = ((pParse.trigStack != null) ? pParse.trigStack.orconf : OE_Default); int iGoto = sqlite3VdbeAddOp0(v, OP_Goto); addr = sqlite3VdbeMakeLabel(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); u32 Ref_0 = 0; sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, null, TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, ref old_col_mask, ref Ref_0); iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); Ref_0 = 0; sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, null, TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, ref old_col_mask, ref Ref_0); iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, iGoto); } #endif /* 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); } /* 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; } #endif /* 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. Note, however, that ** this means that the row change count will be incorrect. */ if (rcauth == SQLITE_OK && pWhere == null && null == pTrigger && !IsVirtual(pTab)) { 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 iRowid = ++pParse.nMem; /* Used for storing rowid values. */ int iRowSet = ++pParse.nMem; /* Register for rowset of rows to delete */ 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, false); sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); if ((db.flags & SQLITE_CountRows) != 0) { sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } sqlite3WhereEnd(pWInfo); /* Open the pseudo-table used to store OLD if there are triggers. */ if (pTrigger != null) { sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab.nCol); } /* 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); if (!isView) { /* Open cursors for the table we are deleting from and ** all its indices. */ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); } /* This is the beginning of the delete loop. If a trigger encounters ** an IGNORE constraint, it jumps back to here. */ if (pTrigger != null) { sqlite3VdbeResolveLabel(v, addr); } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); if (pTrigger != null) { int iData = ++pParse.nMem; /* For storing row data of OLD table */ /* If the record is no longer present in the table, jump to the ** next iteration of the loop through the contents of the fifo. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid); /* Populate the OLD.* pseudo-table */ if (old_col_mask != 0) { sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData); } else { sqlite3VdbeAddOp2(v, OP_Null, 0, iData); } sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid); /* Jump back and run the BEFORE triggers */ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); sqlite3VdbeJumpHere(v, iEndBeforeTrigger); } if (!isView) { /* 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); } else #endif { sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, pParse.nested == 0 ? 1 : 0); } } /* If there are row triggers, close all cursors then invoke ** the AFTER triggers */ if (pTrigger != null) { /* Jump back and run the AFTER triggers */ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger); } /* End of the delete loop */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeResolveLabel(v, end); /* Close the cursors after the loop if there are no row triggers */ 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.trigStack == 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 && pParse.nested == 0 && pParse.trigStack == null) { 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; }
/* ** Call sqlite3ExprAnalyzeAggregates() for every expression in an ** expression list. Return the number of errors. ** ** If an error is found, the analysis is cut short. */ static void sqlite3ExprAnalyzeAggList( NameContext pNC, ExprList pList ) { ExprList_item pItem; int i; if ( pList != null ) { for ( i = 0; i < pList.nExpr; i++ )//, pItem++) { pItem = pList.a[i]; sqlite3ExprAnalyzeAggregates( pNC, ref pItem.pExpr ); } } }
static void sqlite3EndTable( Parse pParse, /* Parse context */ Token pCons, /* The ',' token after the last column defn. */ Token pEnd, /* The final ')' token in the CREATE TABLE */ Select pSelect /* Select from a "CREATE ... AS SELECT" */ ) { Table p; sqlite3 db = pParse.db; int iDb; if ((pEnd == null && pSelect == null) /*|| db.mallocFailed != 0 */ ) { return; } p = pParse.pNewTable; if (p == null) return; Debug.Assert(0 == db.init.busy || pSelect == null); iDb = sqlite3SchemaToIndex(db, p.pSchema); #if !SQLITE_OMIT_CHECK /* Resolve names in all CHECK constraint expressions. */ if (p.pCheck != null) { SrcList sSrc; /* Fake SrcList for pParse.pNewTable */ NameContext sNC; /* Name context for pParse.pNewTable */ sNC = new NameContext();// memset(sNC, 0, sizeof(sNC)); sSrc = new SrcList();// memset(sSrc, 0, sizeof(sSrc)); sSrc.nSrc = 1; sSrc.a = new SrcList_item[1]; sSrc.a[0] = new SrcList_item(); sSrc.a[0].zName = p.zName; sSrc.a[0].pTab = p; sSrc.a[0].iCursor = -1; sNC.pParse = pParse; sNC.pSrcList = sSrc; sNC.isCheck = 1; if (sqlite3ResolveExprNames(sNC, ref p.pCheck) != 0) { return; } } #endif // * !SQLITE_OMIT_CHECK) */ /* If the db.init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number ** for the table from the db.init.newTnum field. (The page number ** should have been put there by the sqliteOpenCb routine.) */ if (db.init.busy != 0) { p.tnum = db.init.newTnum; } /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. ** ** If this is a TEMPORARY table, write the entry into the auxiliary ** file instead of into the main database file. */ if (0 == db.init.busy) { int n; Vdbe v; String zType = ""; /* "view" or "table" */ String zType2 = ""; /* "VIEW" or "TABLE" */ String zStmt = ""; /* Text of the CREATE TABLE or CREATE VIEW statement */ v = sqlite3GetVdbe(pParse); if (NEVER(v == null)) return; sqlite3VdbeAddOp1(v, OP_Close, 0); /* ** Initialize zType for the new view or table. */ if (p.pSelect == null) { /* A regular table */ zType = "table"; zType2 = "TABLE"; #if !SQLITE_OMIT_VIEW } else { /* A view */ zType = "view"; zType2 = "VIEW"; #endif } /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT ** statement to populate the new table. The root-page number for the ** new table is in register pParse->regRoot. ** ** Once the SELECT has been coded by sqlite3Select(), it is in a ** suitable state to query for the column names and types to be used ** by the new table. ** ** A shared-cache write-lock is not required to write to the new table, ** as a schema-lock must have already been obtained to create it. Since ** a schema-lock excludes all other database users, the write-lock would ** be redundant. */ if (pSelect != null) { SelectDest dest = new SelectDest(); Table pSelTab; Debug.Assert(pParse.nTab == 1); sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse.regRoot, iDb); sqlite3VdbeChangeP5(v, 1); pParse.nTab = 2; sqlite3SelectDestInit(dest, SRT_Table, 1); sqlite3Select(pParse, pSelect, ref dest); sqlite3VdbeAddOp1(v, OP_Close, 1); if (pParse.nErr == 0) { pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect); if (pSelTab == null) return; Debug.Assert(p.aCol == null); p.nCol = pSelTab.nCol; p.aCol = pSelTab.aCol; pSelTab.nCol = 0; pSelTab.aCol = null; sqlite3DeleteTable(db, ref pSelTab); } } /* Compute the complete text of the CREATE statement */ if (pSelect != null) { zStmt = createTableStmt(db, p); } else { n = (int)(pParse.sNameToken.z.Length - pEnd.z.Length) + 1; zStmt = sqlite3MPrintf(db, "CREATE %s %.*s", zType2, n, pParse.sNameToken.z ); } /* A slot for the record has already been allocated in the ** SQLITE_MASTER table. We just need to update that slot with all ** the information we've collected. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s " + "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " + "WHERE rowid=#%d", db.aDb[iDb].zName, SCHEMA_TABLE(iDb), zType, p.zName, p.zName, pParse.regRoot, zStmt, pParse.regRowid ); sqlite3DbFree(db, ref zStmt); sqlite3ChangeCookie(pParse, iDb); #if !SQLITE_OMIT_AUTOINCREMENT /* Check to see if we need to create an sqlite_sequence table for ** keeping track of autoincrement keys. */ if ((p.tabFlags & TF_Autoincrement) != 0) { Db pDb = db.aDb[iDb]; Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null)); if (pDb.pSchema.pSeqTab == null) { sqlite3NestedParse(pParse, "CREATE TABLE %Q.sqlite_sequence(name,seq)", pDb.zName ); } } #endif /* Reparse everything to update our internal data structures */ sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "tbl_name='%q'", p.zName)); } /* Add the table to the in-memory representation of the database. */ if (db.init.busy != 0) { Table pOld; Schema pSchema = p.pSchema; Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null)); pOld = sqlite3HashInsert(ref pSchema.tblHash, p.zName, sqlite3Strlen30(p.zName), p); if (pOld != null) { Debug.Assert(p == pOld); /* Malloc must have failed inside HashInsert() */ // db.mallocFailed = 1; return; } pParse.pNewTable = null; db.nTable++; db.flags |= SQLITE_InternChanges; #if !SQLITE_OMIT_ALTERTABLE if (p.pSelect == null) { string zName = pParse.sNameToken.z; int nName; Debug.Assert(pSelect == null && pCons != null && pEnd != null); if (pCons.z == null) { pCons = pEnd; } nName = zName.Length - pCons.z.Length; p.addColOffset = 13 + nName; // sqlite3Utf8CharLen(zName, nName); } #endif } }
/* ** pE is a pointer to an expression which is a single term in the ** ORDER BY of a compound SELECT. The expression has not been ** name resolved. ** ** At the point this routine is called, we already know that the ** ORDER BY term is not an integer index into the result set. That ** case is handled by the calling routine. ** ** Attempt to match pE against result set columns in the left-most ** SELECT statement. Return the index i of the matching column, ** as an indication to the caller that it should sort by the i-th column. ** The left-most column is 1. In other words, the value returned is the ** same integer value that would be used in the SQL statement to indicate ** the column. ** ** If there is no match, return 0. Return -1 if an error occurs. */ static int resolveOrderByTermToExprList( Parse pParse, /* Parsing context for error messages */ Select pSelect, /* The SELECT statement with the ORDER BY clause */ Expr pE /* The specific ORDER BY term */ ) { int i = 0; /* Loop counter */ ExprList pEList; /* The columns of the result set */ NameContext nc; /* Name context for resolving pE */ sqlite3 db; /* Database connection */ int rc; /* Return code from subprocedures */ u8 savedSuppErr; /* Saved value of db->suppressErr */ Debug.Assert( sqlite3ExprIsInteger( pE, ref i ) == 0 ); pEList = pSelect.pEList; /* Resolve all names in the ORDER BY term expression */ nc = new NameContext();// memset( &nc, 0, sizeof( nc ) ); nc.pParse = pParse; nc.pSrcList = pSelect.pSrc; nc.pEList = pEList; nc.allowAgg = 1; nc.nErr = 0; db = pParse.db; savedSuppErr = db.suppressErr; db.suppressErr = 1; rc = sqlite3ResolveExprNames( nc, ref pE ); db.suppressErr = savedSuppErr; if ( rc != 0 ) return 0; /* Try to match the ORDER BY expression against an expression ** in the result set. Return an 1-based index of the matching ** result-set entry. */ for ( i = 0; i < pEList.nExpr; i++ ) { if ( sqlite3ExprCompare( pEList.a[i].pExpr, pE ) < 2 ) { return i + 1; } } /* If no match, return 0. */ return 0; }
/// <summary> /// Resolve all names in all expressions of a SELECT and in all /// decendents of the SELECT, including compounds off of p.pPrior, /// subqueries in expressions, and subqueries used as FROM clause /// terms. /// /// See sqlite3ResolveExprNames() for a description of the kinds of /// transformations that occur. /// /// All SELECT statements should have been expanded using /// sqlite3SelectExpand() prior to invoking this routine. /// </summary> /// <param name='pParse'> /// The parser context /// </param> /// <param name='p'> /// The SELECT statement being coded. /// </param> /// <param name='pOuterNC'> /// Name context for parent SELECT statement /// </param> static void sqlite3ResolveSelectNames(Parse pParse, Select p, NameContext pOuterNC) { Walker w = new Walker(); Debug.Assert(p != null); w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.pParse = pParse; w.u.pNC = pOuterNC; sqlite3WalkSelect(w, p); }
/* ** Return a pointer to a string containing the 'declaration type' of the ** expression pExpr. The string may be treated as static by the caller. ** ** The declaration type is the exact datatype definition extracted from the ** original CREATE TABLE statement if the expression is a column. The ** declaration type for a ROWID field is INTEGER. Exactly when an expression ** is considered a column can be complex in the presence of subqueries. The ** result-set expression in all of the following SELECT statements is ** considered a column by this function. ** ** SELECT col FROM tbl; ** SELECT (SELECT col FROM tbl; ** SELECT (SELECT col FROM tbl); ** SELECT abc FROM (SELECT col AS abc FROM tbl); ** ** The declaration type for any expression other than a column is NULL. */ static string columnType( NameContext pNC, Expr pExpr, ref string pzOriginDb, ref string pzOriginTab, ref string pzOriginCol ) { string zType = null; string zOriginDb = null; string zOriginTab = null; string zOriginCol = null; int j; if ( NEVER( pExpr == null ) || pNC.pSrcList == null ) return null; switch ( pExpr.op ) { case TK_AGG_COLUMN: case TK_COLUMN: { /* The expression is a column. Locate the table the column is being ** extracted from in NameContext.pSrcList. This table may be real ** database table or a subquery. */ Table pTab = null; /* Table structure column is extracted from */ Select pS = null; /* Select the column is extracted from */ int iCol = pExpr.iColumn; /* Index of column in pTab */ testcase( pExpr.op == TK_AGG_COLUMN ); testcase( pExpr.op == TK_COLUMN ); while ( pNC != null && pTab == null ) { SrcList pTabList = pNC.pSrcList; for ( j = 0 ; j < pTabList.nSrc && pTabList.a[j].iCursor != pExpr.iTable ; j++ ) ; if ( j < pTabList.nSrc ) { pTab = pTabList.a[j].pTab; pS = pTabList.a[j].pSelect; } else { pNC = pNC.pNext; } } if ( pTab == null ) { /* FIX ME: ** This can occurs if you have something like "SELECT new.x;" inside ** a trigger. In other words, if you reference the special "new" ** table in the result set of a select. We do not have a good way ** to find the actual table type, so call it "TEXT". This is really ** something of a bug, but I do not know how to fix it. ** ** This code does not produce the correct answer - it just prevents ** a segfault. See ticket #1229. */ zType = "TEXT"; break; } Debug.Assert( pTab != null ); if ( pS != null ) { /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ if ( ALWAYS( iCol >= 0 && iCol < pS.pEList.nExpr ) ) { /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. */ NameContext sNC = new NameContext(); Expr p = pS.pEList.a[iCol].pExpr; sNC.pSrcList = pS.pSrc; sNC.pNext = null; sNC.pParse = pNC.pParse; zType = columnType( sNC, p, ref zOriginDb, ref zOriginTab, ref zOriginCol ); } } else if ( ALWAYS( pTab.pSchema ) ) { /* A real table */ Debug.Assert( pS == null ); if ( iCol < 0 ) iCol = pTab.iPKey; Debug.Assert( iCol == -1 || ( iCol >= 0 && iCol < pTab.nCol ) ); if ( iCol < 0 ) { zType = "INTEGER"; zOriginCol = "rowid"; } else { zType = pTab.aCol[iCol].zType; zOriginCol = pTab.aCol[iCol].zName; } zOriginTab = pTab.zName; if ( pNC.pParse != null ) { int iDb = sqlite3SchemaToIndex( pNC.pParse.db, pTab.pSchema ); zOriginDb = pNC.pParse.db.aDb[iDb].zName; } } break; } #if !SQLITE_OMIT_SUBQUERY case TK_SELECT: { /* The expression is a sub-select. Return the declaration type and ** origin info for the single column in the result set of the SELECT ** statement. */ NameContext sNC = new NameContext(); Select pS = pExpr.x.pSelect; Expr p = pS.pEList.a[0].pExpr; Debug.Assert( ExprHasProperty( pExpr, EP_xIsSelect ) ); sNC.pSrcList = pS.pSrc; sNC.pNext = pNC; sNC.pParse = pNC.pParse; zType = columnType( sNC, p, ref zOriginDb, ref zOriginTab, ref zOriginCol ); break; } #endif } if ( pzOriginDb != null ) { Debug.Assert( pzOriginTab != null && pzOriginCol != null ); pzOriginDb = zOriginDb; pzOriginTab = zOriginTab; pzOriginCol = zOriginCol; } return zType; }
/* ** 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; }
/* ** Return a pointer to a string containing the 'declaration type' of the ** expression pExpr. The string may be treated as static by the caller. ** ** The declaration type is the exact datatype definition extracted from the ** original CREATE TABLE statement if the expression is a column. The ** declaration type for a ROWID field is INTEGER. Exactly when an expression ** is considered a column can be complex in the presence of subqueries. The ** result-set expression in all of the following SELECT statements is ** considered a column by this function. ** ** SELECT col FROM tbl; ** SELECT (SELECT col FROM tbl; ** SELECT (SELECT col FROM tbl); ** SELECT abc FROM (SELECT col AS abc FROM tbl); ** ** The declaration type for any expression other than a column is NULL. */ static string columnType( NameContext pNC, Expr pExpr, ref string pzOriginDb, ref string pzOriginTab, ref string pzOriginCol ) { string zType = null; string zOriginDb = null; string zOriginTab = null; string zOriginCol = null; int j; if ( NEVER( pExpr == null ) || pNC.pSrcList == null ) return null; switch ( pExpr.op ) { case TK_AGG_COLUMN: case TK_COLUMN: { /* The expression is a column. Locate the table the column is being ** extracted from in NameContext.pSrcList. This table may be real ** database table or a subquery. */ Table pTab = null; /* Table structure column is extracted from */ Select pS = null; /* Select the column is extracted from */ int iCol = pExpr.iColumn; /* Index of column in pTab */ testcase( pExpr.op == TK_AGG_COLUMN ); testcase( pExpr.op == TK_COLUMN ); while ( pNC != null && pTab == null ) { SrcList pTabList = pNC.pSrcList; for ( j = 0; j < pTabList.nSrc && pTabList.a[j].iCursor != pExpr.iTable; j++ ) ; if ( j < pTabList.nSrc ) { pTab = pTabList.a[j].pTab; pS = pTabList.a[j].pSelect; } else { pNC = pNC.pNext; } } if ( pTab == null ) { /* At one time, code such as "SELECT new.x" within a trigger would ** cause this condition to run. Since then, we have restructured how ** trigger code is generated and so this condition is no longer ** possible. However, it can still be true for statements like ** the following: ** ** CREATE TABLE t1(col INTEGER); ** SELECT (SELECT t1.col) FROM FROM t1; ** ** when columnType() is called on the expression "t1.col" in the ** sub-select. In this case, set the column type to NULL, even ** though it should really be "INTEGER". ** ** This is not a problem, as the column type of "t1.col" is never ** used. When columnType() is called on the expression ** "(SELECT t1.col)", the correct type is returned (see the TK_SELECT ** branch below. */ break; } //Debug.Assert( pTab != null && pExpr.pTab == pTab ); if ( pS != null ) { /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ if ( iCol >= 0 && ALWAYS( iCol < pS.pEList.nExpr ) ) { /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. */ NameContext sNC = new NameContext(); Expr p = pS.pEList.a[iCol].pExpr; sNC.pSrcList = pS.pSrc; sNC.pNext = pNC; sNC.pParse = pNC.pParse; zType = columnType( sNC, p, ref zOriginDb, ref zOriginTab, ref zOriginCol ); } } else if ( ALWAYS( pTab.pSchema ) ) { /* A real table */ Debug.Assert( pS == null ); if ( iCol < 0 ) iCol = pTab.iPKey; Debug.Assert( iCol == -1 || ( iCol >= 0 && iCol < pTab.nCol ) ); if ( iCol < 0 ) { zType = "INTEGER"; zOriginCol = "rowid"; } else { zType = pTab.aCol[iCol].zType; zOriginCol = pTab.aCol[iCol].zName; } zOriginTab = pTab.zName; if ( pNC.pParse != null ) { int iDb = sqlite3SchemaToIndex( pNC.pParse.db, pTab.pSchema ); zOriginDb = pNC.pParse.db.aDb[iDb].zName; } } break; } #if !SQLITE_OMIT_SUBQUERY case TK_SELECT: { /* The expression is a sub-select. Return the declaration type and ** origin info for the single column in the result set of the SELECT ** statement. */ NameContext sNC = new NameContext(); Select pS = pExpr.x.pSelect; Expr p = pS.pEList.a[0].pExpr; Debug.Assert( ExprHasProperty( pExpr, EP_xIsSelect ) ); sNC.pSrcList = pS.pSrc; sNC.pNext = pNC; sNC.pParse = pNC.pParse; zType = columnType( sNC, p, ref zOriginDb, ref zOriginTab, ref zOriginCol ); break; } #endif } //if ( pzOriginDb != null ) { //Debug.Assert( pzOriginTab != null && pzOriginCol != null ); pzOriginDb = zOriginDb; pzOriginTab = zOriginTab; pzOriginCol = zOriginCol; } return zType; }
/* ** Analyze the given expression looking for aggregate functions and ** for variables that need to be added to the pParse.aAgg[] array. ** Make additional entries to the pParse.aAgg[] array as necessary. ** ** This routine should only be called after the expression has been ** analyzed by sqlite3ResolveExprNames(). */ static void sqlite3ExprAnalyzeAggregates( NameContext pNC, ref Expr pExpr ) { Walker w = new Walker(); w.xExprCallback = (dxExprCallback)analyzeAggregate; w.xSelectCallback = (dxSelectCallback)analyzeAggregatesInSelect; w.u.pNC = pNC; Debug.Assert( pNC.pSrcList != null ); sqlite3WalkExpr( w, ref pExpr ); }
/* ** Add type and collation information to a column list based on ** a SELECT statement. ** ** The column list presumably came from selectColumnNamesFromExprList(). ** The column list has only names, not types or collations. This ** routine goes through and adds the types and collations. ** ** This routine requires that all identifiers in the SELECT ** statement be resolved. */ static void selectAddColumnTypeAndCollation( Parse pParse, /* Parsing contexts */ int nCol, /* Number of columns */ Column[] aCol, /* List of columns */ Select pSelect /* SELECT used to determine types and collations */ ) { ////sqlite3 db = pParse.db; NameContext sNC; Column pCol; CollSeq pColl; int i; Expr p; ExprList_item[] a; Debug.Assert( pSelect != null ); Debug.Assert( ( pSelect.selFlags & SF_Resolved ) != 0 ); Debug.Assert( nCol == pSelect.pEList.nExpr /*|| db.mallocFailed != 0 */ ); // if ( db.mallocFailed != 0 ) return; sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); sNC.pSrcList = pSelect.pSrc; a = pSelect.pEList.a; for ( i = 0; i < nCol; i++ )//, pCol++ ) { pCol = aCol[i]; p = a[i].pExpr; string bDummy = null; pCol.zType = columnType( sNC, p, ref bDummy, ref bDummy, ref bDummy );// sqlite3DbStrDup( db, columnType( sNC, p, 0, 0, 0 ) ); pCol.affinity = sqlite3ExprAffinity( p ); if ( pCol.affinity == 0 ) pCol.affinity = SQLITE_AFF_NONE; pColl = sqlite3ExprCollSeq( pParse, p ); if ( pColl != null ) { pCol.zColl = pColl.zName;// sqlite3DbStrDup( db, pColl.zName ); } } }
static void SelectAddColumnTypeAndCollation(Parse parse, int colsLength, Column[] cols, Select select) { Context ctx = parse.Ctx; Debug.Assert(select != null); Debug.Assert((select.SelFlags & SF.Resolved) != 0); Debug.Assert(colsLength == select.EList.Exprs || ctx.MallocFailed); if (ctx.MallocFailed) return; NameContext sNC = new NameContext(); sNC.SrcList = select.Src; ExprList.ExprListItem[] ids = select.EList.Ids; int i; Column col; for (i = 0; i < colsLength; i++) { col = cols[i]; Expr p = ids[i].Expr; string dummy1 = null; col.Type = ColumnType(sNC, p, ref dummy1, ref dummy1, ref dummy1); col.Affinity = p.Affinity(); if (col.Affinity == 0) col.Affinity = AFF.NONE; CollSeq coll = p.CollSeq(parse); if (coll != null) col.Coll = coll.Name; } }
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; }
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 }
/* ** 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; }
protected virtual void RenderName(HtmlTextWriter output, string id, NameContext context) { Assert.ArgumentNotNull(output, "output"); Assert.ArgumentNotNullOrEmpty(id, "id"); if (!context.Editable) { output.Write("<span class='header-title'>"); output.Write(context.Name); output.Write("</span>"); } else { output.Write("<a href='#' class='header-title'>"); output.Write(StringUtil.EscapeQuote(context.Name)); output.Write("</a>"); string str = "onkeydown='javascript:return Sitecore.CollapsiblePanel.editNameChanging(this, event);'"; string str2 = string.IsNullOrEmpty(context.OnNameChanging) ? string.Empty : ("onkeyup=\"" + context.OnNameChanging + "\""); string str3 = string.IsNullOrEmpty(context.OnNameChanged) ? string.Empty : ("onchange=\"" + context.OnNameChanged + "\""); output.Write( "<input type='text' {0} {1} id='{2}_name' name='{2}_name' data-meta-id='{2}' data-validation-msg=\"{3}\" style='display:none' class='header-title-edit' value=\"{4}\" {5} />", new object[] {str2, str3, id, Translate.Text("The name cannot be blank."), StringUtil.EscapeQuote(context.Name), str}); } }
/* ** This procedure generates VDBE code for a single invocation of either the ** sqlite_detach() or sqlite_attach() SQL user functions. */ static void codeAttach( Parse pParse, /* The parser context */ int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ FuncDef pFunc, /* FuncDef wrapper for detachFunc() or attachFunc() */ Expr pAuthArg, /* Expression to pass to authorization callback */ Expr pFilename, /* Name of database file */ Expr pDbname, /* Name of the database to use internally */ Expr pKey /* Database key for encryption extension */ ) { int rc; NameContext sName; Vdbe v; sqlite3 db = pParse.db; int regArgs; sName = new NameContext();// memset( &sName, 0, sizeof(NameContext)); sName.pParse = pParse; if ( SQLITE_OK != ( rc = resolveAttachExpr( sName, pFilename ) ) || SQLITE_OK != ( rc = resolveAttachExpr( sName, pDbname ) ) || SQLITE_OK != ( rc = resolveAttachExpr( sName, pKey ) ) ) { pParse.nErr++; goto attach_end; } #if !SQLITE_OMIT_AUTHORIZATION if( pAuthArg ){ char *zAuthArg; if( pAuthArg->op==TK_STRING ){ zAuthArg = pAuthArg->u.zToken; }else{ zAuthArg = 0; } rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); if(rc!=SQLITE_OK ){ goto attach_end; } } #endif //* SQLITE_OMIT_AUTHORIZATION */ v = sqlite3GetVdbe( pParse ); regArgs = sqlite3GetTempRange( pParse, 4 ); sqlite3ExprCode( pParse, pFilename, regArgs ); sqlite3ExprCode( pParse, pDbname, regArgs + 1 ); sqlite3ExprCode( pParse, pKey, regArgs + 2 ); Debug.Assert( v != null /*|| db.mallocFailed != 0 */ ); if ( v != null ) { sqlite3VdbeAddOp3( v, OP_Function, 0, regArgs + 3 - pFunc.nArg, regArgs + 3 ); Debug.Assert( pFunc.nArg == -1 || ( pFunc.nArg & 0xff ) == pFunc.nArg ); sqlite3VdbeChangeP5( v, (u8)( pFunc.nArg ) ); sqlite3VdbeChangeP4( v, -1, pFunc, P4_FUNCDEF ); /* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this ** statement only). For DETACH, set it to false (expire all existing ** statements). */ sqlite3VdbeAddOp1( v, OP_Expire, ( type == SQLITE_ATTACH ) ? 1 : 0 ); } attach_end: sqlite3ExprDelete( db, ref pFilename ); sqlite3ExprDelete( db, ref pDbname ); sqlite3ExprDelete( db, ref pKey ); }
/* ** 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 */ }
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); }
/* ** This routine sets of a SELECT statement for processing. The ** following is accomplished: ** ** * VDBE VdbeCursor numbers are assigned to all FROM-clause terms. ** * Ephemeral Table objects are created for all FROM-clause subqueries. ** * ON and USING clauses are shifted into WHERE statements ** * Wildcards "*" and "TABLE.*" in result sets are expanded. ** * Identifiers in expression are matched to tables. ** ** This routine acts recursively on all subqueries within the SELECT. */ static void sqlite3SelectPrep( Parse pParse, /* The parser context */ Select p, /* The SELECT statement being coded. */ NameContext pOuterNC /* Name context for container */ ) { if ( NEVER( p == null ) ) return; ////sqlite3 db = pParse.db; if ( ( p.selFlags & SF_HasTypeInfo ) != 0 ) return; sqlite3SelectExpand( pParse, p ); if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) return; sqlite3ResolveSelectNames( pParse, p, pOuterNC ); if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) return; sqlite3SelectAddTypeInfo( pParse, p ); }
static void sqlite3Insert( Parse pParse, /* Parser context */ SrcList pTabList, /* Name of table into which we are inserting */ ExprList pList, /* List of values to be inserted */ Select pSelect, /* A SELECT statement to use as the data source */ IdList pColumn, /* Column names corresponding to IDLIST. */ int onError /* How to handle constraint errors */ ) { sqlite3 db; /* The main database structure */ Table pTab; /* The table to insert into. aka TABLE */ string zTab; /* Name of the table into which we are inserting */ string zDb; /* Name of the database holding this table */ int i = 0; int j = 0; int idx = 0; /* Loop counters */ Vdbe v; /* Generate code into this virtual machine */ Index pIdx; /* For looping over indices of the table */ int nColumn; /* Number of columns in the data */ int nHidden = 0; /* Number of hidden columns if TABLE is virtual */ int baseCur = 0; /* VDBE VdbeCursor number for pTab */ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop = 0; /* Label for the end of the insertion loop */ bool useTempTable = false; /* Store SELECT results in intermediate table */ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ int addrInsTop = 0; /* Jump to label "D" */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ int iDb; /* Index of database holding TABLE */ Db pDb; /* The database containing table being inserted into */ bool appendFlag = false; /* True if the insert is likely to be an append */ /* Register allocations */ int regFromSelect = 0; /* Base register for data coming from SELECT */ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ int regRowCount = 0; /* Memory cell used for the row counter */ int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int regEof = 0; /* Register recording end of SELECT data */ int[] aRegIdx = null; /* One register allocated to each index */ #if !SQLITE_OMIT_TRIGGER bool isView = false; /* True if attempting to insert into a view */ Trigger pTrigger; /* List of triggers on pTab, if required */ int tmask = 0; /* Mask of trigger times */ #endif db = pParse.db; dest = new SelectDest();// memset( &dest, 0, sizeof( dest ) ); if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto insert_cleanup; } /* Locate the table into which we will be inserting new information. */ Debug.Assert( pTabList.nSrc == 1 ); zTab = pTabList.a[0].zName; if ( NEVER( zTab == null ) ) goto insert_cleanup; pTab = sqlite3SrcListLookup( pParse, pTabList ); if ( pTab == null ) { goto insert_cleanup; } iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); Debug.Assert( iDb < db.nDb ); pDb = db.aDb[iDb]; zDb = pDb.zName; #if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab.zName, 0, zDb) ){ goto insert_cleanup; } #endif /* Figure out if we have any triggers and if the table being ** inserted into is a view */ #if !SQLITE_OMIT_TRIGGER pTrigger = sqlite3TriggersExist( pParse, pTab, TK_INSERT, null, out tmask ); isView = pTab.pSelect != null; #else Trigger pTrigger = null; //# define pTrigger 0 int tmask = 0; //# define tmask 0 bool isView = false; #endif #if SQLITE_OMIT_VIEW //# undef isView isView = false; #endif #if !SQLITE_OMIT_TRIGGER Debug.Assert( ( pTrigger != null && tmask != 0 ) || ( pTrigger == null && tmask == 0 ) ); #endif #if !SQLITE_OMIT_VIEW /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual ** module table). */ if ( sqlite3ViewGetColumnNames( pParse, pTab ) != -0 ) { goto insert_cleanup; } #endif /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ if ( sqlite3IsReadOnly( pParse, pTab, tmask ) ) { goto insert_cleanup; } /* Allocate a VDBE */ v = sqlite3GetVdbe( pParse ); if ( v == null ) goto insert_cleanup; if ( pParse.nested == 0 ) sqlite3VdbeCountChanges( v ); sqlite3BeginWriteOperation( pParse, ( pSelect != null || pTrigger != null ) ? 1 : 0, iDb ); #if !SQLITE_OMIT_XFER_OPT /* If the statement is of the form ** ** INSERT INTO <table1> SELECT * FROM <table2>; ** ** Then special optimizations can be applied that make the transfer ** very fast and which reduce fragmentation of indices. ** ** This is the 2nd template. */ if ( pColumn == null && xferOptimization( pParse, pTab, pSelect, onError, iDb ) != 0 ) { Debug.Assert( null == pTrigger ); Debug.Assert( pList == null ); goto insert_end; } #endif // * SQLITE_OMIT_XFER_OPT */ /* If this is an AUTOINCREMENT table, look up the sequence number in the ** sqlite_sequence table and store it in memory cell regAutoinc. */ regAutoinc = autoIncBegin( pParse, iDb, pTab ); /* Figure out how many columns of data are supplied. If the data ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The ** co-routine is the common header to the 3rd and 4th templates. */ if ( pSelect != null ) { /* Data is coming from a SELECT. Generate code to implement that SELECT ** as a co-routine. The code is common to both the 3rd and 4th ** templates: ** ** EOF <- 0 ** X <- A ** goto B ** A: setup for the SELECT ** loop over the tables in the SELECT ** load value into register R..R+n ** yield X ** end loop ** cleanup after the SELECT ** EOF <- 1 ** yield X ** halt-error ** ** On each invocation of the co-routine, it puts a single row of the ** SELECT result into registers dest.iMem...dest.iMem+dest.nMem-1. ** (These output registers are allocated by sqlite3Select().) When ** the SELECT completes, it sets the EOF flag stored in regEof. */ int rc = 0, j1; regEof = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, regEof ); /* EOF <- 0 */ #if SQLITE_DEBUG VdbeComment( v, "SELECT eof flag" ); #endif sqlite3SelectDestInit( dest, SRT_Coroutine, ++pParse.nMem ); addrSelect = sqlite3VdbeCurrentAddr( v ) + 2; sqlite3VdbeAddOp2( v, OP_Integer, addrSelect - 1, dest.iParm ); j1 = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ); #if SQLITE_DEBUG VdbeComment( v, "Jump over SELECT coroutine" ); #endif /* Resolve the expressions in the SELECT statement and execute it. */ rc = sqlite3Select( pParse, pSelect, ref dest ); Debug.Assert( pParse.nErr == 0 || rc != 0 ); if ( rc != 0 || NEVER( pParse.nErr != 0 ) /*|| db.mallocFailed != 0 */ ) { goto insert_cleanup; } sqlite3VdbeAddOp2( v, OP_Integer, 1, regEof ); /* EOF <- 1 */ sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); /* yield X */ sqlite3VdbeAddOp2( v, OP_Halt, SQLITE_INTERNAL, OE_Abort ); #if SQLITE_DEBUG VdbeComment( v, "End of SELECT coroutine" ); #endif sqlite3VdbeJumpHere( v, j1 ); /* label B: */ regFromSelect = dest.iMem; Debug.Assert( pSelect.pEList != null ); nColumn = pSelect.pEList.nExpr; Debug.Assert( dest.nMem == nColumn ); /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to ** FALSE if each* row of the SELECT can be written directly into ** the destination table (template 3). ** ** A temp table must be used if the table being updated is also one ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ if ( pTrigger != null || readsTable( pParse, addrSelect, iDb, pTab ) ) { useTempTable = true; } if ( useTempTable ) { /* Invoke the coroutine to extract information from the SELECT ** and add it to a transient table srcTab. The code generated ** here is from the 4th template: ** ** B: open temp table ** L: yield X ** if EOF goto M ** insert row from R..R+n into temp table ** goto L ** M: ... */ int regRec; /* Register to hold packed record */ int regTempRowid; /* Register to hold temp table ROWID */ int addrTop; /* Label "L" */ int addrIf; /* Address of jump to M */ srcTab = pParse.nTab++; regRec = sqlite3GetTempReg( pParse ); regTempRowid = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp2( v, OP_OpenEphemeral, srcTab, nColumn ); addrTop = sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); addrIf = sqlite3VdbeAddOp1( v, OP_If, regEof ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regFromSelect, nColumn, regRec ); sqlite3VdbeAddOp2( v, OP_NewRowid, srcTab, regTempRowid ); sqlite3VdbeAddOp3( v, OP_Insert, srcTab, regRec, regTempRowid ); sqlite3VdbeAddOp2( v, OP_Goto, 0, addrTop ); sqlite3VdbeJumpHere( v, addrIf ); sqlite3ReleaseTempReg( pParse, regRec ); sqlite3ReleaseTempReg( pParse, regTempRowid ); } } else { /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ NameContext sNC; sNC = new NameContext();// memset( &sNC, 0, sNC ).Length; sNC.pParse = pParse; srcTab = -1; Debug.Assert( !useTempTable ); nColumn = pList != null ? pList.nExpr : 0; for ( i = 0; i < nColumn; i++ ) { if ( sqlite3ResolveExprNames( sNC, ref pList.a[i].pExpr ) != 0 ) { goto insert_cleanup; } } } /* Make sure the number of columns in the source data matches the number ** of columns to be inserted into the table. */ if ( IsVirtual( pTab ) ) { for ( i = 0; i < pTab.nCol; i++ ) { nHidden += ( IsHiddenColumn( pTab.aCol[i] ) ? 1 : 0 ); } } if ( pColumn == null && nColumn != 0 && nColumn != ( pTab.nCol - nHidden ) ) { sqlite3ErrorMsg( pParse, "table %S has %d columns but %d values were supplied", pTabList, 0, pTab.nCol - nHidden, nColumn ); goto insert_cleanup; } if ( pColumn != null && nColumn != pColumn.nId ) { sqlite3ErrorMsg( pParse, "%d values for %d columns", nColumn, pColumn.nId ); goto insert_cleanup; } /* If the INSERT statement included an IDLIST term, then make sure ** all elements of the IDLIST really are columns of the table and ** remember the column indices. ** ** If the table has an INTEGER PRIMARY KEY column and that column ** is named in the IDLIST, then record in the keyColumn variable ** the index into IDLIST of the primary key column. keyColumn is ** the index of the primary key as it appears in IDLIST, not as ** is appears in the original table. (The index of the primary ** key in the original table is pTab.iPKey.) */ if ( pColumn != null ) { for ( i = 0; i < pColumn.nId; i++ ) { pColumn.a[i].idx = -1; } for ( i = 0; i < pColumn.nId; i++ ) { for ( j = 0; j < pTab.nCol; j++ ) { if ( pColumn.a[i].zName.Equals( pTab.aCol[j].zName ,StringComparison.InvariantCultureIgnoreCase ) ) { pColumn.a[i].idx = j; if ( j == pTab.iPKey ) { keyColumn = i; } break; } } if ( j >= pTab.nCol ) { if ( sqlite3IsRowid( pColumn.a[i].zName ) ) { keyColumn = i; } else { sqlite3ErrorMsg( pParse, "table %S has no column named %s", pTabList, 0, pColumn.a[i].zName ); pParse.checkSchema = 1; goto insert_cleanup; } } } } /* If there is no IDLIST term but the table has an integer primary ** key, the set the keyColumn variable to the primary key column index ** in the original table definition. */ if ( pColumn == null && nColumn > 0 ) { keyColumn = pTab.iPKey; } /* Initialize the count of rows to be inserted */ if ( ( db.flags & SQLITE_CountRows ) != 0 ) { regRowCount = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, regRowCount ); } /* If this is not a view, open the table and and all indices */ if ( !isView ) { int nIdx; baseCur = pParse.nTab; nIdx = sqlite3OpenTableAndIndices( pParse, pTab, baseCur, OP_OpenWrite ); aRegIdx = new int[nIdx + 1];// sqlite3DbMallocRaw( db, sizeof( int ) * ( nIdx + 1 ) ); if ( aRegIdx == null ) { goto insert_cleanup; } for ( i = 0; i < nIdx; i++ ) { aRegIdx[i] = ++pParse.nMem; } } /* This is the top of the main insertion loop */ if ( useTempTable ) { /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 4): ** ** rewind temp table ** C: loop over rows of intermediate table ** transfer values form intermediate table into <table> ** end loop ** D: ... */ addrInsTop = sqlite3VdbeAddOp1( v, OP_Rewind, srcTab ); addrCont = sqlite3VdbeCurrentAddr( v ); } else if ( pSelect != null ) { /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 3): ** ** C: yield X ** if EOF goto D ** insert the select result into <table> from R..R+n ** goto C ** D: ... */ addrCont = sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); addrInsTop = sqlite3VdbeAddOp1( v, OP_If, regEof ); } /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ regRowid = regIns = pParse.nMem + 1; pParse.nMem += pTab.nCol + 1; if ( IsVirtual( pTab ) ) { regRowid++; pParse.nMem++; } regData = regRowid + 1; /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel( v ); #if !SQLITE_OMIT_TRIGGER if ( ( tmask & TRIGGER_BEFORE ) != 0 ) { int regCols = sqlite3GetTempRange( pParse, pTab.nCol + 1 ); /* build the NEW.* reference row. Note that if there is an INTEGER ** PRIMARY KEY into which a NULL is being inserted, that NULL will be ** translated into a unique ID for the row. But on a BEFORE trigger, ** we do not know what the unique ID will be (because the insert has ** not happened yet) so we substitute a rowid of -1 */ if ( keyColumn < 0 ) { sqlite3VdbeAddOp2( v, OP_Integer, -1, regCols ); } else { int j1; if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, keyColumn, regCols ); } else { Debug.Assert( pSelect == null ); /* Otherwise useTempTable is true */ sqlite3ExprCode( pParse, pList.a[keyColumn].pExpr, regCols ); } j1 = sqlite3VdbeAddOp1( v, OP_NotNull, regCols ); sqlite3VdbeAddOp2( v, OP_Integer, -1, regCols ); sqlite3VdbeJumpHere( v, j1 ); sqlite3VdbeAddOp1( v, OP_MustBeInt, regCols ); } /* Cannot have triggers on a virtual table. If it were possible, ** this block would have to account for hidden column. */ Debug.Assert( !IsVirtual( pTab ) ); /* Create the new column data */ for ( i = 0; i < pTab.nCol; i++ ) { if ( pColumn == null ) { j = i; } else { for ( j = 0; j < pColumn.nId; j++ ) { if ( pColumn.a[j].idx == i ) break; } } if ( ( !useTempTable && null == pList ) || ( pColumn != null && j >= pColumn.nId ) ) { sqlite3ExprCode( pParse, pTab.aCol[i].pDflt, regCols + i + 1 ); } else if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, j, regCols + i + 1 ); } else { Debug.Assert( pSelect == null ); /* Otherwise useTempTable is true */ sqlite3ExprCodeAndCache( pParse, pList.a[j].pExpr, regCols + i + 1 ); } } /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, ** do not attempt any conversions before assembling the record. ** If this is a real table, attempt conversions as required by the ** table column affinities. */ if ( !isView ) { sqlite3VdbeAddOp2( v, OP_Affinity, regCols + 1, pTab.nCol ); sqlite3TableAffinityStr( v, pTab ); } /* Fire BEFORE or INSTEAD OF triggers */ sqlite3CodeRowTrigger( pParse, pTrigger, TK_INSERT, null, TRIGGER_BEFORE, pTab, regCols - pTab.nCol - 1, onError, endOfLoop ); sqlite3ReleaseTempRange( pParse, regCols, pTab.nCol + 1 ); } #endif /* Push the record number for the new entry onto the stack. The ** record number is a randomly generate integer created by NewRowid ** except when the table has an INTEGER PRIMARY KEY column, in which ** case the record number is the same as that column. */ if ( !isView ) { if ( IsVirtual( pTab ) ) { /* The row that the VUpdate opcode will delete: none */ sqlite3VdbeAddOp2( v, OP_Null, 0, regIns ); } if ( keyColumn >= 0 ) { if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, keyColumn, regRowid ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_SCopy, regFromSelect + keyColumn, regRowid ); } else { VdbeOp pOp; sqlite3ExprCode( pParse, pList.a[keyColumn].pExpr, regRowid ); pOp = sqlite3VdbeGetOp( v, -1 ); if ( ALWAYS( pOp != null ) && pOp.opcode == OP_Null && !IsVirtual( pTab ) ) { appendFlag = true; pOp.opcode = OP_NewRowid; pOp.p1 = baseCur; pOp.p2 = regRowid; pOp.p3 = regAutoinc; } } /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid ** to generate a unique primary key value. */ if ( !appendFlag ) { int j1; if ( !IsVirtual( pTab ) ) { j1 = sqlite3VdbeAddOp1( v, OP_NotNull, regRowid ); sqlite3VdbeAddOp3( v, OP_NewRowid, baseCur, regRowid, regAutoinc ); sqlite3VdbeJumpHere( v, j1 ); } else { j1 = sqlite3VdbeCurrentAddr( v ); sqlite3VdbeAddOp2( v, OP_IsNull, regRowid, j1 + 2 ); } sqlite3VdbeAddOp1( v, OP_MustBeInt, regRowid ); } } else if ( IsVirtual( pTab ) ) { sqlite3VdbeAddOp2( v, OP_Null, 0, regRowid ); } else { sqlite3VdbeAddOp3( v, OP_NewRowid, baseCur, regRowid, regAutoinc ); appendFlag = true; } autoIncStep( pParse, regAutoinc, regRowid ); /* Push onto the stack, data for all columns of the new entry, beginning ** with the first column. */ nHidden = 0; for ( i = 0; i < pTab.nCol; i++ ) { int iRegStore = regRowid + 1 + i; if ( i == pTab.iPKey ) { /* The value of the INTEGER PRIMARY KEY column is always a NULL. ** Whenever this column is read, the record number will be substituted ** in its place. So will fill this column with a NULL to avoid ** taking up data space with information that will never be used. */ sqlite3VdbeAddOp2( v, OP_Null, 0, iRegStore ); continue; } if ( pColumn == null ) { if ( IsHiddenColumn( pTab.aCol[i] ) ) { Debug.Assert( IsVirtual( pTab ) ); j = -1; nHidden++; } else { j = i - nHidden; } } else { for ( j = 0; j < pColumn.nId; j++ ) { if ( pColumn.a[j].idx == i ) break; } } if ( j < 0 || nColumn == 0 || ( pColumn != null && j >= pColumn.nId ) ) { sqlite3ExprCode( pParse, pTab.aCol[i].pDflt, iRegStore ); } else if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, j, iRegStore ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_SCopy, regFromSelect + j, iRegStore ); } else { sqlite3ExprCode( pParse, pList.a[j].pExpr, iRegStore ); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ #if !SQLITE_OMIT_VIRTUALTABLE if ( IsVirtual( pTab ) ) { VTable pVTab = sqlite3GetVTable( db, pTab ); sqlite3VtabMakeWritable( pParse, pTab ); sqlite3VdbeAddOp4( v, OP_VUpdate, 1, pTab.nCol + 2, regIns, pVTab, P4_VTAB ); sqlite3VdbeChangeP5( v, (byte)( onError == OE_Default ? OE_Abort : onError ) ); sqlite3MayAbort( pParse ); } else #endif { int isReplace = 0; /* Set to true if constraints may cause a replace */ sqlite3GenerateConstraintChecks( pParse, pTab, baseCur, regIns, aRegIdx, keyColumn >= 0 ? 1 : 0, false, onError, endOfLoop, out isReplace ); sqlite3FkCheck( pParse, pTab, 0, regIns ); sqlite3CompleteInsertion( pParse, pTab, baseCur, regIns, aRegIdx, false, appendFlag, isReplace == 0 ); } } /* Update the count of rows that are inserted */ if ( ( db.flags & SQLITE_CountRows ) != 0 ) { sqlite3VdbeAddOp2( v, OP_AddImm, regRowCount, 1 ); } #if !SQLITE_OMIT_TRIGGER if ( pTrigger != null ) { /* Code AFTER triggers */ sqlite3CodeRowTrigger( pParse, pTrigger, TK_INSERT, null, TRIGGER_AFTER, pTab, regData - 2 - pTab.nCol, onError, endOfLoop ); } #endif /* The bottom of the main insertion loop, if the data source ** is a SELECT statement. */ sqlite3VdbeResolveLabel( v, endOfLoop ); if ( useTempTable ) { sqlite3VdbeAddOp2( v, OP_Next, srcTab, addrCont ); sqlite3VdbeJumpHere( v, addrInsTop ); sqlite3VdbeAddOp1( v, OP_Close, srcTab ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_Goto, 0, addrCont ); sqlite3VdbeJumpHere( v, addrInsTop ); } if ( !IsVirtual( pTab ) && !isView ) { /* Close all tables opened */ sqlite3VdbeAddOp1( v, OP_Close, baseCur ); for ( idx = 1, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, idx++ ) { sqlite3VdbeAddOp1( v, OP_Close, idx + baseCur ); } } insert_end: /* 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 inserted. 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, regRowCount, 1 ); sqlite3VdbeSetNumCols( v, 1 ); sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC ); } insert_cleanup: sqlite3SrcListDelete( db, ref pTabList ); sqlite3ExprListDelete( db, ref pList ); sqlite3SelectDelete( db, ref pSelect ); sqlite3IdListDelete( db, ref pColumn ); sqlite3DbFree( db, ref aRegIdx ); }
/* ** Create and populate a new TriggerPrg object with a sub-program ** implementing trigger pTrigger with ON CONFLICT policy orconf. */ static TriggerPrg codeRowTrigger( Parse pParse, /* Current parse context */ Trigger pTrigger, /* Trigger to code */ Table pTab, /* The table pTrigger is attached to */ int orconf /* ON CONFLICT policy to code trigger program with */ ) { Parse pTop = sqlite3ParseToplevel( pParse ); sqlite3 db = pParse.db; /* Database handle */ TriggerPrg pPrg; /* Value to return */ Expr pWhen = null; /* Duplicate of trigger WHEN expression */ Vdbe v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ SubProgram pProgram = null; /* Sub-vdbe for trigger program */ Parse pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ Debug.Assert( pTrigger.zName == null || pTab == tableOfTrigger( pTrigger ) ); Debug.Assert( pTop.pVdbe != null ); /* Allocate the TriggerPrg and SubProgram objects. To ensure that they ** are freed if an error occurs, link them into the Parse.pTriggerPrg ** list of the top-level Parse object sooner rather than later. */ pPrg = new TriggerPrg();// sqlite3DbMallocZero( db, sizeof( TriggerPrg ) ); //if ( null == pPrg ) return 0; pPrg.pNext = pTop.pTriggerPrg; pTop.pTriggerPrg = pPrg; pPrg.pProgram = pProgram = new SubProgram();// sqlite3DbMallocZero( db, sizeof( SubProgram ) ); //if( null==pProgram ) return 0; sqlite3VdbeLinkSubProgram( pTop.pVdbe, pProgram ); pPrg.pTrigger = pTrigger; pPrg.orconf = orconf; pPrg.aColmask[0] = 0xffffffff; pPrg.aColmask[1] = 0xffffffff; /* Allocate and populate a new Parse context to use for coding the ** trigger sub-program. */ pSubParse = new Parse();// sqlite3StackAllocZero( db, sizeof( Parse ) ); //if ( null == pSubParse ) return null; sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); sNC.pParse = pSubParse; pSubParse.db = db; pSubParse.pTriggerTab = pTab; pSubParse.pToplevel = pTop; pSubParse.zAuthContext = pTrigger.zName; pSubParse.eTriggerOp = pTrigger.op; pSubParse.nQueryLoop = pParse.nQueryLoop; v = sqlite3GetVdbe( pSubParse ); if ( v != null ) { #if SQLITE_DEBUG VdbeComment( v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger.zName != null ? pTrigger.zName : "", onErrorText( orconf ), ( pTrigger.tr_tm == TRIGGER_BEFORE ? "BEFORE" : "AFTER" ), ( pTrigger.op == TK_UPDATE ? "UPDATE" : "" ), ( pTrigger.op == TK_INSERT ? "INSERT" : "" ), ( pTrigger.op == TK_DELETE ? "DELETE" : "" ), pTab.zName ); #endif #if !SQLITE_OMIT_TRACE sqlite3VdbeChangeP4( v, -1, sqlite3MPrintf( db, "-- TRIGGER %s", pTrigger.zName ), P4_DYNAMIC ); #endif /* If one was specified, code the WHEN clause. If it evaluates to false ** (or NULL) the sub-vdbe is immediately halted by jumping to the ** OP_Halt inserted at the end of the program. */ if ( pTrigger.pWhen != null ) { pWhen = sqlite3ExprDup( db, pTrigger.pWhen, 0 ); if ( SQLITE_OK == sqlite3ResolveExprNames( sNC, ref pWhen ) //&& db.mallocFailed==0 ) { iEndTrigger = sqlite3VdbeMakeLabel( v ); sqlite3ExprIfFalse( pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL ); } sqlite3ExprDelete( db, ref pWhen ); } /* Code the trigger program into the sub-vdbe. */ codeTriggerProgram( pSubParse, pTrigger.step_list, orconf ); /* Insert an OP_Halt at the end of the sub-program. */ if ( iEndTrigger != 0 ) { sqlite3VdbeResolveLabel( v, iEndTrigger ); } sqlite3VdbeAddOp0( v, OP_Halt ); #if SQLITE_DEBUG VdbeComment( v, "End: %s.%s", pTrigger.zName, onErrorText( orconf ) ); #endif transferParseError( pParse, pSubParse ); //if( db.mallocFailed==0 ){ pProgram.aOp = sqlite3VdbeTakeOpArray( v, ref pProgram.nOp, ref pTop.nMaxArg ); //} pProgram.nMem = pSubParse.nMem; pProgram.nCsr = pSubParse.nTab; pProgram.token = pTrigger.GetHashCode(); pPrg.aColmask[0] = pSubParse.oldmask; pPrg.aColmask[1] = pSubParse.newmask; sqlite3VdbeDelete( ref v ); } Debug.Assert( null == pSubParse.pAinc && null == pSubParse.pZombieTab ); Debug.Assert( null == pSubParse.pTriggerPrg && 0 == pSubParse.nMaxArg ); //sqlite3StackFree(db, pSubParse); return pPrg; }
/* ** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect. ** The Name context of the SELECT statement is pNC. zType is either ** "ORDER" or "GROUP" depending on which type of clause pOrderBy is. ** ** This routine resolves each term of the clause into an expression. ** If the order-by term is an integer I between 1 and N (where N is the ** number of columns in the result set of the SELECT) then the expression ** in the resolution is a copy of the I-th result-set expression. If ** the order-by term is an identify that corresponds to the AS-name of ** a result-set expression, then the term resolves to a copy of the ** result-set expression. Otherwise, the expression is resolved in ** the usual way - using sqlite3ResolveExprNames(). ** ** This routine returns the number of errors. If errors occur, then ** an appropriate error message might be left in pParse. (OOM errors ** excepted.) */ static int resolveOrderGroupBy( NameContext pNC, /* The name context of the SELECT statement */ Select pSelect, /* The SELECT statement holding pOrderBy */ ExprList pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */ string zType /* Either "ORDER" or "GROUP", as appropriate */ ) { int i; /* Loop counter */ int iCol; /* Column number */ ExprList_item pItem; /* A term of the ORDER BY clause */ Parse pParse; /* Parsing context */ int nResult; /* Number of terms in the result set */ if ( pOrderBy == null ) return 0; nResult = pSelect.pEList.nExpr; pParse = pNC.pParse; for ( i = 0; i < pOrderBy.nExpr; i++ )//, pItem++ ) { pItem = pOrderBy.a[i]; Expr pE = pItem.pExpr; iCol = resolveAsName( pParse, pSelect.pEList, pE ); if ( iCol > 0 ) { /* If an AS-name match is found, mark this ORDER BY column as being ** a copy of the iCol-th result-set column. The subsequent call to ** sqlite3ResolveOrderGroupBy() will convert the expression to a ** copy of the iCol-th result-set expression. */ pItem.iCol = (u16)iCol; continue; } if ( sqlite3ExprIsInteger( pE, ref iCol ) != 0 ) { /* The ORDER BY term is an integer constant. Again, set the column ** number so that sqlite3ResolveOrderGroupBy() will convert the ** order-by term to a copy of the result-set expression */ if ( iCol < 1 ) { resolveOutOfRangeError( pParse, zType, i + 1, nResult ); return 1; } pItem.iCol = (u16)iCol; continue; } /* Otherwise, treat the ORDER BY term as an ordinary expression */ pItem.iCol = 0; if ( sqlite3ResolveExprNames( pNC, ref pE ) != 0 ) { return 1; } } return sqlite3ResolveOrderGroupBy( pParse, pSelect, pOrderBy, zType ); }
/* ** 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; }
/* ** Resolve names in the SELECT statement p and all of its descendents. */ static int resolveSelectStep( Walker pWalker, Select p ) { NameContext pOuterNC; /* Context that contains this SELECT */ NameContext sNC; /* Name context of this SELECT */ bool isCompound; /* True if p is a compound select */ int nCompound; /* Number of compound terms processed so far */ Parse pParse; /* Parsing context */ ExprList pEList; /* Result set expression list */ int i; /* Loop counter */ ExprList pGroupBy; /* The GROUP BY clause */ Select pLeftmost; /* Left-most of SELECT of a compound */ Debug.Assert( p != null ); if ( ( p.selFlags & SF_Resolved ) != 0 ) { return WRC_Prune; } pOuterNC = pWalker.u.pNC; pParse = pWalker.pParse; //sqlite3 db = pParse.db; /* 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 ) { sqlite3SelectPrep( pParse, p, pOuterNC ); return ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) ? WRC_Abort : WRC_Prune; } isCompound = p.pPrior != null; nCompound = 0; pLeftmost = p; 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. */ sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); sNC.pParse = pParse; if ( sqlite3ResolveExprNames( sNC, ref p.pLimit ) != 0 || sqlite3ResolveExprNames( sNC, ref p.pOffset ) != 0 ) { return WRC_Abort; } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. */ sNC.allowAgg = 1; sNC.pSrcList = p.pSrc; sNC.pNext = pOuterNC; /* Resolve names in the result set. */ pEList = p.pEList; Debug.Assert( pEList != null ); for ( i = 0; i < pEList.nExpr; i++ ) { Expr pX = pEList.a[i].pExpr; if ( sqlite3ResolveExprNames( sNC, ref pX ) != 0 ) { return WRC_Abort; } } /* Recursively resolve names in all subqueries */ for ( i = 0; i < p.pSrc.nSrc; i++ ) { SrcList_item pItem = p.pSrc.a[i]; if ( pItem.pSelect != null ) { string zSavedContext = pParse.zAuthContext; if ( pItem.zName != null ) pParse.zAuthContext = pItem.zName; sqlite3ResolveSelectNames( pParse, pItem.pSelect, pOuterNC ); pParse.zAuthContext = zSavedContext; if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) 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 ); pGroupBy = p.pGroupBy; if ( pGroupBy != null || sNC.hasAgg != 0 ) { p.selFlags |= SF_Aggregate; } else { sNC.allowAgg = 0; } /* If a HAVING clause is present, then there must be a GROUP BY clause. */ if ( p.pHaving != null && pGroupBy == null ) { sqlite3ErrorMsg( pParse, "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. */ sNC.pEList = p.pEList; if ( sqlite3ResolveExprNames( sNC, ref p.pWhere ) != 0 || sqlite3ResolveExprNames( sNC, ref p.pHaving ) != 0 ) { return WRC_Abort; } /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries */ sNC.pNext = null; sNC.allowAgg = 1; /* 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 && resolveOrderGroupBy( sNC, p, p.pOrderBy, "ORDER" ) != 0 ) { return WRC_Abort; } //if ( db.mallocFailed != 0 ) //{ // return WRC_Abort; //} /* Resolve the GROUP BY clause. At the same time, make sure ** the GROUP BY clause does not contain aggregate functions. */ if ( pGroupBy != null ) { ExprList_item pItem; if ( resolveOrderGroupBy( sNC, p, pGroupBy, "GROUP" ) != 0 /*|| db.mallocFailed != 0 */ ) { return WRC_Abort; } for ( i = 0; i < pGroupBy.nExpr; i++ )//, pItem++) { pItem = pGroupBy.a[i]; if ( ( pItem.pExpr.flags & EP_Agg ) != 0 )//HasProperty(pItem.pExpr, EP_Agg) ) { sqlite3ErrorMsg( pParse, "aggregate functions are not allowed in " + "the GROUP BY clause" ); return WRC_Abort; } } } /* Advance to the next term of the compound */ p = p.pPrior; nCompound++; } /* Resolve the ORDER BY on a compound SELECT after all terms of ** the compound have been resolved. */ if ( isCompound && resolveCompoundOrderBy( pParse, pLeftmost ) != 0 ) { return WRC_Abort; } return WRC_Prune; }
public DepartmentsController(NameContext context) { _context = context; }
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.ColumnIdx = -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); }