diff --git a/Documentation/StyleGuide/.gitignore b/Documentation/StyleGuide/.gitignore new file mode 100644 index 0000000000..d60c2cebd1 --- /dev/null +++ b/Documentation/StyleGuide/.gitignore @@ -0,0 +1,3 @@ +*.aux +*.log +*.toc diff --git a/Documentation/StyleGuide/StyleGuide.pdf b/Documentation/StyleGuide/StyleGuide.pdf new file mode 100644 index 0000000000..69aaeb8d46 Binary files /dev/null and b/Documentation/StyleGuide/StyleGuide.pdf differ diff --git a/Documentation/StyleGuide/StyleGuide.tex b/Documentation/StyleGuide/StyleGuide.tex new file mode 100644 index 0000000000..b85403ff9f --- /dev/null +++ b/Documentation/StyleGuide/StyleGuide.tex @@ -0,0 +1,2118 @@ +\documentclass[a4paper,11pt,oneside]{scrbook} +% +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[pdftex]{color,graphicx} +\usepackage{fancyvrb} +\usepackage{geometry} +\usepackage{lastpage} +\usepackage{makeidx} +% +\geometry{a4paper,left=30mm,right=20mm,top=2.5cm,bottom=3cm} +\setlength{\headheight}{3.5em} +\setlength{\parindent}{0pt} +\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} +% +\title{Programming Style Guidelines\\ ArangoDB Edition} +\author{Dr. Frank Celler} +\date{Version 1.2.0} +% +\DefineVerbatimEnvironment% + {code}{Verbatim}{commandchars=} +\DefineVerbatimEnvironment% + {example}{Verbatim}{} +% +\newcommand{\guideline}[1]{{\subsection{#1}}} +\newcommand{\motivation}[1]{{\normalfont \itshape #1}} +\newcommand{\trfile}[1]{"#1"} +\newcommand{\trclass}[1]{\emph{#1}} +\newcommand{\trmethod}[1]{\emph{#1}} +\newcommand{\trtable}[1]{\emph{#1}} +\newcommand{\troption}[1]{\emph{#1}} +\newcommand{\trcode}[1]{{\normalfont \ttfamily #1}} +% +\begin{document} +\maketitle +\tableofcontents +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{Introduction} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +This document lists coding recommendations for the projects ArangoDB. They are +based on C++ coding recommendations common in the C++ development community, on +established standards collected from a number of sources, individual experience, +local requirements/needs, as well as suggestions given in \ref{bib:1} - +\ref{bib:5} of chapter \ref{cha:references}. + +There are several reasons for introducing a new guideline rather than just +referring to the ones above. The main reason is that these guides are far too +general in their scope and that more specific rules (especially naming rules) +need to be established. Also, the present guide has an annotated form that makes +it far easier to use during project code reviews than most other existing +guidelines. In addition, programming recommendations generally tend to mix style +issues with language technical issues in a somewhat confusing manner. The +present document does not contain any C++ technical recommendations at all, but +focuses mainly on programming style. + +While a given development environment (IDE) can improve the readability of code +by access visibility, color coding, automatic formatting and so on, the +programmer should never rely on such features. Source code should always be +considered larger than the IDE it is developed within and should be written in a +way that maximize its readability independent of any IDE. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Layout of the Recommendations} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +The recommendations are grouped by topic and each recommendation is numbered to +make it easier to refer to during reviews. Layout of the recommendations is as +follows: + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Guideline short description} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Guideline longer description + +\begin{code} + Example if applicable +\end{code} + +\motivation{ + Motivation, background and additional information. +} + +The motivation section is important. Coding standards and guidelines +tend to start "religious wars", and it is important to state the +background for the recommendation. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Recommendation Importance} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +In the guideline sections the terms \emph{must}, \emph{should} and \emph{can} +have special meaning. A \emph{must} requirement must be followed, a +\emph{should} is a strong recommendation, and a \emph{can} is a general +guideline. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{General Recommendations} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\emph{Any} violation to the guide is allowed if it enhances readability. The +main goal of the recommendation is to \emph{improve} readability and thereby the +understanding and the maintainability and general quality of the code. It is +impossible to cover all the specific cases in a general guide and the programmer +should be flexible. + +However, as readability is subject to personal taste, any violation should be +discussed with the team. If a violation makes sense within a given context, a +corresponding recomemendation should be added to this document. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{General Naming Conventions} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{sec:general-naming-conventions} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Names must be chosen by forming an english description} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names must be chosen by forming an english description with "of", "in", "to", +"from" left out. + +\begin{code} + TypeLanguage // NOT TypeOfLanguage or LanguageType +\end{code} + +\motivation{ + Words like "of" are automatically added when reading. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Names representing types must be in upper camel case} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names representing types (Classes, Interfaces) must be in upper camel case +starting with upper case. + +\begin{code} + Line + SavingsAccount +\end{code} + +\motivation{ + Common practice in the C++ development community. +} + +Standard names are allowed to violated the above rule. Names used for instance +in the C++ STL do not always follow the above rule. In this case the established +names should be used. Examples are \trcode{size}, \trcode{length}, \trcode{at}, +\trcode{c\_str}. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Variable names must be in lower camel case} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Variable names must be in lower camel case starting with lower case. Private +and protected member variables must start with a "\_". + +\begin{code} + line + savingsAccount +\end{code} + +\motivation{ + Common practice in the C++ development community. Makes variables easy to + distinguish from types, and effectively resolves potential naming collision as + in the declaration Line line. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Named constants must be all uppercase} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Named constants (including enumeration values) must be all uppercase +using underscore to separate words. + +\begin{code} + MAX_ITERATIONS + COLOR_RED + PI +\end{code} + +\motivation{ + Common practice in the C++ development community. +} + +In general, the use of such constants should be minimized. In many cases +implementing the value as a method is a better choice: + +\begin{code} + int maxIterations () const { + return 25; + } +\end{code} + +This form is both easier to read, and it ensures a unified interface towards +class values. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Names representing methods or functions must be in lower camel case} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names representing methods or functions, which \emph{do some work}, must be +verbs and written in lower camel case starting with lower case. Names +representing methods or functions, which \emph{return something}, must be +substantives and written in lower camel case starting with lower case. + +\begin{code} + w = totalWidth(); + validateInput(); +\end{code} + +\motivation{ + Common practice in the C++ development community. This is identical to + variable names, but functions in C++ are already distinguishable from + variables by their specific form. + + Functions (methods returning something) should be named after what they return + and procedures (void methods) after what they do. This increases + readability. Makes it clear what the unit should do and especially all the + things it is not supposed to do. This again makes it easier to keep the code + clean of side effects. +} + +One exception to these rules are getter and setter - see below. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Names representing namespaces must be all lowercase} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names representing namespaces must be all lowercase using underscore to separate +words. + +\begin{code} + analyzer + io_manager +\end{code} + +\motivation{ + Common practice in the C++ development community. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Abbreviations and acronyms must not be uppercase when used as name} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + exportHtmlSource(); // NOT: exportHTMLSource(); + openDvdPlayer(); // NOT: openDVDPlayer(); +\end{code} + +\motivation{ + Using all uppercase for the base name will give conflicts with the naming + conventions given above. A variable of this type whould have to be named dVD, + hTML etc. which obviously is not very readable. Another problem is illustrated + in the examples above; When the name is connected to another, the readbility + is seriously reduced; the word following the abbreviation does not stand out + as it should. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Generic variables must have the same name as their type} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + void setTopic (Topic *topic) // NOT: void setTopic (Topic *value) + // NOT: void setTopic (Topic *aTopic) + // NOT: void setTopic (Topic *x) + + void connect (Database *database) // NOT: void connect (Database *db) + // NOT: void connect (Database *oracleDB) +\end{code} + +\motivation{ + Reduce complexity by reducing the number of terms and names used. Also makes + it easy to deduce the type given a variable name only. If for some reason this + convention doesn't seem to fit it is a strong indication that the type name is + badly chosen. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Non-generic variables should have a role} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Non-generic variables have a role. These variables can often be named by +combining role and type. + +\begin{code} + Point startingPoint, centerPoint; + Name loginName; +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{All names should be written in English} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + FileName; // NOT: filNavn +\end{code} + +\motivation{ + English is the prefered language for international development. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The length of a name should corresponde to the scope} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Variables with a large scope should have long names, variables with a small +scope can have short names. + +\motivation{ + Scratch variables used for temporary storage or indices are best kept short. A + programmer reading such variables should be able to assume that its value is + not used outside a few lines of code. Common scratch variables for integers + are i, j, k, m, n and for characters c and d. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The maximal length of a name must be 40} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + Very long names make the program harder to read because more line breaks are + required. If the very long name is required to describe a method or variable + it could be an indication that this method or variable is to complex and that + the code should be refactored. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The name of the object should be avoided in a method name} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The name of the object is implicit, and should be avoided in a method name. + +\begin{code} + line.getLength(); // NOT: line.getLineLength(); +\end{code} + +\motivation{ + The latter seems natural in the class declaration, but proves superfluous in + use, as shown in the example. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Specific Naming Conventions} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Getters and setters} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The terms \trcode{get}/\trcode{set} must be used where an attribute is accessed +directly + +\begin{code} + employee.getName(); + matrix.getElement(2, 4); + employee.setName(name); + matrix.setElement(2, 4, value); +\end{code} + +\motivation{ + In Java this convention has become more or less standard. +} + +Note that this is only used when accessing a member variable directly. If any +computation is involved the above rule does not apply and section +\ref{sec:general-naming-conventions} should be followed. A getter or setter must +only consists of a few line of code. + +The terms \trcode{is}/\trcode{set} must be used where a boolean attribute is +accessed directly. + +\begin{code} + matrix.isDense(); +\end{code} + +\motivation{ + In Java this convention has become more or less standard. There are exceptions + to this rule in C++. The STL for example defines \trcode{empty} instead + \trcode{isEmpty}. +} + +If definition a setter \trcode{setSomething} use \trcode{newSomething} as +variable name for the new value. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use \trcode{compute} for methods which compute and store} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The term \trcode{compute} should be used in methods where something is computed +and stored within the object. + +\begin{code} + valueSet->computeAverage(); + matrix->computeInverse(); +\end{code} + +\motivation{ + Give the reader the immediate clue that this is a potential time consuming + operation, and if used repeatedly, he might consider caching the + result. Consistent use of the term enhances readability. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use \trcode{find} and \trcode{lookup} for look ups} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The terms \trcode{find} and \trcode{lookup} should be used in methods where +something is looked up. + +\begin{code} + vertex.findNearestVertex(); + matrix.findMinElement(); + vertex.lookupVertex(vertexId); +\end{code} + +\motivation{ + Give the reader the immediate clue that this is a simple look up method with a + minimum of computations involved. \trcode{lookup} should be used when no + computation or minimal computation is done and no error is raised when the + element is not found. \trcode{find} should be used when computations are + required; it is allowed to create missing elements or to raise an + error. Consistent use of the terms enhances readability. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use \trcode{initialize} for initialisation} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The term \trcode{initialize} should be used where an object or a concept is +established. + +\begin{code} + printer.initializeFontSet(); +\end{code} + +\motivation{ + The american initialize should be preferred over the british initialise. The + abbreviation \trcode{init} should be avoided. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use GUI component type name as suffix} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Variables representing GUI components should be suffixed by the component type +name. + +\begin{code} + mainWindow, propertiesDialog, widthScale, loginText, leftScrollbar, + mainForm, fileMenu, minLabel, exitButton, yesToggle +\end{code} + +\motivation{ + Enhances readability since the name gives the user an immediate clue of the + type of the variable and thereby the objects resources. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Can use \trcode{List} suffix for lists} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The suffix \trcode{List} can be used on names representing a list of objects. + +\begin{code} + vertex (one vertex) + vertexList (a list of vertices) +\end{code} + +\motivation{ + Enhances readability since the name gives the user an immediate clue of the + type of the variable and the operations that can be performed on the + object. Simply using the plural form of the base class name for a list + (matrixElement (one matrix element), matrixElements (list of matrix elements)) + shoul be avoided since the two only differ in a single character and are + thereby difficult to distinguish. +} + +A list in this context is the compound data type that can be traversed +backwards, forwards, etc. (typically an STL vector). A plain array is +simpler. The suffix \trcode{Array} can be used to denote an array of objects. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use prefix \trcode{n} or \trcode{number}} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The prefix \trcode{n} or \trcode{number} should be used for variables +representing a number of objects. + +\begin{code} + nPoints, numberLines +\end{code} + +\motivation{ + The notation is taken from mathematics where it is an established convention + for indicating a number of objects. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use suffix \trcode{Id} for identifier} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The suffix \trcode{Id} should be used for variables representing an entity + number. + +\begin{code} + tableId, employeeId +\end{code} + +\motivation{ + The notation is taken from mathematics where it is an established convention + for indicating an entity number. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should use mathematical iterators names} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Iterator variables should be called \trcode{i}, \trcode{j}, \trcode{k} etc +or \trcode{iter}. + +\begin{code} + for (int i = 0; i < nTables); i++) { + ... + } + + vector::iterator iter; + + for (iter = list.begin(); iter != list.end(); ++iter) { + Element element = *iter; + ... + } +\end{code} + +\motivation{ + The notation is taken from mathematics where it is an established convention + for indicating iterators. \trcode{iter} should be used for STL iterators. +} + +An elegant alternative is to prefix such variables with an i or iter: iTable, +iterEmployee. This effectively makes them named iterators. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The prefix \trcode{is} should be used for boolean variables and methods} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + isSet, isVisible, isFinished, isFound, isOpen +\end{code} + +\motivation{ + Common practice in the C++ development community and partially enforced in Java. +} + +Using the is prefix solves a common problem of choosing bad boolean names like +status or flag. isStatus or isFlag simply doesn't fit, and the programmer is +forced to choose more meaningful names. There are a few alternatives to the +\trcode{is} prefix that fits better in some situations. These are the +\trcode{has}, \trcode{can} and \trcode{should} prefixes: + +\begin{code} + bool hasLicense(); + bool canEvaluate(); + bool shouldSort(); +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Complement names must be used for complement operations} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + get/set + add/remove + create/destroy + start/stop + insert/delete + increment/decrement + old/new + begin/end + first/last + up/down + min/max + next/previous + open/close + show/hide + suspend/resume +\end{code} + +\motivation{ + Reduce complexity by symmetry. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Abbreviations in names must be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + computeAverage(); // NOT: compAvg(); +\end{code} + +There are two types of words to consider. First are the common words listed in a +language dictionary. These must never be abbreviated. Never write: + +\begin{itemize} + \item + cmd instead of command + + \item + cp instead of copy + + \item + pt instead of point + + \item + comp instead of compute + + \item + init instead of initialize +\end{itemize} + +Then there are domain specific phrases that are more naturally known through +their abbreviations/acronym. These phrases must be kept abbreviated. Never +write: + +\begin{itemize} + \item + HypertextMarkupLanguage instead of html + + \item + CentralProcessingUnit instead of cpu + + \item + PriceEarningRatio instead of pe +\end{itemize} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Naming pointers specifically should be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + Line * line; // NOT: Line * pLine; or Line * linePtr; etc. +\end{code} + +Many variables in a C/C++ environment are pointers, so a convention like this is +almost impossible to follow. Also objects in C++ are often oblique types where +the specific implementation should be ignored by the programmer. Only when the +actual type of an object is of special significance, the name should empahsize +the type. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Negated boolean variable names must be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + bool isError; // NOT: isNoError + bool isFound; // NOT: isNotFound +\end{code} + +The problem arises when such a name is used in conjunction with the logical +negation operator as this results in a double negative. It is not immediately +apparent what \trcode{!isNotFound} means. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Enumeration constants can be prefixed by a common type name} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + enum color_e { + COLOR_RED, + COLOR_GREEN, + COLOR_BLUE + }; +\end{code} + +\motivation{ + This gives additional information of where the declaration can be found, which + constants belongs together, and what concept the constants represent. An + alternative approach is to always refer to the constants through their common + type: \trcode{Color::RED}, \trcode{Airline::AIR\_FRANCE}, etc. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{C++ Naming Conventions} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Within a given module, class names must be unique} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\label{subsec:unique-class-name} + +\motivation{ + If class names are unique it is much easier to understand the code because + mix-ups are avoided. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Exception classes should be suffixed with \trcode{Exception} or \trcode{Error}} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + class AccessException { + ... + } +\end{code} + +\motivation{ + Exception classes are really not part of the main design of the program, and + naming them like this makes them stand out relative to the other classes. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Enumerations should be lowercase follwed by \trcode{\_e}} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + enum color_e { + .. + }; +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Template names should are short and must be uppercase} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Names representing template types must be uppercase. + +Names representing generic template types should be a single uppercase +letter. Two uppercase letters at most. Names representing specific template +types should be named after that type. + +\begin{code} + template ... + template ... + template ... +\end{code} + +\motivation{ + Common practice in the C++ development community. This makes template names + stand out relative to all other names used. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Global variables should always be referred to using the \trcode{::} operator} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + ::mainWindow.open() + ::applicationContext.getName() +\end{code} + +\motivation{ + In general, the use of global variables should be avoided. Consider using + singleton objects instead. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{Files} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Source Files} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Must use \trfile{.h} and \trfile{.cpp} extensions} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +C++ header files must have the extension \trfile{.h}. Source files must the +extension \trfile{.cpp}. The extensions \trfile{.C}, \trfile{.cc} or +\trfile{.c++} are not allowed. + +\begin{code} + MyClass.cpp, MyClass.h +\end{code} + +\motivation{ + These are all accepted C++ standards for file extension. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Must use two files per class for public classes} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\label{subsec:two-files-per-class} + +A class must be declared in a header file and implemented in a source file where +the name of the files match the name of the class. Classes which are local to +computation and have a file context, can be defined in the source file in an +anonymous namespace. + +The files must have the same name as the class including case. A class +\trcode{MyHthml} must be declared in \trfile{MyHtml.h"} and implemented in +\trfile{MyHtml.cpp}. + +\begin{code} + MyClass.h, MyClass.cpp +\end{code} + +\motivation{ + Makes it easy to find the associated files of a given class. This convention + is enforced in Java and has become very successful as such. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{All computations must reside in source files} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + class MyClass { + public: + int getValue () {return value;} // YES + int computeSomething () {...computation...} // NO! + + private: + int value; + } +\end{code} + +\motivation{ + The header files must declare an interface of a class, the source file must + implement it. When looking for an implementation, the programmer should always + know that it is found in the source file. The obvious exception to this rule + are of course inline functions and templates that must be defined in the + header file. Trivial getter and setter are another exception. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{File content should be kept within 80 columns} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +80 columns is a common dimension for editors, terminal emulators, printers and +debuggers, and files that are shared between several people should keep within +these constraints. It improves readability when unintentional line breaks are +avoided when passing a file between programmers. Special characters like TAB +and page break must be avoided. + +These characters are bound to cause problem for editors, printers, terminal +emulators or debuggers when used in a multi-programmer, multi-platform +environment. The incompleteness of split lines must be made obvious. + +\begin{code} + totalSum = a + b + c + + d + e; + + function (param1, param2, + param3); + + setText ("Long line split" + "into two parts."); + + for (tableNo = 0; tableNo < nTables; + tableNo += tableStep) +\end{code} + +Split lines occurs when a statement exceed the 80 column limit given above. It +is difficult to give rigid rules for how lines should be split, but the examples +above should give a general hint. In general: + +\begin{itemize} + \item + Break after a comma. + + \item + Break after an operator. + + \item + Align the new line with the beginning of the expression on the previous line. +\end{itemize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Include Files and Include Statements} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Must use include guards} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\label{guideline:define-guard} + +Header files must include a construction that prevents multiple inclusion. The +convention is an all uppercase construction of the module/directory name, the +file name and the \trcode{h} suffix. + +Obeying \ref{subsec:two-files-per-class} and \ref{subsec:unique-class-name} +guarantees that the input guards is unique. + +\begin{code} + #ifndef MODULE_FILENAME_H + #define MODULE_FILENAME_H + ... + #endif +\end{code} + +\motivation{ + The construction is to avoid compilation errors. The name convention is common + practice. The construction should appear in the top of the file (before the + file header) so file parsing is aborted immediately and compilation time is + reduced. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Include statements should be sorted and grouped} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorted by their hierarchical position in the system with low level files +included first. Leave an empty line between groups of include statements. + +\begin{code} + #include + #include + + #include + #include + + #include "ui/PropertiesDialog.h" + #include "ui/MainWindow.h" +\end{code} + +\motivation{ + In addition to show the reader the individual include files, it also give an + immediate clue about the modules that are involved. Include file paths must + never be absolute. Compiler directives should instead be used to indicate root + directories for includes. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Include statements must be located at the top of a file only} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + Common practice. Avoid unwanted compilation side effects + by "hidden" include statements deep into a source file. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{File Structure} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Header file structure} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parts of a class must be sorted public, protected and private. All +sections must be identified explicitly. Not applicable sections should +be left out. The ordering is "most public first" so people who only +wish to use the class can stop reading when they reach the +protected/private sections. + +Use the following ordering within a header file. + +\begin{enumerate} + \item + header including copyright, author, and date + + \item + define-guard, see \ref{guideline:define-guard} + + \item + include \trfile{Common.h} or header-file of base classes + + \item + include system C headers + + \item + include system C++ headers + + \item + include project C++ headers, use forward declarations where possible + + \item + forward declararions for C++ classes and structs + + \item + class declararion + + \begin{enumerate} + \item + local classes and enumerations + + \item + static constant variables + + \item + static functions + + \item + static variables + + \item + constructors and destructors + + \item + public methods + + \item + public variables + + \item + protected methods + + \item + protected variables + + \item + private methods + + \item + private variables + \end{enumerate} +\end{enumerate} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Source file structure} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parts of a class must be sorted public, protected and private. All +sections must be identified explicitly. Not applicable sections should +be left out. The ordering is "most public first" so people who only +wish to use the class can stop reading when they reach the +protected/private sections. + +Use the following ordering within a source file. + +\begin{enumerate} + \item + header including copyright, author, and date + + \item + corresponding header file + + \item + include system C headers + + \item + include system C++ headers + + \item + include project C++ headers + + \item + class implementation + + \begin{enumerate} + \item + auxillary function hidden in an anonymous namespace + + \item + static constant variables + + \item + static functions + + \item + constructors and destructors + + \item + public methods + + \item + protected methods + + \item + private methods + \end{enumerate} +\end{enumerate} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{C++ Statements} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Namespaces} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Namespaces must not be included globally in the header} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A namespace must be included by \trcode{using} either within the source file +or within another namespace. It must not be included globally in a header file. + +\motivation{ + Avoids conflicts with other libraries. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Types} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Types that are local to one file only can be declared inside that file} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Classes which are local to computation and have a file context, can +be defined in the source file in an anonymous namespace. + +\motivation{ + Enforces information hiding. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Type conversions must always be done explicitly} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Type conversions must always be done explicitly. Never rely on +implicit type conversion. + +\begin{code} + floatValue = static_cast (intValue); // YES! + floatValue = intValue; // NO! +\end{code} + +\motivation{ + By this, the programmer indicates that he is aware of the different + types involved and that the mix is intentional. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Variables} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Variables should be initialized where they are declared} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This ensures that variables are valid at any time. Sometimes it is +impossible to initialize a variable to a valid value where it is +declared: + +\begin{code} + int x, y, z; + getCenter (&x, &y, &z); +\end{code} + +In these cases it should be left uninitialized rather than initialized to some phony value. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Variables must never have dual meaning} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + Enhance readability by ensuring all concepts are represented + uniquely. Reduce chance of error by side effects. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Use of global variables should be minimized} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + In C++ there is no reason global variables need to be used at all. The + same is true for global functions or file scope (static) variables. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Non-constant class variables must never be declared public} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + The concept of C++ information hiding and encapsulation is violated by + public variables. Use private variables and access functions + instead. One exception to this rule is when the class is essentially a + data structure, with no behavior (equivalent to a C struct). In this + case it is appropriate to make the class' instance variables public. +} + +Note that structs are kept in C++ for compatibility with C only, and +avoiding them increases the readability of the code by reducing the +number of constructs used. You should use a class instead. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Related variables of the same type can be declared in a common statement} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Related variables of the same type can be declared in a common statement. +Unrelated variables should not be declared in the same statement. + +\begin{code} + float x, y, z; + float revenueJanuary, revenueFebruary, revenueMarch; +\end{code} + +The common requirement of having declarations on separate lines is not +useful in the situations like the ones above. It enhances readability +to group variables like this. Howevery, in the case the type must not be +a pointer. + +Pointers and classes however must be declared on separate lines. + +C++ pointers and references should have their reference symbol next to +the type name rather than to the variable name. + +\begin{code} + float* x; // NOT: float *x; + int& y; // NOT: int &y; +\end{code} + +It is debatable whether a pointer is a variable of a pointer type +(float* x) or a pointer to a given type (float *x). It is impossible +to declare more than one pointer in a given statement using the first +approach. I.e. float* x, y, z; is equivalent with float *x; float y; +float z; The same goes for references. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{\trcode{const} must go after the type} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + string const& ref = ...; // NOT: const string& ref +\end{code} + +\motivation{ + The type declarations are read from right to left. The type should be the last. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Constant must be on the left hand side of a comparison} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a constant is on the left hand side then forgetting a \trcode{=} sign +results in an error message. + +\begin{code} + if (a = 0) // compiles, but always false + if (0 = a) // compiler error + if (0 == a) // correct +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{You must not use implicit 0 tests} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Implicit test for 0 should not be used other than for boolean +variables and pointers. + +\begin{code} + if (0 != nLines) // NOT: if (nLines) + if (0.0 != value) // NOT: if (value) +\end{code} + +\motivation{ + It is not necessarily defined by the compiler that ints and floats 0 + are implemented as binary 0. Also, by using explicit test the + statement give immediate clue of the type being tested. +} + +It is common also to suggest that pointers shouldn't test implicit for +0 either, i.e. if (line == 0) instead of if (line). The latter is +regarded as such a common practice in C/C++ however that it can be +used. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Variables should be declared in the smallest scope possible. } +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + Keeping the operations on a variable within a small + scope, it is easier to control the effects and side effects of the + variable. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Loops} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Only loop control statements must be included in the \trcode{for} construction} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + sum = 0; + + for (i = 0; i < 100; i++) { + sum += value[i]; + } + + // NOT: for (i = 0, sum = 0; i < 100; i++) { + // sum += value[i]; + // } +\end{code} + +\motivation{ + Increase maintainability and readability. Make it crystal clear what + controls the loop and what the loop contains. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should initialise loop variables before the loop} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Loop variables should be initialized immediately before the loop. + +\begin{code} + bool isDone = false; + + while (!isDone) { + ... + } + + // NOT: bool isDone = false; + // ... + // some more code + /// ... + // while (!isDone) { + // ... + // } +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{\trcode{do-while} loops can be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +do-while loops are less readable than ordinary while loops and for +loops since the conditional is at the bottom of the loop. The reader +must scan the entire loop in order to understand the scope of the +loop. In addition, \trcode{do-while} loops are not needed. Any +\trcode{do-while} loop can easily be rewritten into a \trcode{while} +loop or a \trcode{for} loop. Reducing the number of constructs used +enhance readbility. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The use of \trcode{break} and \trcode{continue} in loops should be minimized} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These constructs can be compared to \trcode{goto} and they should only +be used if they prove to have higher readability than their structured +counterpart. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The form \trcode{while (true)} should be used for infinite loops} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + while (true) { + ... + } + + for (;;) { // NO! + ... + } + + while (1) { // NO! + ... + } +\end{code} + +Testing against 1 is neither necessary nor meaningful. The form for +(;;) is not very readable, and it is not apparent that this actually +is an infinite loop. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Conditionals} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Complex conditional expressions must be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Complex conditional expressions must be avoided. Introduce temporary +boolean variables instead. + +\begin{code} + if ((elementNo < 0) || (elementNo > maxElement)|| + elementNo == lastElement) { + ... + } +\end{code} + +should be replaced by assuming that short-cutting is not required: + +\begin{code} + isFinished = (elementNo < 0) || (elementNo > maxElement); + isRepeatedEntry = elementNo == lastElement; + + if (isFinished || isRepeatedEntry) { + ... + } +\end{code} + +\motivation{ + By assigning boolean variables to expressions, the program gets + automatic documentation. The construction will be easier to read and + to debug. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Should put the exceptional case in the \trcode{else}-part} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The nominal case should be put in the \trcode{if}-part and the exception in the +\trcode{else}-part of an \trcode{if} statement. + +\begin{code} + isError = readFile (fileName); + + if (!isError) { + ... + } + else { + ... + } +\end{code} + +\motivation{ + Makes sure that the exceptions don't obscure the normal path of + execution. This is important for both the readability and performance. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The conditional should be put on a separate line} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + if (isDone) { // NOT: if (isDone) { doCleanup(); } + doCleanup(); + } +\end{code} + +\motivation{ + This is for debugging purposes. When writing on a single line, it is + not apparent whether the test is really true or not. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Executable statements in conditionals must be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + // Bad! + if (!(fileHandle = open (fileName, "w"))) { + ... + } + + // Better! + fileHandle = open (fileName, "w"); + + if (!fileHandle) { + ... + } +\end{code} + +\motivation{ + Conditionals with executable statements are just very + difficult to read. This is especially true for programmers new to + C/C++. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Miscellaneous} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{\trcode{goto} should not be used} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ \trcode{goto} statements violates the idea of structured + code. Only in some very few cases (for instance breaking out of + deeply nested structures or for special needs when dealing with an + error) should \trcode{goto} be considered, and only if the + alternative structured counterpart (for example, exceptions) is + proven to be less readable. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Functions must always have the return value explicitly listed} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + int getValue() { // NOT: getValue() + ... + } +\end{code} + +\motivation{ + If not exlicitly listed, C++ implies int return value for functions. A + programmer must never rely on this feature, since this might be + confusing for programmers not aware of this artifact. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{\trcode{0} should be used instead of \trcode{NULL} in C++} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + NULL is part of the standard C library, but is made obsolete in C++. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{Miscellaneous} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Miscellaneous} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The use of magic numbers in the code must be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Numbers other than 0 and 1 must be declared as named constants instead. + +\motivation{ + If the number does not have an obvious meaning by itself, the + readability is enhanced by introducing a named constant instead. A + different approach is to introduce a method from which the constant + can be accessed. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The use of magic strings in the code should be avoided} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Strings of length 3 or greater should be considered declared as named +constants instead. + +\motivation{ + Using a constant reduces the errors due to typing mistakes. Strings used in + text messages should only be declared as constants if used more than once. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Use a decimal point for floating point constants} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Floating point constants should always be written with decimal point +and at least one decimal. + +\begin{code} + double total = 0.0; // NOT: double total = 0; + double speed = 3.0e8; // NOT: double speed = 3e8; + double sum; + ... + sum = (a + b) * 10.0; +\end{code} + +\motivation{ + This empasize the different nature of integer and floating point + numbers even if their values might happen to be the same in a specific + case. +} + +Also, as in the last example above, it emphasize the type of the +assigned variable (sum) at a point in the code where this might not be +evident. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Floating point constants should always be written with a digit before the decimal point} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + double total = 0.5; // NOT: double total = .5; +\end{code} + +\motivation{ + The number and expression system in C++ is borrowed from mathematics + and one should adhere to mathematical conventions for syntax wherever + possible. Also, 0.5 is a lot more readable than .5; There is no way it + can be mixed with the integer 5. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Floating point comparison should be avoid} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Floating point comparison is dangerous due to rounding errors. It should therefore +be handled with care. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{Layout and Comments} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Layout} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Tabs must be eight characters wide} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + Since the beginnig of time. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Basic indentation should be 2} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + for (i = 0; i < nElements; i++) { + a[i] = 0; + } +\end{code} + +\motivation{ + Indentation of 1 is to small to emphasize the logical + layout of the code. Indentation larger than 4 makes deeply nested + code difficult to read and increase the chance that the lines must + be split. Choosing between indentation of 2, 3 and 4, 2 and 4 are + the more common, and 2 chosen to reduce the chance of splitting code + lines. +} + +Block layout should be as illustrated in example 1 below (recommended) +or example 2, and must not be as shown in example 3. Function and +class blocks must use the block layout of example 1. + +\begin{code} + // example 1 + while (!done) { + doSomething(); + done = moreToDo(); + } + + // example 2 + while (!done) + { + doSomething(); + done = moreToDo(); + } + + // example 3 + while (!done) + { + doSomething(); + done = moreToDo(); + } +\end{code} + +Example 3 introduce an extra indentation level which doesn't emphasize +the logical structure of the code as clearly as example 1 and 2. + +The class declarations should have the following form: + +\begin{code} + class SomeClass : public BaseClass { + public: + void doSomething (); + + protected: + ... + + private: + ... + } +\end{code} + +This follows partly from the general block rule above. + +The function declarations should have the following form: + +\begin{code} + void someMethod () { + ... + } +\end{code} + +This follows from the general block rule above. Note that there is an +extra space before the \trcode{()}. When calling a function there +should be no space. + +The \trcode{if-else} statements should have the following form: + +\begin{code} + if (condition) { + statements; + } + + if (condition) { + statements; + } + else { + statements; + } + + if (condition) { + statements; + } + else if (condition) { + statements; + } + else { + statements; + } +\end{code} + +This follows partly from the general block rule above. An else clause should not +be on the same line as the closing bracket of the previous if or else clause: + +\begin{code} + if (condition) { + statements; + } else { // NO + statements; + } +\end{code} + +\motivation{ + This is equivalent to the Sun recommendation. The chosen approach is + considered better in the way that each part of the \trcode{if-else} statement + is written on separate lines of the file. This should make it easier + to manipulate the statement, for instance when moving else clauses + around. +} + +A for statement should have the following form: + +\begin{code} + for (initialization; condition; update) { + statements; + } +\end{code} + +This follows from the general block rule above. + +An empty for statement should have the following form: + +\begin{code} + for (initialization; condition; update) { + } +\end{code} + +\motivation{ + This emphasize the fact that the for statement is empty and it makes + it obvious for the reader that this is intentional. Empty loops should + be avoided however. +} + +A while statement should have the following form: + +\begin{code} + while (condition) { + statements; + } +\end{code} + +This follows from the general block rule above. + +A do-while statement should have the following form: + +\begin{code} + do { + statements; + } while (condition); +\end{code} + +This follows from the general block rule above. + +A switch statement should have the following form: + +\begin{code} + switch (condition) { + case ABC: + statements; + // Fallthrough + + case DEF: + statements; + break; + + case XYZ1: + case XYZ2: + statements; + break; + + default: + statements; + break; + } +\end{code} + +Note that each \trcode{case} keyword is indented relative to the switch +statement as a whole. Note also that no extra space before the : character +exits. The explicit \trcode{// Fallthrough} comment must be included whenever +there is a case statement without a \trcode{break} statement. Leaving the +\trcode{break} out is a common error, and it must be made clear that it is +intentional when it is not there. + +A \trcode{try-catch} statement should have the following form: + +\begin{code} + try { + statements; + } + catch (Exception &exception) { + statements; + } +\end{code} + +This follows partly from the general block rule above. The discussion about +closing brackets for \trcode{if-else} statements apply to the \trcode{try-catch} +statments. + +The function return type must be put in the immediately before the function +name. + +\begin{code} + void MyClass::myMethod (void) { + ... + } +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Fallthrough comment must be used} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See the example above. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Empty blocks or on-line blocks must use brackets} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Even if a block contains only a single statement, it must be enclosed in +brackets. + +\begin{code} + if (0 == a) { + doIt(); + } + + if (1 == a) { + // do nothing because ... + } +\end{code} + +\motivation{ + If a if-clause starts out is one-line and is later extended it is easy to forget + the brackets. + + It is a common recommendation (Sun Java recommendation included) that + brackets should always be used in all these cases. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{White Space} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Conventional operators should be surrounded by a space character} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +C++ reserved words should be followed by a white space. Commas should be +followed by a white space. Colons should be surrounded by white space. Colons +in \trcode{case} statements should not be surrounded by white space. Semicolons +in \trcode{for} statments should be followed by one or two (recommended) space +characters. + +\begin{code} + a = (b + c) * d; // NOT: a=(b+c)*d + + while (true) { // NOT: while(true) ... + + doSomething(a, b, c, d); // NOT: doSomething(a,b,c,d); + + case 100: // NOT: case 100 : + + for (i = 0; i < 10; i++) { // NOT: for (i=0;i<10;i++){ +\end{code} + +\motivation{ + Makes the individual components of the statements stand + out. Enhances readability. It is difficult to give a complete list + of the suggested use of whitespace in C++ code. The examples above + however should give a general idea of the intentions. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{The method name should be followed by a space} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Method or function names should be followed by a white space in the declaration +or implementation, but not when calling. + +\begin{code} + void doSomething (FILE *currentFile) { + } + + doSomething(currentFile); +\end{code} + +\motivation{ + Allows to search for \trcode{doSomething (} to find the + declaration or implementation. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Logical units within a block should be separated by one blank line} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A blank line should be inserted before and after a \trcode{if} or \trcode{while} +block. + +\begin{code} + int f = 1; + + if (f > e) { + if (g > h) { + int a = f + e + g + h; + + if (a == 1) { + ... + } + } + + int x = f + 1; + + ... + } +\end{code} + +\motivation{ + Enhance readability by introducing white space between logical units of a block. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Methods should be separated by blank lines} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +They should be separated with three blank lines in larger files. + +\motivation{ + The methods will stand out within the file. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Variables in declarations can be left aligned} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + AsciiFile * file; + int nPoints; + float x, y; +\end{code} + +\motivation{ + Enhance readability. The variables are easier to spot from the types by alignment. + Use alignment wherever it enhances readability. +} + +\begin{code} + if (a == lowValue) compueSomething(); + else if (a == mediumValue) computeSomethingElse(); + else if (a == highValue) computeSomethingElseYet(); + + value = (potential * oilDensity) / constant1 + + (depth * waterDensity) / constant2 + + (zCoordinateValue * gasDensity) / constant3; + + minPosition = computeDistance (min, x, y, z); + averagePosition = computeDistance (average, x, y, z); + + switch (value) { + case PHASE_OIL: strcpy (string, "Oil"); break; + case PHASE_WATER: strcpy (string, "Water"); break; + case PHASE_GAS: strcpy (string, "Gas"); break; + } +\end{code} + +\motivation{ + There are a number of places in the code where white space can be + included to enhance readability even if this violates common + guidelines. Many of these cases have to do with code + alignment. General guidelines on code alignment are difficult to give, + but the examples above should give a general clue. +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Comments} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Tricky code should not be commented but rewritten} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + In general, the use of comments should be minimized by making the code + self-documenting by appropriate name choices and an explicit logical + structure. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{All comments should be written in english} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\motivation{ + In an international environment english is the preferred language. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Use \trcode{//} for all comments, including multi-line comments} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\begin{code} + // Comment spanning + // more than one line. +\end{code} + +\motivation{ + Since multilevel C-commenting is not supported, using \trcode{//} comments + ensure that it is always possible to comment out entire sections of a + file using \trcode{/* */} for debugging purposes etc. +} + +There should be a space between the \trcode{//} and the actual comment. +Comments should be included relative to their position in the code. + +\begin{code} + while (true) { // NOT: while (true) { + // Do something // // Do something + something(); // something(); + } // } +\end{code} + +\motivation{ + This is to avoid that the comments break the logical structure of the program. +} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Must use development comments only during development} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The comments \trcode{FIXME} and \trcode{TODO} must be used to flag code blocks +which must be refactored. The release code must be free of such comments and +code blocks. + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{You must use doxygen conventions for C++} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Class and method header comments must follow the Doxygen conventions. + +\begin{code} + //////////////////////////////////////////////////////////////////////////////// + /// @brief returns the maximum of two numbers + //////////////////////////////////////////////////////////////////////////////// +\end{code} + +\motivation{ + Regarding standardized class and method documentation the Java development + community is far more mature than the C++. This is of course becuase Java + includes a tool for extracting such comments and produce high quality + hypertext documentation from it. There have never been a common convention + for writing this kind of documentation in C++, so when choosing between + inventing your own convention, and using an existing one, the latter option + seem natural. Also, there are JavaDoc tools for C++ available. See for + instance Doc++ or Doxygen. +} + +For inherited methods use + +\begin{code} + //////////////////////////////////////////////////////////////////////////////// + /// {@inheritDoc} + //////////////////////////////////////////////////////////////////////////////// +\end{code} + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\guideline{Sectionise large files} +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use section descriptions to split large files into sections. + +\begin{code} + // ///////////////////////////////////////////////////////////////////////////// + // constructors and destructors + // ///////////////////////////////////////////////////////////////////////////// + + ... + + // ///////////////////////////////////////////////////////////////////////////// + // public methods + // ///////////////////////////////////////////////////////////////////////////// + + ... +\end{code} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapter{References} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\label{cha:references} + +\begin{enumerate} + \item + Code Complete, \\ + Steve McConnel - Microsoft Press + \label{bib:1} + + \item + Programming in C++, Rules and Recommendations, \\ + M Henricson, e. Nyquist, Ellemtel (Swedish telecom), \\ + http://www.doc.ic.ac.uk/lab/cplus/c%2b%2b.rules/ + \label{bib:2} + + \item + Wildfire C++ Programming Style, \\ + Keith Gabryelski, Wildfire Communications Inc., \\ + http://www.wildfire.com/~ag/Engineering/Development/C++Style/ + \label{bib:3} + + \item + C++ Coding Standard, \\ + Todd Hoff, \\ + http://www.possibility.com/Cpp/CppCodingStandard.htm + \label{bib:4} + + \item + Doxygen documentation system, \\ + http://www.stack.nl/~dimitri/doxygen/index.html + \label{bib:5} +\end{enumerate} +% +\end{document} +% +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 748451210b..856aee56e3 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -744,7 +744,8 @@ SHELL_CLIENT_ONLY = \ @top_srcdir@/js/client/tests/shell-endpoints.js \ @top_srcdir@/js/client/tests/shell-client.js \ @top_srcdir@/js/client/tests/shell-fm.js \ - @top_srcdir@/js/client/tests/shell-request.js + @top_srcdir@/js/client/tests/shell-request.js \ + @top_srcdir@/js/client/tests/shell-require-canceled.js SHELL_CLIENT = $(SHELL_COMMON) $(SHELL_CLIENT_ONLY) diff --git a/arangod/Aql/AqlItemBlock.cpp b/arangod/Aql/AqlItemBlock.cpp index 71826bcd0a..5a148641c4 100644 --- a/arangod/Aql/AqlItemBlock.cpp +++ b/arangod/Aql/AqlItemBlock.cpp @@ -231,7 +231,7 @@ void AqlItemBlock::shrink (size_t nrItems) { if (nrItems > _nrItems) { // cannot use shrink() to increase the size of the block - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "cannout use shrink() to increase block"); } // erase all stored values in the region that we freed diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index d9faf9e259..4de60b348f 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -1210,7 +1210,7 @@ bool AstNode::isFalse () const { bool AstNode::isAttributeAccessForVariable (std::pair>& result) const { if (type != NODE_TYPE_ATTRIBUTE_ACCESS && type != NODE_TYPE_EXPANSION) { - return nullptr; + return false; } // initialize @@ -1224,7 +1224,10 @@ bool AstNode::isAttributeAccessForVariable (std::pairtype == NODE_TYPE_ATTRIBUTE_ACCESS || node->type == NODE_TYPE_EXPANSION) { if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) { - result.second.insert(result.second.begin(), triagens::basics::AttributeName(std::string(node->getStringValue(), node->getStringLength()), expandNext)); + result.second.insert( + result.second.begin(), + triagens::basics::AttributeName(std::string(node->getStringValue(), node->getStringLength()), expandNext) + ); node = node->getMember(0); expandNext = false; } diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index 02271ab758..516523d8a9 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -478,17 +478,18 @@ namespace triagens { } auto node = this; - + while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS || node->type == NODE_TYPE_EXPANSION) { if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) { node = node->getMember(0); } else { - // expansion + // expansion, i.e. [*] TRI_ASSERT(node->type == NODE_TYPE_EXPANSION); + TRI_ASSERT(node->numMembers() >= 2); - if (node->getMember(1)->getMember(1)->getAttributeAccessForVariable() == nullptr) { + if (node->getMember(1)->getAttributeAccessForVariable() == nullptr) { return nullptr; } diff --git a/arangod/Aql/Condition.cpp b/arangod/Aql/Condition.cpp index 49935b3bb7..0d2209ca4f 100644 --- a/arangod/Aql/Condition.cpp +++ b/arangod/Aql/Condition.cpp @@ -72,9 +72,11 @@ ConditionPart::~ConditionPart () { } +#if 0 void ConditionPart::dump () const { std::cout << "VARIABLE NAME: " << variable->name << "." << attributeName << " " << triagens::basics::JsonHelper::toString(valueNode->toJson(TRI_UNKNOWN_MEM_ZONE, false)) << "\n"; } +#endif // ----------------------------------------------------------------------------- // --SECTION-- class Condition @@ -84,6 +86,7 @@ void ConditionPart::dump () const { // --SECTION-- static helper function // ----------------------------------------------------------------------------- +#if 0 static void dumpNode (AstNode const* node, int indent) { if (node == nullptr) { return; @@ -111,6 +114,7 @@ static void dumpNode (AstNode const* node, int indent) { dumpNode(node->getMember(i), indent + 1); } } +#endif // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors @@ -140,6 +144,24 @@ Condition::~Condition () { // --SECTION-- public functions // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a condition from JSON +//////////////////////////////////////////////////////////////////////////////// + +Condition* Condition::fromJson (ExecutionPlan* plan, + triagens::basics::Json const& json) { + std::unique_ptr condition(new Condition(plan->getAst())); + + std::unique_ptr node(new AstNode(plan->getAst(), json)); + condition->andCombine(node.get()); + node.release(); + + condition->_isNormalized = true; + //condition->normalize(plan); + + return condition.release(); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief clone the condition //////////////////////////////////////////////////////////////////////////////// @@ -303,10 +325,12 @@ void Condition::normalize (ExecutionPlan* plan) { optimize(plan); +#if 0 std::cout << "\n"; dump(); std::cout << "\n"; _isNormalized = true; +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -675,9 +699,11 @@ AstNode* Condition::mergeInOperations (AstNode const* lhs, /// @brief dump the condition for debug purposes //////////////////////////////////////////////////////////////////////////////// +#if 0 void Condition::dump () const { dumpNode(_root, 0); } +#endif AstNode* Condition::transformNode (AstNode* node) { if (node == nullptr) { @@ -693,10 +719,12 @@ AstNode* Condition::transformNode (AstNode* node) { } TRI_ASSERT(node->type != NODE_TYPE_OPERATOR_BINARY_AND && - node->type != NODE_TYPE_OPERATOR_BINARY_OR); + node->type != NODE_TYPE_OPERATOR_BINARY_OR); if (node->type == NODE_TYPE_OPERATOR_NARY_AND) { // first recurse into subnodes + + // TODO: this will not work in the general case with an NARY_AND node having 0..n children node->changeMember(0, transformNode(node->getMember(0))); node->changeMember(1, transformNode(node->getMember(1))); @@ -730,8 +758,10 @@ AstNode* Condition::transformNode (AstNode* node) { } else if (node->type == NODE_TYPE_OPERATOR_NARY_OR) { // recurse into subnodes - node->changeMember(0, transformNode(node->getMember(0))); - node->changeMember(1, transformNode(node->getMember(1))); + size_t const n = node->numMembers(); + for (size_t i = 0; i < n; ++i) { + node->changeMember(i, transformNode(node->getMemberUnchecked(i))); + } } else if (node->type == NODE_TYPE_OPERATOR_UNARY_NOT) { // push down logical negations diff --git a/arangod/Aql/Condition.h b/arangod/Aql/Condition.h index 5374e6d3c3..77bcc60ada 100644 --- a/arangod/Aql/Condition.h +++ b/arangod/Aql/Condition.h @@ -103,11 +103,13 @@ namespace triagens { } } +#if 0 //////////////////////////////////////////////////////////////////////////////// /// @brief dump the condition for debug purposes //////////////////////////////////////////////////////////////////////////////// void dump () const; +#endif Variable const* variable; std::string const attributeName; @@ -175,14 +177,20 @@ namespace triagens { /// @brief return the condition as a Json object //////////////////////////////////////////////////////////////////////////////// - triagens::basics::Json toJson (TRI_memory_zone_t* zone) const { + triagens::basics::Json toJson (TRI_memory_zone_t* zone, bool verbose) const { if (_root == nullptr) { return triagens::basics::Json(triagens::basics::Json::Object); } - return triagens::basics::Json(zone, _root->toJson(zone, false)); + return triagens::basics::Json(zone, _root->toJson(zone, verbose)); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a condition from JSON +//////////////////////////////////////////////////////////////////////////////// + + static Condition* fromJson (ExecutionPlan*, triagens::basics::Json const&); + //////////////////////////////////////////////////////////////////////////////// /// @brief clone the condition //////////////////////////////////////////////////////////////////////////////// @@ -221,7 +229,9 @@ namespace triagens { /// @brief dump the condition //////////////////////////////////////////////////////////////////////////////// +#if 0 void dump () const; +#endif // ----------------------------------------------------------------------------- // --SECTION-- private methods diff --git a/arangod/Aql/ConditionFinder.cpp b/arangod/Aql/ConditionFinder.cpp index 5dfd983df0..76de978fa7 100644 --- a/arangod/Aql/ConditionFinder.cpp +++ b/arangod/Aql/ConditionFinder.cpp @@ -104,7 +104,7 @@ bool ConditionFinder::before (ExecutionNode* en) { case EN::ENUMERATE_COLLECTION: { auto node = static_cast(en); if (_changes->find(node->id()) != _changes->end()) { - std::cout << "Already optimized " << node->id() << std::endl; + // already optimized this node break; } diff --git a/arangod/Aql/ExecutionEngine.cpp b/arangod/Aql/ExecutionEngine.cpp index ae8b64b787..61eb6a53fd 100644 --- a/arangod/Aql/ExecutionEngine.cpp +++ b/arangod/Aql/ExecutionEngine.cpp @@ -730,21 +730,13 @@ struct CoordinatorInstanciator : public WalkerWorker { if (nodeType == ExecutionNode::GATHER) { // we found a gather node - TRI_ASSERT(remoteNode != nullptr); + if (remoteNode == nullptr) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "expecting a remoteNode"); + } // now we'll create a remote node for each shard and add it to the gather node - Collection const* collection = nullptr; - if (nodeType == ExecutionNode::GATHER) { - collection = static_cast((*en))->collection(); - } - else { - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); - } + Collection const* collection = static_cast((*en))->collection(); - if (remoteNode == nullptr) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); - } - auto&& shardIds = collection->shardIds(); for (auto const& shardId : shardIds) { diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index a51815e4a3..5658d7eaec 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -131,7 +131,7 @@ void ExecutionNode::getSortElements (SortElementVector& elements, triagens::basics::Json oneJsonElement = jsonElements.at(static_cast(i)); bool ascending = JsonHelper::checkAndGetBooleanValue(oneJsonElement.json(), "ascending"); Variable *v = varFromJson(plan->getAst(), oneJsonElement, "inVariable"); - elements.emplace_back(std::make_pair(v, ascending)); + elements.emplace_back(v, ascending); } } @@ -143,6 +143,7 @@ ExecutionNode* ExecutionNode::fromJsonFactory (ExecutionPlan* plan, validateType(nodeTypeID); NodeType nodeType = (NodeType) nodeTypeID; + switch (nodeType) { case SINGLETON: return new SingletonNode(plan, oneNode); diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 516ed252b3..b6a1036c43 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -1773,8 +1773,7 @@ void ExecutionPlan::insertDependency (ExecutionNode* oldNode, ExecutionNode* ExecutionPlan::fromJson (triagens::basics::Json const& json) { ExecutionNode* ret = nullptr; triagens::basics::Json nodes = json.get("nodes"); - //std::cout << nodes.toString() << "\n"; - + if (! nodes.isArray()) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "nodes is not an array"); } @@ -1789,8 +1788,8 @@ ExecutionNode* ExecutionPlan::fromJson (triagens::basics::Json const& json) { if (! oneJsonNode.isObject()) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "json node is not an object"); } - ret = ExecutionNode::fromJsonFactory(this, oneJsonNode); + ret = ExecutionNode::fromJsonFactory(this, oneJsonNode); registerNode(ret); TRI_ASSERT(ret != nullptr); diff --git a/arangod/Aql/Executor.cpp b/arangod/Aql/Executor.cpp index cef6daf1ba..97ce2308dd 100644 --- a/arangod/Aql/Executor.cpp +++ b/arangod/Aql/Executor.cpp @@ -583,11 +583,11 @@ void Executor::HandleV8Error (v8::TryCatch& tryCatch, } // we can't figure out what kind of error occurred and throw a generic error - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unknown error in scripting"); } if (result.IsEmpty()) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unknown error in scripting"); } // if we get here, no exception has been raised diff --git a/arangod/Aql/Functions.cpp b/arangod/Aql/Functions.cpp index 613969ae7c..622621bdfe 100644 --- a/arangod/Aql/Functions.cpp +++ b/arangod/Aql/Functions.cpp @@ -1723,6 +1723,22 @@ static Json VertexIdToJson (triagens::arango::AqlTransaction* trx, VertexId const& id) { TRI_doc_mptr_copy_t mptr; auto collection = trx->trxCollection(id.cid); + if (collection == nullptr) { + int res = TRI_AddCollectionTransaction(trx->getInternals(), + id.cid, + TRI_TRANSACTION_READ, + trx->nestingLevel(), + true, + true); + if (res != TRI_ERROR_NO_ERROR) { + THROW_ARANGO_EXCEPTION(res); + } + TRI_EnsureCollectionsTransaction(trx->getInternals()); + collection = trx->trxCollection(id.cid); + if (collection == nullptr) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "collection is a nullptr"); + } + } int res = trx->readSingle(collection, &mptr, id.key); if (res != TRI_ERROR_NO_ERROR) { diff --git a/arangod/Aql/IndexBlock.cpp b/arangod/Aql/IndexBlock.cpp index 5e0577f82c..68bfef9ce6 100644 --- a/arangod/Aql/IndexBlock.cpp +++ b/arangod/Aql/IndexBlock.cpp @@ -65,7 +65,6 @@ IndexBlock::IndexBlock (ExecutionEngine* engine, _context(nullptr), _iterator(nullptr), _condition(en->_condition->root()), - _freeCondition(false), _hasV8Expression(false) { _context = new IndexIteratorContext(en->_vocbase); @@ -80,10 +79,6 @@ IndexBlock::IndexBlock (ExecutionEngine* engine, IndexBlock::~IndexBlock () { delete _iterator; delete _context; - - if (_freeCondition && _condition != nullptr) { - delete _condition; - } } void IndexBlock::buildExpressions () { @@ -106,208 +101,20 @@ void IndexBlock::buildExpressions () { ->getMember(toReplace.andMember) ->changeMember(toReplace.operatorMember, evaluatedNode); } - - /* - bool const useHighBounds = this->useHighBounds(); - // TODO - std::unique_ptr newCondition; - - for (size_t i = 0; i < en->_ranges.size(); i++) { - size_t const n = en->_ranges[i].size(); - // prefill with n default-constructed vectors - std::vector> collector(n); - - // collect the evaluated bounds here - for (size_t k = 0; k < n; k++) { - auto const& r = en->_ranges[i][k]; - - { - // First create a new RangeInfo containing only the constant - // low and high bound of r: - RangeInfo riConst(r._var, r._attr, r._lowConst, r._highConst, r.is1ValueRangeInfo()); - collector[k].emplace_back(std::move(riConst)); - } - - // Now work the actual values of the variable lows and highs into - // this constant range: - for (auto const& l : r._lows) { - Expression* e = _allVariableBoundExpressions[posInExpressions]; - TRI_ASSERT(e != nullptr); - AqlValue a = e->execute(_trx, cur, _pos, _inVars[posInExpressions], _inRegs[posInExpressions], &myCollection); - posInExpressions++; - - Json bound; - if (a._type == AqlValue::JSON) { - bound = *(a._json); - a.destroy(); // the TRI_json_t* of a._json has been stolen - } - else if (a._type == AqlValue::SHAPED || a._type == AqlValue::DOCVEC) { - bound = a.toJson(_trx, myCollection, true); - a.destroy(); // the TRI_json_t* of a._json has been stolen - } - else { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "AQL: computed a variable bound and got non-JSON"); - } - - if (! bound.isArray()) { - if (useHighBounds) { - auto b(bound.copy()); - - RangeInfo ri(r._var, - r._attr, - RangeInfoBound(l.inclusive(), true, b), // will steal b's JSON - RangeInfoBound(), - false); - - for (size_t j = 0; j < collector[k].size(); j++) { - collector[k][j].fuse(ri); - } - } - else { - auto b1(bound.copy()); // first instance of bound - auto b2(bound.copy()); // second instance of same bound - - RangeInfo ri(r._var, - r._attr, - RangeInfoBound(l.inclusive(), true, b1), // will steal b1's JSON - RangeInfoBound(l.inclusive(), true, b2), // will steal b2's JSON - false); - - for (size_t j = 0; j < collector[k].size(); j++) { - collector[k][j].fuse(ri); - } - } - } - else { - std::vector riv; - riv.reserve(bound.size()); - - for (size_t j = 0; j < bound.size(); j++) { - auto b1(bound.at(static_cast(j)).copy()); // first instance of bound - auto b2(bound.at(static_cast(j)).copy()); // second instance of same bound - - riv.emplace_back(RangeInfo(r._var, - r._attr, - RangeInfoBound(l.inclusive(), true, b1), // will steal b1's JSON - RangeInfoBound(l.inclusive(), true, b2), // will steal b2's JSON - true)); - } - - collector[k] = std::move(andCombineRangeInfoVecs(collector[k], riv)); - } - } - - if (useHighBounds) { - for (auto const& h : r._highs) { - Expression* e = _allVariableBoundExpressions[posInExpressions]; - TRI_ASSERT(e != nullptr); - TRI_document_collection_t const* myCollection = nullptr; - AqlValue a = e->execute(_trx, cur, _pos, _inVars[posInExpressions], _inRegs[posInExpressions], &myCollection); - posInExpressions++; - - Json bound; - if (a._type == AqlValue::JSON) { - bound = *(a._json); - a.destroy(); // the TRI_json_t* of a._json has been stolen - } - else if (a._type == AqlValue::SHAPED || a._type == AqlValue::DOCVEC) { - bound = a.toJson(_trx, myCollection, true); - a.destroy(); // the TRI_json_t* of a._json has been stolen - } - else { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "AQL: computed a variable bound and got non-JSON"); - } - if (! bound.isArray()) { - auto b(bound.copy()); - RangeInfo ri(r._var, - r._attr, - RangeInfoBound(), - RangeInfoBound(h.inclusive(), true, b), // will steal b's JSON - false); - - for (size_t j = 0; j < collector[k].size(); j++) { - collector[k][j].fuse(ri); - } - } - else { - std::vector riv; - riv.reserve(bound.size()); - - for (size_t j = 0; j < bound.size(); j++) { - auto b1(bound.at(static_cast(j)).copy()); // first instance of bound - auto b2(bound.at(static_cast(j)).copy()); // second instance of same bound - - riv.emplace_back(RangeInfo(r._var, - r._attr, - RangeInfoBound(h.inclusive(), true, b1), // will steal b1's JSON - RangeInfoBound(h.inclusive(), true, b2), // will steal b2's JSON - true)); - } - - collector[k] = std::move(andCombineRangeInfoVecs(collector[k], riv)); - } - } - } - } - - - bool isEmpty = false; - - for (auto const& x : collector) { - if (x.empty()) { - isEmpty = true; - break; - } - } - - if (! isEmpty) { - // otherwise the condition is impossible to fulfill - // the elements of the direct product of the collector are and - // conditions which should be added to newCondition - - // create cartesian product - std::unique_ptr indexAnds(cartesian(collector)); - - if (newCondition == nullptr) { - newCondition.reset(indexAnds.release()); - } - else { - for (auto const& indexAnd : *indexAnds) { - newCondition->emplace_back(std::move(indexAnd)); - } - } - } - - } - - freeCondition(); - - if (newCondition != nullptr) { - _condition = newCondition.release(); - _freeCondition = true; - - // remove duplicates . . . - removeOverlapsIndexOr(*_condition); - } - else { - _condition = new IndexOrCondition; - _freeCondition = true; - } - */ } int IndexBlock::initialize () { ENTER_BLOCK int res = ExecutionBlock::initialize(); +/* + // why is this called again here? it has already been called in the constructor...!? if (res == TRI_ERROR_NO_ERROR) { if (_trx->orderDitch(_trx->trxCollection(_collection->cid())) == nullptr) { res = TRI_ERROR_OUT_OF_MEMORY; } } - +*/ _nonConstExpressions.clear(); _alreadyReturned.clear(); @@ -349,9 +156,9 @@ int IndexBlock::initialize () { auto outVariable = static_cast(getPlanNode())->outVariable(); for (size_t i = 0; i < _condition->numMembers(); ++i) { - auto andCond = _condition->getMember(i); + auto andCond = _condition->getMemberUnchecked(i); for (size_t j = 0; j < andCond->numMembers(); ++j) { - auto leaf = andCond->getMember(j); + auto leaf = andCond->getMemberUnchecked(j); // We only support binary conditions TRI_ASSERT(leaf->numMembers() == 2); @@ -366,7 +173,8 @@ int IndexBlock::initialize () { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } } - } else { + } + else { // Index is responsible for the right side, check if left side has to be evaluated if (! lhs->isConstant()) { instantiateExpression(i, j, 0, lhs); @@ -400,8 +208,6 @@ int IndexBlock::initialize () { bool IndexBlock::initIndexes () { ENTER_BLOCK - _flag = true; - // We start with a different context. Return documents found in the previous context again. _alreadyReturned.clear(); @@ -451,7 +257,7 @@ bool IndexBlock::initIndexes () { } } } - + _currentIndex = 0; auto outVariable = static_cast(getPlanNode())->outVariable(); auto ast = static_cast(getPlanNode())->_plan->getAst(); @@ -472,14 +278,6 @@ bool IndexBlock::initIndexes () { LEAVE_BLOCK; } -void IndexBlock::freeCondition () { - if (_condition != nullptr && _freeCondition) { - delete _condition; - _condition = nullptr; - _freeCondition = false; - } -} - void IndexBlock::startNextIterator () { delete _iterator; _iterator = nullptr; diff --git a/arangod/Aql/IndexBlock.h b/arangod/Aql/IndexBlock.h index 692d77e0d9..0e02d6e760 100644 --- a/arangod/Aql/IndexBlock.h +++ b/arangod/Aql/IndexBlock.h @@ -146,24 +146,12 @@ namespace triagens { void buildExpressions (); -//////////////////////////////////////////////////////////////////////////////// -/// @brief free _condition if it belongs to us -//////////////////////////////////////////////////////////////////////////////// - - void freeCondition (); - //////////////////////////////////////////////////////////////////////////////// /// @brief continue fetching of documents //////////////////////////////////////////////////////////////////////////////// bool readIndex (size_t atMost); -//////////////////////////////////////////////////////////////////////////////// -// @brief: sorts the index range conditions and resets _posInRanges to 0 -//////////////////////////////////////////////////////////////////////////////// - - void sortConditions (); - // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -247,29 +235,7 @@ namespace triagens { std::unordered_set _alreadyReturned; -//////////////////////////////////////////////////////////////////////////////// -/// @brief _condition: holds the complete condition this Block can serve for -//////////////////////////////////////////////////////////////////////////////// - - AstNode* _evaluatedCondition; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief _flag: since readIndex for primary, hash, edges indexes reads the -/// whole index, this is if initIndex has been called but readIndex has -/// not been called, otherwise it is to avoid rereading the entire index -/// with successive calls to readIndex. -////////////////////////////////////////////////////////////////////////////////// - - bool _flag; size_t _posInRanges; - std::vector _sortCoords; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief _freeCondition: whether or not the _condition is owned by the -/// IndexRangeBlock and must be freed -//////////////////////////////////////////////////////////////////////////////// - - bool _freeCondition; bool _hasV8Expression; diff --git a/arangod/Aql/IndexNode.cpp b/arangod/Aql/IndexNode.cpp index 07cff64237..4bc2746be6 100644 --- a/arangod/Aql/IndexNode.cpp +++ b/arangod/Aql/IndexNode.cpp @@ -63,12 +63,10 @@ void IndexNode::toJsonHelper (triagens::basics::Json& nodes, for (auto& index : _indexes) { indexes.add(index->toJson()); } - - json("indexes", indexes); - json("condition", _condition->toJson(TRI_UNKNOWN_MEM_ZONE)); - - json("reverse", triagens::basics::Json(_reverse)); + json("indexes", indexes); + json("condition", _condition->toJson(TRI_UNKNOWN_MEM_ZONE, verbose)); + json("reverse", triagens::basics::Json(_reverse)); // And add it: nodes(json); @@ -83,9 +81,8 @@ ExecutionNode* IndexNode::clone (ExecutionPlan* plan, outVariable = plan->getAst()->variables()->createVariable(outVariable); } - // TODO: check if we need to clone _condition or if we can reuse it here auto c = new IndexNode(plan, _id, _vocbase, _collection, - outVariable, _indexes, _condition, _reverse); + outVariable, _indexes, _condition->clone(), _reverse); cloneHelper(c, plan, withDependencies, withProperties); @@ -106,7 +103,7 @@ IndexNode::IndexNode (ExecutionPlan* plan, _condition(nullptr), _reverse(JsonHelper::checkAndGetBooleanValue(json.json(), "reverse")) { - auto indexes = JsonHelper::checkAndGetObjectValue(json.json(), "indexes"); + auto indexes = JsonHelper::checkAndGetArrayValue(json.json(), "indexes"); TRI_ASSERT(TRI_IsArrayJson(indexes)); size_t length = TRI_LengthArrayJson(indexes); @@ -123,7 +120,12 @@ IndexNode::IndexNode (ExecutionPlan* plan, _indexes.emplace_back(index); } - // TODO: rebuild _condition here!! + TRI_json_t const* condition = JsonHelper::checkAndGetObjectValue(json.json(), "condition"); + + triagens::basics::Json conditionJson(TRI_UNKNOWN_MEM_ZONE, condition, triagens::basics::Json::NOFREE); + _condition = Condition::fromJson(plan, conditionJson); + + TRI_ASSERT(_condition != nullptr); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index b540e83909..538746cbfe 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -2054,8 +2054,6 @@ int triagens::aql::useIndexesRule (Optimizer* opt, n->walk(&finder); } - std::cout << "Candidates to replace " << changes.size() << std::endl; - if (! changes.empty()) { for (auto& it : changes) { plan->registerNode(it.second); diff --git a/arangod/Aql/grammar.cpp b/arangod/Aql/grammar.cpp index e8e144b909..b5f5aa7fab 100644 --- a/arangod/Aql/grammar.cpp +++ b/arangod/Aql/grammar.cpp @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.0.2. */ +/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "3.0.2" +#define YYBISON_VERSION "3.0.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -176,7 +176,7 @@ extern int Aqldebug; /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef union YYSTYPE YYSTYPE; + union YYSTYPE { #line 17 "arangod/Aql/grammar.y" /* yacc.c:355 */ @@ -191,6 +191,8 @@ union YYSTYPE #line 193 "arangod/Aql/grammar.cpp" /* yacc.c:355 */ }; + +typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif @@ -250,7 +252,7 @@ void Aqlerror (YYLTYPE* locp, #define scanner parser->scanner() -#line 254 "arangod/Aql/grammar.cpp" /* yacc.c:358 */ +#line 256 "arangod/Aql/grammar.cpp" /* yacc.c:358 */ #ifdef short # undef short @@ -1771,187 +1773,187 @@ yyreduce: switch (yyn) { case 2: -#line 205 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 205 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1778 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1780 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 3: -#line 207 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 207 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1785 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1787 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 4: -#line 209 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 209 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1792 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1794 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 5: -#line 211 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 211 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1799 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1801 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 6: -#line 213 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 213 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1806 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1808 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 7: -#line 215 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 215 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1813 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1815 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 8: -#line 220 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 220 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1820 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1822 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 9: -#line 222 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 222 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1827 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1829 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 10: -#line 227 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 227 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // still need to close the scope opened by the data-modification statement parser->ast()->scopes()->endNested(); } -#line 1836 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1838 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 11: -#line 231 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 231 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // the RETURN statement will close the scope opened by the data-modification statement } -#line 1844 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1846 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 12: -#line 237 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 237 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1851 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1853 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 13: -#line 239 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 239 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1858 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1860 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 14: -#line 244 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 244 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1865 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1867 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 15: -#line 246 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 246 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1872 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1874 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 16: -#line 248 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 248 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1879 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1881 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 17: -#line 250 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 250 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1886 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1888 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 18: -#line 252 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 252 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1893 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1895 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 19: -#line 254 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 254 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1900 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1902 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 20: -#line 259 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 259 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeFor((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node), true); parser->ast()->addOperation(node); } -#line 1911 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1913 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 21: -#line 268 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 268 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // operand is a reference. can use it directly auto node = parser->ast()->createNodeFilter((yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 1921 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1923 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 22: -#line 276 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 276 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1928 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1930 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 23: -#line 281 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 281 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1935 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1937 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 24: -#line 283 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 283 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1942 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1944 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 25: -#line 288 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 288 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeLet((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node), true); parser->ast()->addOperation(node); } -#line 1951 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1953 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 26: -#line 295 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 295 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! TRI_CaseEqualString((yyvsp[-2].strval).value, "COUNT")) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected qualifier '%s', expecting 'COUNT'", (yyvsp[-2].strval).value, yylloc.first_line, yylloc.first_column); @@ -1959,20 +1961,20 @@ yyreduce: (yyval.strval) = (yyvsp[0].strval); } -#line 1963 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1965 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 27: -#line 305 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 305 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 1972 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1974 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 28: -#line 308 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 308 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); @@ -1981,11 +1983,11 @@ yyreduce: } (yyval.node) = list; } -#line 1985 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1987 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 29: -#line 319 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 319 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); @@ -2002,11 +2004,11 @@ yyreduce: auto node = parser->ast()->createNodeCollectCount(parser->ast()->createNodeArray(), (yyvsp[-1].strval).value, (yyvsp[-1].strval).length, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2006 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2008 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 30: -#line 335 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 335 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); @@ -2034,11 +2036,11 @@ yyreduce: auto node = parser->ast()->createNodeCollectCount((yyvsp[-2].node), (yyvsp[-1].strval).value, (yyvsp[-1].strval).length, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2038 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2040 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 31: -#line 362 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 362 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); @@ -2066,11 +2068,11 @@ yyreduce: auto node = parser->ast()->createNodeCollect((yyvsp[-2].node), (yyvsp[-1].strval).value, (yyvsp[-1].strval).length, nullptr, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2070 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2072 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 32: -#line 389 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 389 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); @@ -2103,11 +2105,11 @@ yyreduce: auto node = parser->ast()->createNodeCollect((yyvsp[-3].node), (yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2107 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2109 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 33: -#line 421 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 421 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); @@ -2135,52 +2137,52 @@ yyreduce: auto node = parser->ast()->createNodeCollectExpression((yyvsp[-5].node), (yyvsp[-3].strval).value, (yyvsp[-3].strval).length, (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2139 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2141 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 34: -#line 451 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 451 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2146 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2148 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 35: -#line 453 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 453 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2153 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2155 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 36: -#line 458 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 458 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeAssign((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node)); parser->pushArrayElement(node); } -#line 2162 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2164 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 37: -#line 465 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 465 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval).value = nullptr; (yyval.strval).length = 0; } -#line 2171 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2173 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 38: -#line 469 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 469 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval).value = (yyvsp[0].strval).value; (yyval.strval).length = (yyvsp[0].strval).length; } -#line 2180 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2182 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 39: -#line 476 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 476 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->ast()->scopes()->existsVariable((yyvsp[0].strval).value, (yyvsp[0].strval).length)) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "use of unknown variable '%s' for KEEP", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2195,11 +2197,11 @@ yyreduce: node->setFlag(FLAG_KEEP_VARIABLENAME); parser->pushArrayElement(node); } -#line 2199 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2201 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 40: -#line 490 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 490 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->ast()->scopes()->existsVariable((yyvsp[0].strval).value, (yyvsp[0].strval).length)) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "use of unknown variable '%s' for KEEP", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2214,11 +2216,11 @@ yyreduce: node->setFlag(FLAG_KEEP_VARIABLENAME); parser->pushArrayElement(node); } -#line 2218 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2220 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 41: -#line 507 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 507 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! TRI_CaseEqualString((yyvsp[0].strval).value, "KEEP")) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected qualifier '%s', expecting 'KEEP'", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2227,140 +2229,140 @@ yyreduce: auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2231 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2233 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 42: -#line 514 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 514 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); (yyval.node) = list; } -#line 2240 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2242 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 43: -#line 521 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 521 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2249 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2251 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 44: -#line 524 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 524 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); auto node = parser->ast()->createNodeSort(list); parser->ast()->addOperation(node); } -#line 2259 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2261 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 45: -#line 532 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 532 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2267 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2269 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 46: -#line 535 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 535 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2275 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2277 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 47: -#line 541 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 541 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeSortElement((yyvsp[-1].node), (yyvsp[0].node)); } -#line 2283 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2285 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 48: -#line 547 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 547 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 2291 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2293 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 49: -#line 550 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 550 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 2299 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2301 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 50: -#line 553 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 553 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(false); } -#line 2307 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2309 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 51: -#line 556 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 556 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2315 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2317 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 52: -#line 562 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 562 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto offset = parser->ast()->createNodeValueInt(0); auto node = parser->ast()->createNodeLimit(offset, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2325 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2327 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 53: -#line 567 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 567 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeLimit((yyvsp[-2].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2334 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2336 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 54: -#line 574 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 574 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeReturn((yyvsp[0].node)); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } -#line 2344 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2346 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 55: -#line 582 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 582 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2352 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2354 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 56: -#line 585 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 585 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2360 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2362 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 57: -#line 591 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 591 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2368,11 +2370,11 @@ yyreduce: auto node = parser->ast()->createNodeRemove((yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2372 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2374 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 58: -#line 601 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 601 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2380,11 +2382,11 @@ yyreduce: auto node = parser->ast()->createNodeInsert((yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2384 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2386 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 59: -#line 611 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 611 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2393,11 +2395,11 @@ yyreduce: AstNode* node = parser->ast()->createNodeUpdate(nullptr, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2397 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2399 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 60: -#line 619 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 619 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2406,18 +2408,18 @@ yyreduce: AstNode* node = parser->ast()->createNodeUpdate((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2410 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2412 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 61: -#line 630 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 630 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2417 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2419 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 62: -#line 635 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 635 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2426,11 +2428,11 @@ yyreduce: AstNode* node = parser->ast()->createNodeReplace(nullptr, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2430 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2432 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 63: -#line 643 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 643 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2439,44 +2441,44 @@ yyreduce: AstNode* node = parser->ast()->createNodeReplace((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2443 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2445 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 64: -#line 654 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 654 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2450 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2452 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 65: -#line 659 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 659 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = static_cast(NODE_TYPE_UPDATE); } -#line 2458 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2460 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 66: -#line 662 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 662 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = static_cast(NODE_TYPE_REPLACE); } -#line 2466 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2468 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 67: -#line 668 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 668 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // reserve a variable named "$OLD", we might need it in the update expression // and in a later return thing parser->pushStack(parser->ast()->createNodeVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD), true)); } -#line 2476 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2478 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 68: -#line 672 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 672 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2521,11 +2523,11 @@ yyreduce: auto node = parser->ast()->createNodeUpsert(static_cast((yyvsp[-3].intval)), parser->ast()->createNodeReference(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD)), (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2525 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2527 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 69: -#line 719 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 719 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto const scopeType = parser->ast()->scopes()->type(); @@ -2534,83 +2536,83 @@ yyreduce: parser->registerParseError(TRI_ERROR_QUERY_PARSE, "cannot use DISTINCT modifier on top-level query element", yylloc.first_line, yylloc.first_column); } } -#line 2538 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2540 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 70: -#line 726 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 726 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeDistinct((yyvsp[0].node)); } -#line 2546 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2548 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 71: -#line 729 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 729 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2554 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2556 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 72: -#line 735 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 735 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2562 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2564 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 73: -#line 738 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 738 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2570 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2572 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 74: -#line 741 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 741 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2578 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2580 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 75: -#line 744 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 744 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2586 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2588 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 76: -#line 747 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 747 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2594 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2596 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 77: -#line 750 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 750 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeRange((yyvsp[-2].node), (yyvsp[0].node)); } -#line 2602 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2604 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 78: -#line 756 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 756 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 2610 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2612 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 79: -#line 759 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 759 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { std::string temp((yyvsp[-2].strval).value, (yyvsp[-2].strval).length); temp.append("::"); @@ -2624,205 +2626,205 @@ yyreduce: (yyval.strval).value = p; (yyval.strval).length = temp.size(); } -#line 2628 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2630 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 80: -#line 775 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 775 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushStack((yyvsp[0].strval).value); auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2639 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2641 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 81: -#line 780 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 780 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); (yyval.node) = parser->ast()->createNodeFunctionCall(static_cast(parser->popStack()), list); } -#line 2648 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2650 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 82: -#line 787 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 787 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_PLUS, (yyvsp[0].node)); } -#line 2656 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2658 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 83: -#line 790 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 790 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_MINUS, (yyvsp[0].node)); } -#line 2664 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2666 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 84: -#line 793 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 793 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_NOT, (yyvsp[0].node)); } -#line 2672 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2674 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 85: -#line 799 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 799 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_OR, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2680 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2682 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 86: -#line 802 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 802 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_AND, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2688 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2690 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 87: -#line 805 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 805 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_PLUS, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2696 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2698 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 88: -#line 808 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 808 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MINUS, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2704 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2706 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 89: -#line 811 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 811 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_TIMES, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2712 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2714 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 90: -#line 814 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 814 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_DIV, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2720 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2722 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 91: -#line 817 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 817 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MOD, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2728 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2730 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 92: -#line 820 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 820 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_EQ, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2736 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2738 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 93: -#line 823 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 823 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2744 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2746 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 94: -#line 826 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 826 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LT, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2752 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2754 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 95: -#line 829 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 829 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GT, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2760 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2762 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 96: -#line 832 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 832 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2768 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2770 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 97: -#line 835 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 835 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2776 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2778 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 98: -#line 838 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 838 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_IN, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2784 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2786 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 99: -#line 841 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 841 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NIN, (yyvsp[-3].node), (yyvsp[0].node)); } -#line 2792 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2794 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 100: -#line 847 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 847 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeTernaryOperator((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2800 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2802 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 101: -#line 853 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 853 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2807 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2809 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 102: -#line 855 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 855 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2814 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2816 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 103: -#line 860 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 860 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2822 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2824 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 104: -#line 863 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 863 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (parser->isModificationQuery()) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected subquery after data-modification operation", yylloc.first_line, yylloc.first_column); @@ -2830,11 +2832,11 @@ yyreduce: parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_SUBQUERY); parser->ast()->startSubQuery(); } -#line 2834 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2836 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 105: -#line 869 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 869 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); @@ -2845,98 +2847,98 @@ yyreduce: (yyval.node) = parser->ast()->createNodeReference(variableName); } -#line 2849 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2851 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 106: -#line 882 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 882 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2857 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2859 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 107: -#line 885 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 885 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2865 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2867 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 108: -#line 891 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 891 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2873 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2875 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 109: -#line 894 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 894 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2881 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2883 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 110: -#line 900 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 900 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2890 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2892 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 111: -#line 903 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 903 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = static_cast(parser->popStack()); } -#line 2898 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2900 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 112: -#line 909 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 909 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2905 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2907 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 113: -#line 911 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 911 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2912 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2914 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 114: -#line 916 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 916 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2920 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2922 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 115: -#line 919 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 919 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2928 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2930 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 116: -#line 925 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 925 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 2936 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2938 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 117: -#line 928 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 928 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -2948,56 +2950,56 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 2952 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2954 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 118: -#line 942 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 942 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeObject(); parser->pushStack(node); } -#line 2961 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2963 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 119: -#line 945 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 945 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = static_cast(parser->popStack()); } -#line 2969 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2971 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 120: -#line 951 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 951 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2976 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2978 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 121: -#line 953 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 953 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2983 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2985 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 122: -#line 958 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 958 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2990 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2992 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 123: -#line 960 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 960 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2997 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2999 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 124: -#line 965 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 965 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // attribute-name-only (comparable to JS enhanced object literals, e.g. { foo, bar }) auto ast = parser->ast(); @@ -3012,20 +3014,20 @@ yyreduce: auto node = ast->createNodeReference(variable); parser->pushObjectElement((yyvsp[0].strval).value, (yyvsp[0].strval).length, node); } -#line 3016 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3018 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 125: -#line 979 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 979 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // attribute-name : attribute-value parser->pushObjectElement((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node)); } -#line 3025 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3027 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 126: -#line 983 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 983 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // bind-parameter : attribute-value if ((yyvsp[-2].strval).length < 1 || (yyvsp[-2].strval).value[0] == '@') { @@ -3035,92 +3037,92 @@ yyreduce: auto param = parser->ast()->createNodeParameter((yyvsp[-2].strval).value, (yyvsp[-2].strval).length); parser->pushObjectElement(param, (yyvsp[0].node)); } -#line 3039 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3041 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 127: -#line 992 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 992 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // [ attribute-name-expression ] : attribute-value parser->pushObjectElement((yyvsp[-3].node), (yyvsp[0].node)); } -#line 3048 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3050 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 128: -#line 999 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 999 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = 1; } -#line 3056 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3058 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 129: -#line 1002 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1002 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = (yyvsp[-1].intval) + 1; } -#line 3064 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3066 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 130: -#line 1008 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1008 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3072 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3074 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 131: -#line 1011 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1011 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3080 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3082 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 132: -#line 1017 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1017 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3088 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3090 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 133: -#line 1020 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1020 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeArrayLimit(nullptr, (yyvsp[0].node)); } -#line 3096 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3098 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 134: -#line 1023 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1023 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeArrayLimit((yyvsp[-2].node), (yyvsp[0].node)); } -#line 3104 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3106 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 135: -#line 1029 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1029 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3112 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3114 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 136: -#line 1032 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1032 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3120 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3122 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 137: -#line 1038 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1038 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // variable or collection auto ast = parser->ast(); @@ -3153,27 +3155,27 @@ yyreduce: (yyval.node) = node; } -#line 3157 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3159 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 138: -#line 1070 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1070 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3165 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3167 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 139: -#line 1073 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1073 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3173 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3175 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 140: -#line 1076 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1076 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); @@ -3181,11 +3183,11 @@ yyreduce: ABORT_OOM } } -#line 3185 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3187 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 141: -#line 1083 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1083 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[-1].node)->type == NODE_TYPE_EXPANSION) { // create a dummy passthru node that reduces and evaluates the expansion first @@ -3196,11 +3198,11 @@ yyreduce: (yyval.node) = (yyvsp[-1].node); } } -#line 3200 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3202 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 142: -#line 1093 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1093 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (parser->isModificationQuery()) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected subquery after data-modification operation", yylloc.first_line, yylloc.first_column); @@ -3208,11 +3210,11 @@ yyreduce: parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_SUBQUERY); parser->ast()->startSubQuery(); } -#line 3212 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3214 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 143: -#line 1099 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1099 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); @@ -3223,11 +3225,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeReference(variableName); } -#line 3227 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3229 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 144: -#line 1109 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1109 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // named variable access, e.g. variable.reference if ((yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3243,11 +3245,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeAttributeAccess((yyvsp[-2].node), (yyvsp[0].strval).value, (yyvsp[0].strval).length); } } -#line 3247 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3249 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 145: -#line 1124 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1124 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // named variable access, e.g. variable.@reference if ((yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3262,11 +3264,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeBoundAttributeAccess((yyvsp[-2].node), (yyvsp[0].node)); } } -#line 3266 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3268 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 146: -#line 1138 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1138 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // indexed variable access, e.g. variable[index] if ((yyvsp[-3].node)->type == NODE_TYPE_EXPANSION) { @@ -3281,11 +3283,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeIndexedAccess((yyvsp[-3].node), (yyvsp[-1].node)); } } -#line 3285 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3287 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 147: -#line 1152 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1152 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // variable expansion, e.g. variable[*], with optional FILTER, LIMIT and RETURN clauses if ((yyvsp[0].intval) > 1 && (yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3309,11 +3311,11 @@ yyreduce: auto scopes = parser->ast()->scopes(); scopes->stackCurrentVariable(scopes->getVariable(nextName)); } -#line 3313 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3315 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 148: -#line 1174 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1174 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); scopes->unstackCurrentVariable(); @@ -3332,27 +3334,27 @@ yyreduce: (yyval.node) = parser->ast()->createNodeExpansion((yyvsp[-5].intval), iterator, parser->ast()->createNodeReference(variable->name), (yyvsp[-3].node), (yyvsp[-2].node), (yyvsp[-1].node)); } } -#line 3336 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3338 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 149: -#line 1195 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1195 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3344 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3346 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 150: -#line 1198 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1198 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3352 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3354 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 151: -#line 1204 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1204 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -3360,11 +3362,11 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 3364 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3366 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 152: -#line 1211 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1211 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -3372,67 +3374,67 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 3376 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3378 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 153: -#line 1221 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1221 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3384 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3386 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 154: -#line 1224 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1224 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3392 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3394 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 155: -#line 1227 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1227 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueNull(); } -#line 3400 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3402 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 156: -#line 1230 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1230 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 3408 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3410 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 157: -#line 1233 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1233 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(false); } -#line 3416 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3418 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 158: -#line 1239 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1239 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeCollection((yyvsp[0].strval).value, TRI_TRANSACTION_WRITE); } -#line 3424 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3426 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 159: -#line 1242 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1242 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeCollection((yyvsp[0].strval).value, TRI_TRANSACTION_WRITE); } -#line 3432 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3434 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 160: -#line 1245 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1245 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].strval).length < 2 || (yyvsp[0].strval).value[0] != '@') { parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -3440,43 +3442,43 @@ yyreduce: (yyval.node) = parser->ast()->createNodeParameter((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3444 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3446 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 161: -#line 1255 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1255 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeParameter((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3452 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3454 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 162: -#line 1261 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1261 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3460 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3462 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 163: -#line 1264 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1264 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3468 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3470 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 164: -#line 1269 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1269 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3476 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3478 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; -#line 3480 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3482 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires diff --git a/arangod/Aql/grammar.h b/arangod/Aql/grammar.h index 6990814479..e2118626e9 100644 --- a/arangod/Aql/grammar.h +++ b/arangod/Aql/grammar.h @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.0.2. */ +/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -110,10 +110,10 @@ extern int Aqldebug; /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef union YYSTYPE YYSTYPE; + union YYSTYPE { -#line 17 "arangod/Aql/grammar.y" /* yacc.c:1909 */ +#line 17 "arangod/Aql/grammar.y" /* yacc.c:1915 */ triagens::aql::AstNode* node; struct { @@ -123,8 +123,10 @@ union YYSTYPE bool boolval; int64_t intval; -#line 127 "arangod/Aql/grammar.hpp" /* yacc.c:1909 */ +#line 127 "arangod/Aql/grammar.hpp" /* yacc.c:1915 */ }; + +typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif diff --git a/arangod/Dispatcher/Dispatcher.cpp b/arangod/Dispatcher/Dispatcher.cpp index 04bb8b43da..c97acd5c5a 100644 --- a/arangod/Dispatcher/Dispatcher.cpp +++ b/arangod/Dispatcher/Dispatcher.cpp @@ -102,8 +102,7 @@ void Dispatcher::addStandardQueue (size_t nrThreads, size_t maxSize) { /// @brief adds the AQL queue (used for the cluster) //////////////////////////////////////////////////////////////////////////////// -void Dispatcher::addAQLQueue (size_t nrThreads, - size_t maxSize) { +void Dispatcher::addAQLQueue (size_t nrThreads, size_t maxSize) { TRI_ASSERT(_queues[AQL_QUEUE] == nullptr); _queues[AQL_QUEUE] = new DispatcherQueue( @@ -278,8 +277,3 @@ void Dispatcher::setProcessorAffinity (size_t id, std::vector const& cor // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- - -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: diff --git a/arangod/Indexes/SimpleAttributeEqualityMatcher.h b/arangod/Indexes/SimpleAttributeEqualityMatcher.h index 5a6b657a88..50fac34941 100644 --- a/arangod/Indexes/SimpleAttributeEqualityMatcher.h +++ b/arangod/Indexes/SimpleAttributeEqualityMatcher.h @@ -262,11 +262,20 @@ namespace triagens { else if (index->hasSelectivityEstimate()) { // use index selectivity estimate estimatedCost = 1.0 - index->selectivityEstimate(); + + // the more attributes are covered by an index, the more accurate it + // is considered to be + estimatedCost /= index->fields().size(); } else { // set to highest possible cost by default estimatedCost = 1.0; + + // the more attributes are covered by an index, the more accurate it + // is considered to be + estimatedCost /= index->fields().size(); } + // can use this index return true; } diff --git a/arangod/Utils/Transaction.h b/arangod/Utils/Transaction.h index ac2602d099..24c86b0793 100644 --- a/arangod/Utils/Transaction.h +++ b/arangod/Utils/Transaction.h @@ -235,6 +235,10 @@ namespace triagens { return TRI_TRANSACTION_UNDEFINED; } + int nestingLevel () const { + return _nestingLevel; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief begin the transaction //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index aef2beb2a8..dfa2c18669 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -349,11 +349,59 @@ void ApplicationV8::setVocbase (TRI_vocbase_t* vocbase) { //////////////////////////////////////////////////////////////////////////////// ApplicationV8::V8Context* ApplicationV8::enterContext (TRI_vocbase_t* vocbase, - bool allowUseDatabase) { + bool allowUseDatabase, + ssize_t forceContext) { v8::Isolate* isolate = nullptr; V8Context* context = nullptr; - { + // this is for TESTING / DEBUGGING only + if (forceContext != -1) { + size_t id = (size_t) forceContext; + + while (! _stopping) { + { + CONDITION_LOCKER(guard, _contextCondition); + + for (auto iter = _freeContexts.begin(); iter != _freeContexts.end(); ++iter) { + if ((*iter)->_id == id) { + context = *iter; + _freeContexts.erase(iter); + _busyContexts.emplace(context); + break; + } + } + + if (context != nullptr) { + break; + } + + for (auto iter = _dirtyContexts.begin(); iter != _dirtyContexts.end(); ++iter) { + if ((*iter)->_id == id) { + context = *iter; + _dirtyContexts.erase(iter); + _busyContexts.emplace(context); + break; + } + } + + if (context != nullptr) { + break; + } + } + + LOG_DEBUG("waiting for V8 context %d to become available", (int) id); + usleep(100 * 1000); + } + + if (context == nullptr) { + return nullptr; + } + + isolate = context->isolate; + } + + // look for a free context + else { CONDITION_LOCKER(guard, _contextCondition); while (_freeContexts.empty() && ! _stopping) { @@ -393,6 +441,7 @@ ApplicationV8::V8Context* ApplicationV8::enterContext (TRI_vocbase_t* vocbase, isolate = context->isolate; _freeContexts.pop_back(); + // should not fail because we reserved enough space beforehand _busyContexts.emplace(context); } @@ -407,6 +456,7 @@ ApplicationV8::V8Context* ApplicationV8::enterContext (TRI_vocbase_t* vocbase, v8::HandleScope scope(isolate); auto localContext = v8::Local::New(isolate, context->_context); localContext->Enter(); + { v8::Context::Scope contextScope(localContext); diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index 27b0943fad..6db7149afc 100644 --- a/arangod/V8Server/ApplicationV8.h +++ b/arangod/V8Server/ApplicationV8.h @@ -331,7 +331,8 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// V8Context* enterContext (TRI_vocbase_t*, - bool useDatabase); + bool useDatabase, + ssize_t forceContext = -1); //////////////////////////////////////////////////////////////////////////////// /// @brief exists an context diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 7815437bbd..2a5a9d6fb7 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -140,9 +140,22 @@ class v8_action_t : public TRI_action_t { allowUseDatabaseInRestActions = true; } + + // this is for TESTING / DEBUGGING only - do not document this feature + ssize_t forceContext = -1; + bool found; + + char const* c = request->header("x-arango-v8-context", found); + + if (found && c != nullptr) { + forceContext = StringUtils::int32(c); + } + + // get a V8 context ApplicationV8::V8Context* context = GlobalV8Dealer->enterContext( vocbase, - allowUseDatabaseInRestActions + allowUseDatabaseInRestActions, + forceContext ); // note: the context might be 0 in case of shut-down diff --git a/arangod/VocBase/transaction.cpp b/arangod/VocBase/transaction.cpp index 4a7e1969d1..3193423023 100644 --- a/arangod/VocBase/transaction.cpp +++ b/arangod/VocBase/transaction.cpp @@ -1013,8 +1013,8 @@ int TRI_AddCollectionTransaction (TRI_transaction_t* trx, /// @brief make sure all declared collections are used & locked //////////////////////////////////////////////////////////////////////////////// -int TRI_EnsureCollectionsTransaction (TRI_transaction_t* trx) { - return UseCollections(trx, 0); +int TRI_EnsureCollectionsTransaction (TRI_transaction_t* trx, int nestingLevel) { + return UseCollections(trx, nestingLevel); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/VocBase/transaction.h b/arangod/VocBase/transaction.h index 41910b64e5..6b00ad4895 100644 --- a/arangod/VocBase/transaction.h +++ b/arangod/VocBase/transaction.h @@ -263,7 +263,7 @@ int TRI_AddCollectionTransaction (TRI_transaction_t*, /// @brief make sure all declared collections are used & locked //////////////////////////////////////////////////////////////////////////////// -int TRI_EnsureCollectionsTransaction (TRI_transaction_t*); +int TRI_EnsureCollectionsTransaction (TRI_transaction_t*, int = 0); //////////////////////////////////////////////////////////////////////////////// /// @brief request a lock for a collection diff --git a/js/client/tests/shell-require-canceled.js b/js/client/tests/shell-require-canceled.js new file mode 100644 index 0000000000..fca6f2a690 --- /dev/null +++ b/js/client/tests/shell-require-canceled.js @@ -0,0 +1,104 @@ +/*jshint globalstrict:false, strict:false */ +/*global assertEqual, arango */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test the require which is canceled +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2015 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2015, ArangoDB GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require('jsunity'); + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite for 'require-canceled' +//////////////////////////////////////////////////////////////////////////////// + +function RequireCanceledTestSuite () { + 'use strict'; + + return { + + setUp() { + arango.POST_RAW("/_admin/execute", + "require('module').globalPaths.unshift(require('path').resolve('./js/common/test-data/modules'));", + {'x-arango-v8-context': 0}); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown() { + arango.POST_RAW("/_admin/execute", + "require('module').globalPaths.splice(0,1);", + {'x-arango-v8-context': 0}); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test requiring JSON +//////////////////////////////////////////////////////////////////////////////// + + testRequireJson() { + var internal = require("internal"); + var a = arango.POST_RAW("/_admin/execute", + 'return Object.keys(require("a"));', + {'x-arango-async': "store", 'x-arango-v8-context': 0}); + internal.sleep(3); + + var id = a.headers['x-arango-async-id']; + arango.PUT_RAW("/_api/job/" + id + "/cancel", ''); + + var c = arango.POST_RAW("/_admin/execute?returnAsJSON=true", + 'return Object.keys(require("a"));', + {'x-arango-async': "false", 'x-arango-v8-context': 0}); + var d; + + try { + d = JSON.parse(c.body); + } + catch (err) { + require("internal").print(c.body); + throw err; + } + + assertEqual(2, d.length); + }, + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- main +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(RequireCanceledTestSuite); + +return jsunity.done(); diff --git a/js/common/test-data/modules/a.js b/js/common/test-data/modules/a.js new file mode 100644 index 0000000000..fadf11ca51 --- /dev/null +++ b/js/common/test-data/modules/a.js @@ -0,0 +1,11 @@ +'use strict'; + +let internal = require("internal"); + +var j; +for (j = 0; j < 5; ++j) { + internal.sleep(1); +} + +exports.c = 10; +exports.a = require("b").b; diff --git a/js/common/test-data/modules/b.js b/js/common/test-data/modules/b.js new file mode 100644 index 0000000000..36e690c4e8 --- /dev/null +++ b/js/common/test-data/modules/b.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.b = require("a").c; diff --git a/js/server/modules/org/arangodb/aql-helper.js b/js/server/modules/org/arangodb/aql-helper.js index b6ad45ba59..99563f1dbb 100644 --- a/js/server/modules/org/arangodb/aql-helper.js +++ b/js/server/modules/org/arangodb/aql-helper.js @@ -403,7 +403,7 @@ function getQueryMultiplePlansAndExecutions (query, bindVars, testObject, debug) // first fetch the unmodified version if (debug) { - require("internal").print("Analysing Query unoptimized: " + query); + require("internal").print("Analyzing Query unoptimized: " + query); } plans[0] = AQL_EXPLAIN(query, bindVars, paramNone); // then all of the ones permuted by by the optimizer. diff --git a/js/server/modules/org/arangodb/foxx/routing.js b/js/server/modules/org/arangodb/foxx/routing.js index 0681b56dcc..d21963b586 100644 --- a/js/server/modules/org/arangodb/foxx/routing.js +++ b/js/server/modules/org/arangodb/foxx/routing.js @@ -676,7 +676,7 @@ var mountController = function (service, routes, mount, filename) { createMiddlewareMatchers(ri.middleware, routes, mount, ri.urlPrefix); } if (ri.hasOwnProperty('routes')) { - transformRoutes(ri.routes, routes, mount, ri.urlPrefix, service._isDevelopment); + transformRoutes(ri.routes, routes, mount, ri.urlPrefix, service.isDevelopment); } } }; diff --git a/js/server/modules/org/arangodb/foxx/service.js b/js/server/modules/org/arangodb/foxx/service.js index 574b041d3e..61146a807a 100644 --- a/js/server/modules/org/arangodb/foxx/service.js +++ b/js/server/modules/org/arangodb/foxx/service.js @@ -179,9 +179,12 @@ class FoxxService { try { this.thumbnail = fs.read64(thumb); } catch (e) { + this.thumbnail = null; + /* console.warnLines( `Cannot read thumbnail "${thumb}" for app "${data.mount}": ${e.stack}` ); + */ } } else { this.thumbnail = null;