CINXE.COM

Developer's Guide - Bugzilla

<!DOCTYPE html> <html lang="en"> <head> <title>Developer's Guide - Bugzilla</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="keywords" content="bugzilla bug track tracking enterprise solution"> <link rel="icon" href="/assets/favicon/favicon.ico" type="image/ico"> <link rel="stylesheet" type="text/css" href="/assets/css/global.css"> <link rel="stylesheet" type="text/css" href="/assets/css/addon.css"> <script defer src="/assets/js/global.js"></script> <link color="#9248c8" href="/assets/favicon/mask-icon.svg" rel="mask-icon"> <meta property="og:site_name" content="Bugzilla"> <meta property="og:title" content="Developer's Guide"> <meta property="og:description" content="Document describing coding standards of Bugzilla" /> <meta property="og:type" content="website"> <meta property="og:image" content="https://www.bugzilla.org/assets/img/banner.png"> <meta property="og:url" content="https://www.bugzilla.org/contributing/developer.html"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="Developer's Guide"> <meta name="twitter:description" content="Document describing coding standards of Bugzilla" /> <meta name="twitter:url" content="https://www.bugzilla.org/contributing/developer.html"> <meta name="twitter:image" content="https://www.bugzilla.org/assets/img/banner.png"> <meta name="twitter:site" content="@bugzilla"/> <meta name="twitter:creator" content="@bugzilla"/> <meta name="theme-color" content="#9248c8"> <link rel="canonical" href="https://www.bugzilla.org/contributing/developer.html"> </head> <body id="www-bugzilla-org" class="homepage"> <header id="header" class=""> <div class="inner"> <a id="header-brand" href="/"> <img id="header-logo" src="/assets/img/logo-header.svg" alt="Bugzilla Logo" width="32" height="48"> <span id="header-name">Bugzilla</span> </a> <button id="navbar-button" title="Show Nav Menu">Show Nav Menu</button> <div id="navbar"> <ul id="navbar-nav"> <li class="dropdown"> <a class="dropdown-toggle" title="Find out more about Bugzilla." href="/about/" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> About </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a title="Find out more about Bugzilla." class="dropdown-item" href="/about/">Project</a> <a class="dropdown-item" href="/about/features.html">Features</a> <a title="Hundreds of companies and organizations trust Bugzilla." class="dropdown-item" href="/about/installation-list.html">Who Uses Bugzilla?</a> <a title="Meet the Bugzilla team." class="dropdown-item" href="/about/developers">Meet the Team</a> <a title="Find out about the company managing the Bugzilla Project." class="dropdown-item" href="/about/zarroboogs.html">Zarro Boogs Corporation</a> </div> </li> <li class="dropdown"> <a class="dropdown-toggle" title="Get the low down on what's happening with the project." href="/blog/" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> News </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a title="Bugzilla Blog" class="dropdown-item" href="/blog/">Blog</a> <a title="Information on current and previous releases." class="dropdown-item" href="/releases/">Release Information</a> <a title="Important updates regarding Bugzilla security." class="dropdown-item" href="/security/">Security Advisories</a> <a title="View the status of the Bugzilla project." class="dropdown-item" href="http://planet.bugzilla.org">Planet Bugzilla</a> </div> </li> <li class=""> <a title="Download Bugzilla" href="/download/">Download</a> </li> <li class=""> <a title="Bugzilla Documentation" href="/docs/">Documentation</a> </li> <li class=""> <a title="Find out where to get more information and help." href="/support/">Support</a> </li> <li class="dropdown"> <a class="dropdown-toggle" title="Ways you can help Bugzilla" href="/contributing/" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Contribute </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a title="Ways you can help Bugzilla" class="dropdown-item" href="/contributing/">How to?</a> <a title="Let us know if there is an error in Bugzilla." class="dropdown-item" href="/contributing/reporting_bugs">Report a Bug</a> <a title="Donate to Bugzilla, and see how your money is used." class="dropdown-item" href="/donate">Donate</a> <a title="Collaboratively updated information about Bugzilla." class="dropdown-item" href="https://wiki.mozilla.org/Bugzilla:Home">Wiki</a> </div> </li> </ul> <form role="search" method="get" action="https://www.google.com/cse"> <span class="sr-only">Search bugzilla.org</span> <input type="hidden" name="cx" value="008043952663535741821:9whwb87ip5a"> <span class="icon"></span> <input type="search" placeholder="Search" name="q" aria-label="Search"> </form> </div> </div> </header> <nav id="breadcrumbs" aria-label="breadcrumb"> <div class="inner"> <ol> <li><a href="/contributing/">Contributing</a></li> <li class="current" aria-current="page">Developer's Guide</li> </ol> </div> </nav> <main> <div class="inner"> <h1>Developer's Guide</h1> <ul id="markdown-toc"> <li><a href="#introduction" id="markdown-toc-introduction">Introduction</a></li> <li><a href="#general-guidelines" id="markdown-toc-general-guidelines">General Guidelines</a></li> <li><a href="#bug-fixing" id="markdown-toc-bug-fixing">Bug Fixing</a></li> <li><a href="#test-suite" id="markdown-toc-test-suite">Test Suite</a></li> <li><a href="#all-files" id="markdown-toc-all-files">All Files</a></li> <li><a href="#perl-code" id="markdown-toc-perl-code">Perl Code</a> <ul> <li><a href="#general" id="markdown-toc-general">General</a></li> <li><a href="#throwing-errors" id="markdown-toc-throwing-errors">Throwing Errors</a></li> <li><a href="#taint-mode" id="markdown-toc-taint-mode">Taint Mode</a></li> <li><a href="#style" id="markdown-toc-style">Style</a></li> <li><a href="#cross-platform-compatibility" id="markdown-toc-cross-platform-compatibility">Cross-Platform Compatibility</a></li> </ul> </li> <li><a href="#api-documentation" id="markdown-toc-api-documentation">API Documentation</a></li> <li><a href="#sql" id="markdown-toc-sql">SQL</a> <ul> <li><a href="#general-1" id="markdown-toc-general-1">General</a></li> <li><a href="#schema-changes" id="markdown-toc-schema-changes">Schema Changes</a> <ul> <li><a href="#how-schema-updates-work" id="markdown-toc-how-schema-updates-work">How Schema Updates Work</a></li> </ul> </li> <li><a href="#how-to-send-and-receive-information-fromto-the-database" id="markdown-toc-how-to-send-and-receive-information-fromto-the-database">How to Send And Receive Information From/To the Database</a> <ul> <li><a href="#placeholders" id="markdown-toc-placeholders">Placeholders</a></li> </ul> </li> <li><a href="#join-statements-and-selecting-from-multiple-tables" id="markdown-toc-join-statements-and-selecting-from-multiple-tables">JOIN Statements and SELECTing From Multiple Tables</a> <ul> <li><a href="#cross-join" id="markdown-toc-cross-join">CROSS JOIN</a></li> <li><a href="#inner-join" id="markdown-toc-inner-join">INNER JOIN</a></li> <li><a href="#left-join" id="markdown-toc-left-join">LEFT JOIN</a></li> </ul> </li> <li><a href="#indexes" id="markdown-toc-indexes">Indexes</a> <ul> <li><a href="#simple-description-of-indexes" id="markdown-toc-simple-description-of-indexes">Simple Description of Indexes</a></li> <li><a href="#more-details" id="markdown-toc-more-details">More Details</a></li> <li><a href="#when-to-add-an-index" id="markdown-toc-when-to-add-an-index">When To Add An Index</a></li> <li><a href="#multi-column-indexes" id="markdown-toc-multi-column-indexes">Multi-Column Indexes</a></li> <li><a href="#write-queries-with-indexes-in-mind" id="markdown-toc-write-queries-with-indexes-in-mind">Write Queries With Indexes In Mind</a></li> </ul> </li> <li><a href="#cross-database-query-compatibility" id="markdown-toc-cross-database-query-compatibility">Cross-Database Query Compatibility</a> <ul> <li><a href="#a-note-about-inner-join" id="markdown-toc-a-note-about-inner-join">A Note About INNER JOIN</a></li> </ul> </li> <li><a href="#style-1" id="markdown-toc-style-1">Style</a></li> </ul> </li> <li><a href="#templates" id="markdown-toc-templates">Templates</a> <ul> <li><a href="#general-2" id="markdown-toc-general-2">General</a></li> <li><a href="#filenames-and-paths" id="markdown-toc-filenames-and-paths">Filenames and Paths</a></li> <li><a href="#html-templates" id="markdown-toc-html-templates">HTML Templates</a></li> <li><a href="#web-technologies" id="markdown-toc-web-technologies">Web Technologies</a></li> </ul> </li> <li><a href="#security" id="markdown-toc-security">Security</a> <ul> <li><a href="#dont-trust-url-parameters-and-cookies" id="markdown-toc-dont-trust-url-parameters-and-cookies">Don’t Trust URL Parameters And Cookies!</a></li> <li><a href="#confidential-information-leakage" id="markdown-toc-confidential-information-leakage">Confidential Information Leakage</a></li> <li><a href="#unauthorised-access-to-perform-an-action" id="markdown-toc-unauthorised-access-to-perform-an-action">Unauthorised Access to Perform an Action</a></li> <li><a href="#arbitrary-code-execution" id="markdown-toc-arbitrary-code-execution">Arbitrary Code Execution</a></li> </ul> </li> </ul> <h2 id="introduction">Introduction</h2> <p>This document describes the code standards for Bugzilla, and gives tips for Bugzilla developers.</p> <p>Code is most likely to be integrated into Bugzilla if it follows these guidelines. Sometimes reviewers will allow you to bend the rules, but there must be a very good reason for doing so.</p> <p>It is always better to contribute <strong>some</strong> code rather than <strong>no</strong> code. If your code isn’t perfect, a reviewer will tell you what you need to change in order for us to accept it. Usually, the reviewer will just tell you things that are already in this document.</p> <p>Existing Bugzilla code does not necessarily conform to these guidelines, but we are working towards them. If you’re only changing 1 or 2 lines, there’s no need to fix up the entire file to make the file conform to these guidelines.</p> <h2 id="general-guidelines">General Guidelines</h2> <p>We have a certain set of “basic principles” that this entire Developer’s Guide is based on:</p> <ul> <li>These guidelines exist because they <strong>reduce bugs</strong> and <strong>make it easier to change things in the future</strong>.</li> <li>Making it easier to change things in the future is important, because an important law of code is: <strong>All Code Will Change</strong>.</li> <li>Code should be <strong>as simple as possible</strong>. When you write code, one of your primary goals should be: <strong>make it easy for other programmers to use and read your code</strong>.</li> <li><strong>Readable</strong> code is more important than <strong>clever</strong> code.</li> <li>If you’re trying to be clever instead of trying to be readable, then maybe you’re trying to make things “faster?” If so, just remember: <strong>don’t solve a problem before you <em>know it exists</em></strong>. If you don’t <em>know</em> (by actual, detailed tests) that your code is slow, don’t worry about making it “faster.” This isn’t just limited to optimization–many programmers are constantly solving problems that nobody has ever experienced. Don’t do that.</li> <li><strong>You cannot introduce new bugs unless you change code.</strong> That should be obvious. :-) This means: <ul> <li><strong>The more code you change, the more bugs you <em>will</em> create.</strong> There are no “perfect” programmers. Everybody makes a few mistakes now and then.</li> <li><strong>The number of bugs introduced by a patch is proportional to the size of the patch.</strong></li> <li>Thus, <strong>patches should be as small as possible</strong>, and should change only what they need to change.</li> </ul> </li> <li>An easy way to check if your code is readable, is to ask yourself, “Will another programmer understand this line <em>instantly</em> when they look at it?” If not, that line either needs to be re-written or needs a comment.</li> </ul> <h2 id="bug-fixing">Bug Fixing</h2> <p>When you fix a bug, ask yourself:</p> <ul> <li>Where else in the codebase might this bug occur? Search for other places, and either fix these or file them as new bug reports.</li> <li>How could you, or the Bugzilla team in general, prevent this problem in future? Consider reusable subroutines that could be introduced, rearchitecture that could be done, or tests that could be added to the testing suite. File new bug reports on these.</li> <li>Could this have regressed anything?</li> </ul> <h2 id="test-suite">Test Suite</h2> <p>Nothing will get checked into Bugzilla that doesn’t pass the test suite. The test suite is automatically executed on each commit by the <a href="https://travis-ci.org/bugzilla/bugzilla">Travis CI</a> system to warn us of bad code. It is using Bugzilla’s <a href="https://github.com/bugzilla/bugzilla">GitHub mirror</a>. Anything that fails to pass the testing suite will be backed out immediately.</p> <p>The test suite continues to expand to detect more problems as the Bugzilla architecture matures and our understanding of potential problems improves. If you add new code, consider adding a new test.</p> <p>Currently, it primarily diagnoses problems in code similar to a “lint” like tool. However, it does do some tests on the small but growing “back end” of Bugzilla.</p> <p>In order to run the tests, you must have the <code class="language-plaintext highlighter-rouge">Test::More</code> and <code class="language-plaintext highlighter-rouge">Test::Harness</code> Perl modules installed. You can then <code class="language-plaintext highlighter-rouge">./runtests.pl</code> or <code class="language-plaintext highlighter-rouge">./runtests.pl --verbose</code> from your Bugzilla main directory.</p> <h2 id="all-files">All Files</h2> <ul> <li>The testing suite will check files have no tab characters. Tabs are not reliable for positioning because different editors interpret them differently.</li> <li>The testing suite will check files for some common or past misspellings.</li> <li>File names should be legal across multiple platforms. <code class="language-plaintext highlighter-rouge">\</code> <code class="language-plaintext highlighter-rouge">/</code> <code class="language-plaintext highlighter-rouge">:</code> <code class="language-plaintext highlighter-rouge">*</code> <code class="language-plaintext highlighter-rouge">?</code> <code class="language-plaintext highlighter-rouge">"</code> <code class="language-plaintext highlighter-rouge">&lt;</code> <code class="language-plaintext highlighter-rouge">&gt;</code> and <code class="language-plaintext highlighter-rouge">|</code> are all illegal characters for filenames on various platforms. Also, file names should not have spaces in them as they can cause confusion in CVS and other mozilla.org utilities. You may assume long filename support is present.</li> </ul> <h2 id="perl-code">Perl Code</h2> <h3 id="general">General</h3> <ul> <li> <p>The testing suite will check all Perl files have no errors. This is done by using the <code class="language-plaintext highlighter-rouge">perl -c</code> command to compile the files.</p> </li> <li> <p>The testing suite will check that all Perl files have the appropriate shebang switches (-w plus -T where required), and that <code class="language-plaintext highlighter-rouge">use strict</code> is present.</p> </li> <li> <p><strong>Variable Names</strong> - Do not use global variables (like <code class="language-plaintext highlighter-rouge">$::variable</code>). They make the code harder to use and prone to errors as they can be altered at unexpected places. Use local variables instead. Their name must be understandable and descriptive. For example, <code class="language-plaintext highlighter-rouge">$product</code> could be used as a variable referencing a product.</p> </li> <li> <p><strong>Variables In Regular Expressions</strong> - One common bug is mis-using variables inside of regular expressions. When the variable contains regular expression operators such as <code class="language-plaintext highlighter-rouge">?</code>, <code class="language-plaintext highlighter-rouge">*</code> and <code class="language-plaintext highlighter-rouge">+</code>, bugs occur. This typically occurs with <code class="language-plaintext highlighter-rouge">grep</code> or <code class="language-plaintext highlighter-rouge">=~</code>. You should use:</p> <p><code class="language-plaintext highlighter-rouge">grep ($_ eq $value, @array);</code></p> <p>instead of this:</p> <p><code class="language-plaintext highlighter-rouge">grep (/$value/, @array);</code></p> <p>If you need to use a variable inside of an expression, be sure to quote it properly (using <code class="language-plaintext highlighter-rouge">\Q..\E</code>), like:</p> <p><code class="language-plaintext highlighter-rouge">grep (/blah\Q$value\Eblah/, @array);</code></p> <p>If your variable is actually <em>supposed</em> to contain a regular expression, then you don’t have to escape it. In this case, it is a good idea to include “regexp” in the <em>name of the variable</em>, for readability purposes. For example, <code class="language-plaintext highlighter-rouge">$search_regexp</code>.</p> </li> <li> <p>CGIs should call <code class="language-plaintext highlighter-rouge">Bugzilla-&gt;login</code> before determining user identity, as this will perform the proper verifications against impersonation.</p> </li> <li> <p>All non-obvious pieces of code should have comments explaining what they do, and why they are needed. This means in particular “hacky kludges” and “workarounds.”</p> </li> </ul> <h3 id="throwing-errors">Throwing Errors</h3> <p>Normally, when an error occurs because of user action, you call <code class="language-plaintext highlighter-rouge">ThrowUserError</code>, eg:</p> <p><code class="language-plaintext highlighter-rouge">ThrowUserError("user_made_mistake");</code></p> <p>whereas when an error occurs because of a situation that should never happen (indicating a bug), call <code class="language-plaintext highlighter-rouge">ThrowCodeError</code>, eg:</p> <p><code class="language-plaintext highlighter-rouge">ThrowCodeError("bugzilla_is_broken");</code></p> <p>However, when it comes to throwing errors, there are a few important rules:</p> <ol> <li>Don’t blame the user if there’s doubt whether it’s the user’s fault.</li> <li>Don’t tell the user to email the admin if the error doesn’t mean Bugzilla is broken.</li> </ol> <p>That means: use <code class="language-plaintext highlighter-rouge">ThrowUserError</code> every time that Bugzilla isn’t <em>broken</em>, even if you’re not sure it’s a user error. This rule overrides the general recommendations at the top of this section.</p> <h3 id="taint-mode">Taint Mode</h3> <p>All new CGIs should run in Perl’s taint mode, and existing CGIs which run in taint mode must not have taint mode turned off.</p> <p>Taint mode works by marking untrusted data as “tainted”, and not allowing tainted data to be passed to various places. This ensures a multitude of security holes cannot occur.</p> <p>For example, the values of <code class="language-plaintext highlighter-rouge">$cgi-&gt;param</code> variables and data from browser cookies are tainted, and tainted data may not be passed in to the database. Doing so will cause the CGI to generate an error and terminate.</p> <p>Instead you must first “detaint” the data. The only way to do this directly is to get the untrusted data to match a regular expression and extract parts from it. So for example, to detaint a positive integer in the variable <code class="language-plaintext highlighter-rouge">$foo</code>:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nv">$foo</span> <span class="o">=~</span> <span class="sr">/^(\d+)$/</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$foo</span> <span class="o">=</span> <span class="err">$</span><span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1"># ERROR!</span> <span class="p">}</span> </code></pre></div></div> <p>Afterwards <code class="language-plaintext highlighter-rouge">$foo</code> will be detainted.</p> <p>Note that there are a few convenience functions you should use to make life easier:</p> <ul> <li><code class="language-plaintext highlighter-rouge">detaint_natural</code> detaints a non-negative integer as in the example above.</li> <li><code class="language-plaintext highlighter-rouge">detaint_signed</code> detaints a signed (negative or positive) integer, similar to how <code class="language-plaintext highlighter-rouge">detaint_natural</code> works.</li> <li><code class="language-plaintext highlighter-rouge">trick_taint</code> will detaint a piece of data unconditionally, but this should only be used when you really know this is OK (as opposed to because you’re lazy), because it bypasses the taint mode security system. If you use <code class="language-plaintext highlighter-rouge">trick_taint</code>, add a comment explaining why it’s safe to use it.</li> </ul> <p>For more information on extracting from regular expressions, see <a href="http://search.cpan.org/dist/perl/pod/perlre.pod">perl’s documentation on regular expressions</a>. For more information on taint mode, see <a href="http://search.cpan.org/dist/perl/pod/perlsec.pod">perl’s security documentation</a>.</p> <p>Note that Bugzilla does NOT use DBI “taint-out” mode, so data <em>returned</em> from the database will not be tainted.</p> <h3 id="style">Style</h3> <ul> <li> <p>The Bugzilla development team has decided to adopt the Perl style guide as published by Larry Wall. This guide can be found in <code class="language-plaintext highlighter-rouge">Programming Perl</code> (the camel book), by typing <code class="language-plaintext highlighter-rouge">man perlstyle</code> at your favourite shell prompt, or by reading <a href="http://search.cpan.org/dist/perl/pod/perlstyle.pod">the latest version</a> online.</p> <p>This style is only for Perl. For template style, see the template section.</p> </li> <li> <p>Indentation - Perl code should have 4-space indent.</p> </li> <li> <p>Curly Braces - The opening brace of a block should be on the same line as the statement that is causing the block and the closing brace should be at the same indentation level as that statement, for example:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nv">$var</span><span class="p">)</span> <span class="p">{</span> <span class="k">print</span> <span class="p">"</span><span class="s2">The variable is true</span><span class="p">";</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">print</span> <span class="p">"</span><span class="s2">Try again</span><span class="p">";</span> <span class="p">}</span> </code></pre></div> </div> <p>instead of this:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nv">$var</span><span class="p">)</span> <span class="p">{</span> <span class="k">print</span> <span class="p">"</span><span class="s2">The variable is true</span><span class="p">";</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">print</span> <span class="p">"</span><span class="s2">Try again</span><span class="p">";</span> <span class="p">}</span> </code></pre></div> </div> </li> <li> <p>Please refer to the perl style guide above if you don’t see your question covered here.</p> </li> </ul> <h3 id="cross-platform-compatibility">Cross-Platform Compatibility</h3> <ul> <li> <p>Bugzilla supports *nix platforms, and both ActiveState Perl and cygwin on Win32 platforms.</p> </li> <li> <p>For this reason, *nix-specific features should either be avoided, or only done if you have ensured the platform is Unix, and the code behaves reasonably if not.</p> <p>Some examples of *nixisms: chown, chmod, getgrnam, $&lt; et al, stty and signal handling.</p> </li> <li> <p>Similarly, some features are supported on cygwin, but not ActiveState Perl. You should only use these if you have ensured the platform isn’t ActiveState.</p> <p>Some examples of non-ActiveState features: fork, pipe open syntax (eg <code class="language-plaintext highlighter-rouge">open (DOT, '-|')</code>).</p> </li> </ul> <h2 id="api-documentation">API Documentation</h2> <ul> <li>Recently many Bugzilla modules have been introduced. Some of these even include <a href="https://www.bugzilla.org/docs/tip/html/api/">documentation</a>. Some of these files are still empty, though.</li> </ul> <p>Want to help out with documenting our code? Use the following Perl Documentation example to help you get started:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Perl code can go here.</span> <span class="cm">=head1 NAME Bugzilla::FlagType - A module to deal with Bugzilla flag types. =head1 SYNOPSIS my $flaginfo = get($flag_id); my $count= count($criteria); my $inc_hash = get_inclusions($flag_id); my $exc_hash = get_exclusions($flag_id); =head1 DESCRIPTION FlagType.pm provides an interface to flag types as stored in Bugzilla. See below for more information. =head1 NOTES =over =item * Prior to calling routines in this module, it's assumed that you have already done a C&lt;require CGI.pl&gt;. =item * Use of private functions/variables outside this module may lead to unexpected results after an upgrade. Please avoid using private functions in other files/modules. =back =cut</span> <span class="c1"># More perl code can go here.</span> </code></pre></div></div> <p>For those that are not familiar with writing POD, here are a few things to be aware of:</p> <ul> <li> <p>Some documentation is nearly always better than nothing.</p> </li> <li> <p>POD commands and blocks always start with an equals (=) sign.</p> </li> <li> <p>POD commands must be followed by a blank line to be interpreted properly.</p> </li> <li> <p>To end an inline POD block, use the POD command =cut.</p> </li> <li> <p><strong>If you forget your =cut at the end of your POD section, Perl will ignore any code that follows until it sees an =cut, or the end of the file - whichever comes first.</strong> Many editors like Emacs and VIM have syntax higlighting that understand POD. If you forget your =cut in these editors, you’ll see the POD color continue beyond where you expected your POD to end.</p> </li> <li> <p>Bugzilla PODs should have most (if not all) of the following head1 sections:</p> <ul> <li><code class="language-plaintext highlighter-rouge">NAME</code></li> <li><code class="language-plaintext highlighter-rouge">SYNOPSIS</code></li> <li><code class="language-plaintext highlighter-rouge">DESCRIPTION</code></li> <li><code class="language-plaintext highlighter-rouge">METHODS</code></li> <li><code class="language-plaintext highlighter-rouge">CLASS FUNCTIONS</code></li> <li><code class="language-plaintext highlighter-rouge">NOTES</code></li> <li><code class="language-plaintext highlighter-rouge">SEE ALSO</code></li> </ul> </li> <li> <p>If your module contains private functions and/or variables, use <code class="language-plaintext highlighter-rouge">=begin private</code> and <code class="language-plaintext highlighter-rouge">=end private</code> to hide those functions from POD output, but follow the POD style required for those sections. This allows us to keep the documentation in the module in a consistent format without telling the whole world all our “trade secrets.”</p> </li> <li> <p>Function descriptions should generally follow the format of:&lt;</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C&lt;function_name($var1, $var2, $var3 ...)&gt; Description: Function Description Params: Variable Descriptions Returns: Return Value </code></pre></div> </div> <p>And might look something like this:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">=</span><span class="nv">item</span> <span class="nv">C</span><span class="o">&lt;</span><span class="nv">GiveHelp</span><span class="p">(</span><span class="nv">$fun</span><span class="p">,</span> <span class="p">[</span><span class="nv">$color</span><span class="p">])</span><span class="o">&gt;</span> <span class="nv">Description:</span> <span class="nv">GiveHelp</span> <span class="nv">sends</span> <span class="nv">the</span> <span class="nv">help</span> <span class="nv">string</span> <span class="k">for</span> <span class="nv">the</span> <span class="nv">$fun</span> <span class="nv">function</span><span class="o">.</span> <span class="nv">Params:</span> <span class="nv">C</span><span class="o">&lt;</span><span class="nv">$fun</span><span class="o">&gt;</span> <span class="o">-</span> <span class="nv">The</span> <span class="nv">name</span> <span class="nv">of</span> <span class="nv">the</span> <span class="nv">function</span> <span class="nv">to</span> <span class="nv">be</span> <span class="nv">documented</span><span class="o">.</span> <span class="nv">C</span><span class="o">&lt;</span><span class="nv">$color</span><span class="o">&gt;</span> <span class="p">(</span><span class="nv">optional</span><span class="p">)</span> <span class="o">-</span> <span class="nv">A</span> <span class="nv">string</span><span class="o">.</span> <span class="nv">The</span> <span class="nv">name</span> <span class="nv">of</span> <span class="nv">the</span> <span class="nv">color</span> <span class="nv">of</span> <span class="nv">the</span> <span class="nv">output</span><span class="o">.</span> <span class="nv">Returns:</span> <span class="nv">A</span> <span class="nv">string</span> <span class="nv">containing</span> <span class="nv">HTML</span><span class="o">-</span><span class="nv">formatted</span> <span class="nv">help</span> <span class="k">for</span> <span class="nv">C</span><span class="o">&lt;</span><span class="nv">$fun</span><span class="o">&gt;.</span> </code></pre></div> </div> </li> </ul> <p>For an example of this POD style, see <code class="language-plaintext highlighter-rouge">Bugzilla/DB.pm</code></p> <p>To learn more about inline Perl Documentation, see the manual pages:</p> <ul> <li><a href="http://search.cpan.org/dist/perl/pod/perlpod.pod">Basic POD Overview</a></li> <li><a href="http://search.cpan.org/dist/perl/pod/perlpodspec.pod">Detailed POD Format Specification</a></li> </ul> <h2 id="sql">SQL</h2> <h3 id="general-1">General</h3> <ul> <li><strong>ANSI SQL</strong> - There are three standards for SQL, <a href="http://savage.net.au/SQL/sql-92.bnf.html">SQL 92</a>, <a href="http://savage.net.au/SQL/sql-99.bnf.html">SQL 99</a>, and <a href="http://savage.net.au/SQL/sql-2003-2.bnf.html">SQL 2003</a>. Bugzilla generally tries to conform to SQL 99, where possible.</li> <li>If a feature isn’t ANSI-standard, but is supported by all databases we support, you can go ahead and use it. You can see all the databases we support by looking in the <code class="language-plaintext highlighter-rouge">Bugzilla/DB/</code> directory in a Bugzilla installation. (Every <code class="language-plaintext highlighter-rouge">.pm</code> file except <code class="language-plaintext highlighter-rouge">Schema.pm</code> is a database we support.)</li> <li>Many databases have features that are not ANSI-standard, and are only supported by that one database. These should only be used inside of the <code class="language-plaintext highlighter-rouge">Bugzilla::DB</code> structure. For example, MySQL-specific code should only be used inside of <code class="language-plaintext highlighter-rouge">Bugzilla::DB::Mysql</code> and <code class="language-plaintext highlighter-rouge">Bugzilla::DB::Schema::Mysql</code>.</li> <li>You shouldn’t accept arbitrary SQL from the user, it can easily be a security hole. Short of fully parsing the SQL, use some other method.</li> <li>Regular expression searches done by the database (with $dbh-&gt;sql_regexp) use a subset of the regexp language of Perl, and not all features that work in Perl will work with the database regexps. When in doubt, keep it simple.</li> <li>Databases are (almost) always more clever than you at sorting and searching data quickly. For this reason, it is typically faster to do processing in just one statement than multiple, if you can. If you split your statement up, you will reduce the database’s opportunities to perform the query quickly.</li> <li>Never use <code class="language-plaintext highlighter-rouge">SELECT *</code> – always specify the exact columns you want. Frequently, the order the columns are returned in is important, and if the database schema changes it will be very hard to locate and change your code to deal with the change.</li> <li>Using Numeric IDs - Tables should use integers rather than string names in primary keys. This makes renaming a lot easier and saves database space.</li> <li>Tables should have <em>singular</em> names. For example, “user” instead of “users.”</li> <li>It is usually clearer to have your primary key column be named <code class="language-plaintext highlighter-rouge">table</code>_id rather than just “id.” For example, “bug_id” for the <code class="language-plaintext highlighter-rouge">bugs</code> table instead of just “id.”</li> <li>Similar to perl style, it is better to use underscores to separate different words in column names. “default_qa_contact” is easier to read than “defaultqacontact.”</li> </ul> <h3 id="schema-changes">Schema Changes</h3> <p>If you make schema changes, you should modify:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Bugzilla/DB/Schema.pm</code>, the <code class="language-plaintext highlighter-rouge">ABSTRACT_SCHEMA</code> variable. Documentation on the format is inside of the file itself.</li> <li><code class="language-plaintext highlighter-rouge">Bugzilla::Install::DB</code> to automatically convert old installations. Search for the last occurrence of <code class="language-plaintext highlighter-rouge">--TABLE--</code> in the file and put your new code <em>above</em> that comment.</li> <li>(Pre-4.0) <code class="language-plaintext highlighter-rouge">sanitycheck.cgi</code> to properly check the new schema. This includes updating the “cross checks” (referential checks).</li> </ul> <p>If you’re changing the definition of a column, your checksetup code needs to only run <em>if the column has the old definition</em>. That is, it needs to do something like this:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># If bugs.qa_contact is currently defined as NOT NULL, re-define it without that.</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$dbh</span><span class="o">-&gt;</span><span class="nv">bz_column_info</span><span class="p">('</span><span class="s1">bugs</span><span class="p">',</span> <span class="p">'</span><span class="s1">qa_contact</span><span class="p">')</span><span class="o">-&gt;</span><span class="p">{</span><span class="nv">NOTNULL</span><span class="p">})</span> <span class="p">{</span> <span class="nv">$dbh</span><span class="o">-&gt;</span><span class="nv">bz_alter_column</span><span class="p">('</span><span class="s1">bugs</span><span class="p">',</span> <span class="p">'</span><span class="s1">qa_contact</span><span class="p">',</span> <span class="p">{</span><span class="s">TYPE</span> <span class="o">=&gt;</span> <span class="p">'</span><span class="s1">INT3</span><span class="p">'});</span> <span class="c1">## ... do other stuff here to fix up the new column ... ##</span> <span class="p">}</span> </code></pre></div></div> <h4 id="how-schema-updates-work">How Schema Updates Work</h4> <p>When Bugzilla updates the on-disk database schema, it also updates a Bugzilla::DB::Schema object in the <code class="language-plaintext highlighter-rouge">bz_schema</code> table. This is in <code class="language-plaintext highlighter-rouge">Data::Dumper</code> format, and shows what Bugzilla <em>thinks</em> the on-disk structure is.</p> <p>Why do we do this? Well, because <em>Bugzilla</em> has one idea of what a field’s type is, and the underlying database frequently has a <em>different</em> idea. For example, in MySQL, <code class="language-plaintext highlighter-rouge">bugs.bug_id</code> has the following definition:</p> <p><code class="language-plaintext highlighter-rouge">bug_id MEDIUMINT AUTO_INCREMENT NOT NULL PRIMARY KEY</code></p> <p>In PostgreSQL, that same field has the following definition:</p> <p><code class="language-plaintext highlighter-rouge">bug_id SERIAL UNIQUE NOT NULL PRIMARY KEY</code></p> <p>See how different those are? And yet, to Bugzilla, both of these are represented with the following code:</p> <p><code class="language-plaintext highlighter-rouge">bug_id =&gt; {TYPE =&gt; 'MEDIUMSERIAL', NOTNULL =&gt; 1, PRIMARYKEY =&gt; 1}</code></p> <p>Later, if we want to change bug_id to say, a string (which I hope we never do, but who knows), in <code class="language-plaintext highlighter-rouge">checksetup.pl</code> we need to only do that change if it’s currently <em>not</em> already done. That is, we need to check if bug_id is a “MEDIUMSERIAL” and if so, change it to a string.</p> <p>But guess what? The underlying database has no idea if it’s a MEDIUMSERIAL. But the schema we stored in the <code class="language-plaintext highlighter-rouge">bz_schema</code> table, when we first installed Bugzilla–it knows!</p> <p>This becomes even more important when you think about the fact that on PostgreSQL, <code class="language-plaintext highlighter-rouge">INT1, INT2, INT3,</code> and <code class="language-plaintext highlighter-rouge">INT4</code> are <em>all</em> represented as <code class="language-plaintext highlighter-rouge">INTEGER</code> on the disk. So sometimes Bugzilla needs to know whether something is an INT3 or an INT2, and it can’t just ask the database itself. It has to use the information stored in the bz_schema table.</p> <h3 id="how-to-send-and-receive-information-fromto-the-database">How to Send And Receive Information From/To the Database</h3> <p>Bugzilla is using standard DBI functions to interact with the database, through <code class="language-plaintext highlighter-rouge">Bugzilla-&gt;dbh</code>. The current recommended method for creating a query is as follows:</p> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nv">Bugzilla</span><span class="p">;</span> <span class="k">my</span> <span class="nv">$dbh</span> <span class="o">=</span> <span class="nv">Bugzilla</span><span class="o">-&gt;</span><span class="nv">dbh</span><span class="p">;</span> <span class="c1"># Connects if not already connected.</span> <span class="c1"># Also handles db, user, password...</span> <span class="k">my</span> <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$dbh</span><span class="o">-&gt;</span><span class="nv">selectall_arrayref</span><span class="p">("</span><span class="s2">SELECT foo, bar FROM bath WHERE log = ?</span><span class="p">",</span> <span class="nb">undef</span><span class="p">,</span> <span class="p">"</span><span class="s2">foobar</span><span class="p">");</span> <span class="k">foreach</span> <span class="k">my</span> <span class="nv">$row</span> <span class="p">(</span><span class="nv">@$data</span><span class="p">)</span> <span class="p">{</span> <span class="k">my</span> <span class="p">(</span><span class="nv">$foo</span><span class="p">,</span> <span class="nv">$bar</span><span class="p">)</span> <span class="o">=</span> <span class="nv">@$row</span><span class="p">;</span> <span class="c1"># do whatever with $foo and $bar</span> <span class="p">}</span> </code></pre></div></div> <p>For more information, see the <a href="http://search.cpan.org/dist/DBI/DBI.pm">DBI documentation</a> or the <a href="tip/html/api/Bugzilla/DB.html">Bugzilla::DB documentation</a>.</p> <h4 id="placeholders">Placeholders</h4> <p>Note that in the above example we use a question mark instead of inserting the string “foobar” directly into the SQL. That question mark is called a “placeholder.” This is the recommended method for passing in <em>all</em> variables into SQL statements.</p> <p>Here’s an example of some SQL with a placeholder:</p> <p><code class="language-plaintext highlighter-rouge">SELECT short_desc FROM bugs WHERE bug_id = ?</code></p> <p>Then, your perl code looks something like this:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>my $bug_id = 2; my ($short_desc) = $dbh-&gt;selectrow_array("SELECT short_desc FROM bugs WHERE bug_id = ?", undef, $bug_id); </code></pre></div></div> <h3 id="join-statements-and-selecting-from-multiple-tables">JOIN Statements and SELECTing From Multiple Tables</h3> <p>Frequently when using SQL you want data from more than one table at a time. You do this by “joining” the tables in various ways.</p> <p>For this section, we will be using two tables in our examples, called <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>. Here is what they look like:</p> <table> <thead> <tr> <th>user_id</th> <th>user_name</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>jdoe</td> </tr> <tr> <td>2</td> <td>bsmith</td> </tr> <tr> <td>3</td> <td>mkanat</td> </tr> </tbody> </table> <table> <thead> <tr> <th>user_id</th> <th>email</th> </tr> </thead> <tbody> <tr> <td>1</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="660c02090326031e070b160a034805090b">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1e7c6d73776a765e7b667f736e727b307d7173">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="fb998896928f93bb9ec9d5989496">[email&#160;protected]</a></td> </tr> </tbody> </table> <p>Note that in the below examples we use <code class="language-plaintext highlighter-rouge">SELECT *</code> for simplicity, but <a href="#sql-general"><code class="language-plaintext highlighter-rouge">SELECT *</code>should never be used in Bugzilla code</a>.</p> <h4 id="cross-join">CROSS JOIN</h4> <p>The <em>least</em> common (but also least understood) type of JOIN is the “cross join,” also called the “cross-product join.”</p> <p>A cross join means, “return all possible combinations of rows in these two tables.”</p> <p>In SQL, there are two ways to do a cross join of tables <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>:</p> <p><code class="language-plaintext highlighter-rouge">SELECT * FROM a, b</code> or <code class="language-plaintext highlighter-rouge">SELECT * FROM a CROSS JOIN b</code></p> <p>The two pieces of code above are identical. The first one is called an “implicit” join (because the database just “figures out” that we mean CROSS JOIN, from the comma), and the second is called an “explicit” join (because we specified exactly what we want).</p> <p>If we run the SQL above with our <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code> tables, the result looks like:</p> <table> <thead> <tr> <th>a.user_id</th> <th>a.user_name</th> <th>b.user_id</th> <th>b.email</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>jdoe</td> <td>1</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f49e909b91b4918c9599849891da979b99">[email&#160;protected]</a></td> </tr> <tr> <td>1</td> <td>jdoe</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8deffee0e4f9e5cde8f5ece0fde1e8a3eee2e0">[email&#160;protected]</a></td> </tr> <tr> <td>1</td> <td>jdoe</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5133223c3825391134637f323e3c">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td>1</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="abc1cfc4ceebced3cac6dbc7ce85c8c4c6">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3755445a5e435f77524f565a475b521954585a">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ef8d9c82869b87af8addc18c8082">[email&#160;protected]</a></td> </tr> <tr> <td>3</td> <td>mkanat</td> <td>1</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f9939d969cb99c81989489959cd79a9694">[email&#160;protected]</a></td> </tr> <tr> <td>3</td> <td>mkanat</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="92f0e1fffbe6fad2f7eaf3ffe2fef7bcf1fdff">[email&#160;protected]</a></td> </tr> <tr> <td>3</td> <td>mkanat</td> <td>2</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e082938d899488a085d2ce838f8d">[email&#160;protected]</a></td> </tr> </tbody> </table> <p>In general, you want to avoid cross joins unless you are certain they are exactly what you need.</p> <h4 id="inner-join">INNER JOIN</h4> <p>An INNER JOIN is one where you try to actually join two tables together based on a column that they have in common. This is the most common type of join.</p> <p>For our <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code> tables, we have two ways of writing an INNER JOIN:</p> <p><code class="language-plaintext highlighter-rouge">SELECT * FROM a INNER JOIN b ON a.user_id = b.user_id</code> or <code class="language-plaintext highlighter-rouge">SELECT * FROM a, b WHERE a.user_id = b.user_id</code></p> <p>Just like with the cross join, the first version is an “explicit” join, and the second one is an “implicit” join.</p> <p>The inner join above looks like this:</p> <table> <thead> <tr> <th>user_id</th> <th>user_name</th> <th>email</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>jdoe</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e68c828983a6839e878b968a83c885898b">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b6d4c5dbdfc2def6d3ced7dbc6dad398d5d9db">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8deffee0e4f9e5cde8bfa3eee2e0">[email&#160;protected]</a></td> </tr> </tbody> </table> <p>Note that “mkanat” (with user_id 3) is left out, because he doesn’t have an entry in both tables.</p> <h4 id="left-join">LEFT JOIN</h4> <p>A LEFT JOIN is like an INNER JOIN, but it <em>includes</em> records that have an entry in <code class="language-plaintext highlighter-rouge">a</code> but no corresponding entry in <code class="language-plaintext highlighter-rouge">b</code>. That is, where “mkanat” was left out in our INNER JOIN above, he would be included in our LEFT JOIN.</p> <p>A LEFT JOIN is normally only done one way:</p> <p><code class="language-plaintext highlighter-rouge">SELECT * FROM a LEFT JOIN b ON a.user_id = b.user_id</code></p> <p>The result of that code looks like this:</p> <table> <thead> <tr> <th>user_id</th> <th>user_name</th> <th>email</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>jdoe</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f49e909b91b4918c9599849891da979b99">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c1a3b2aca8b5a981a4b9a0acb1ada4efa2aeac">[email&#160;protected]</a></td> </tr> <tr> <td>2</td> <td>bsmith</td> <td><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="caa8b9a7a3bea28aaff8e4a9a5a7">[email&#160;protected]</a></td> </tr> <tr> <td>3</td> <td>mkanat</td> <td><code class="language-plaintext highlighter-rouge">NULL</code></td> </tr> </tbody> </table> <p>There was no “email” for “mkanat,” so the database returns <code class="language-plaintext highlighter-rouge">NULL</code> there.</p> <p>It’s called a “left” join because we include entries from the table on the <em>left</em> (table <code class="language-plaintext highlighter-rouge">a</code>, note that it’s on the left side of the words “LEFT JOIN”) that don’t have a match in the table on the right side.</p> <p><strong>Warning</strong>: If we add a <code class="language-plaintext highlighter-rouge">WHERE email = ?</code> to the above SQL statement, <em>the row containing mkanat will <strong>never</strong> be returned</em>. This is because <strong>NULL is never <em>equal</em> to anything</strong>.</p> <h3 id="indexes">Indexes</h3> <h4 id="simple-description-of-indexes">Simple Description of Indexes</h4> <p>Indexes speed up SELECT statements.</p> <p>Normally, if you have a table of 10,000 rows, and you want row number 8000, the database has to go through all 10,000 rows to find your row. That’s called a “table scan,” and it’s usually slow.</p> <p>With an index, you ask it for “row 8000” and it gives it to you instantly.</p> <h4 id="more-details">More Details</h4> <p>OK, so in reality you wouldn’t be asking for “row 8000,” you’d be doing something like <code class="language-plaintext highlighter-rouge">WHERE bugs.bug_id = 8000</code> in your SQL.</p> <p>In order for the database to do that quickly, it needs an index on the <code class="language-plaintext highlighter-rouge">bugs</code> table, on the <code class="language-plaintext highlighter-rouge">bug_id</code> column.</p> <h4 id="when-to-add-an-index">When To Add An Index</h4> <p>Generally, you should add an index any time you plan to use a column in a WHERE clause, or in the “ON” part of a JOIN statement.</p> <p>Adding too many indexes to a table will slow down INSERT and UPDATE statements, so don’t add indexes you don’t need.</p> <h4 id="multi-column-indexes">Multi-Column Indexes</h4> <p>Many databases (MySQL in particular) will only use <em>one index per table per query</em>.</p> <p>That is, imagine that we do: <code class="language-plaintext highlighter-rouge">SELECT * FROM bugs WHERE priority = 'P1' OR op_sys = 'Windows'</code>. We have two indexes on the bugs table, one for “priority” and one for “op_sys.” <strong>Only one of them will be used.</strong> The one that’s used is up to the database, it will try to pick the “best” one to use, and then do a table scan for the other one.</p> <p>If we want that to be fast, we can create a <em>single</em> index that contains the values for <em>both</em> <code class="language-plaintext highlighter-rouge">priority</code> and <code class="language-plaintext highlighter-rouge">op_sys</code>. That is called a multi-column index.</p> <p>Multi-column indexes are always in a certain order. For example, either “op_sys, priority” or “priority, op_sys.” For your purposes, those two indexes are <em>totally identical</em>, with one exception: <strong>The first column of a multi-column index can be used like it’s a single-column index.</strong> So, for example, having an index on both “priority, op_sys” and “priority” would be redundant. Don’t do it.</p> <h4 id="write-queries-with-indexes-in-mind">Write Queries With Indexes In Mind</h4> <p>When writing a query, give a brief thought to how it will use indexes. Don’t try to solve performance problems before you know they exist (remember our rules from the top of the Developer’s Guide), but do give a brief thought to it, particularly the fact that only one index will be used per table, per query, by common databases.</p> <h3 id="cross-database-query-compatibility">Cross-Database Query Compatibility</h3> <p>Different databases support different things, and different databases use different syntax for similar features. <code class="language-plaintext highlighter-rouge">Bugzilla-&gt;dbh</code> provides many functions that will generate correct SQL for the database that Bugzilla is using. These functions all have names that start with <code class="language-plaintext highlighter-rouge">sql_</code>.</p> <p>Here are the MySQL constructs that must be replaced with Bugzilla::DB <code class="language-plaintext highlighter-rouge">sql_</code> functions:</p> <ul> <li><code class="language-plaintext highlighter-rouge">BEGIN/START [TRANSACTION] / COMMIT [TRANSACTION]</code></li> <li><code class="language-plaintext highlighter-rouge">REGEXP / NOT REGEXP</code></li> <li><code class="language-plaintext highlighter-rouge">LIMIT</code></li> <li><code class="language-plaintext highlighter-rouge">TO_DAYS</code></li> <li><code class="language-plaintext highlighter-rouge">DATE_FORMAT</code></li> <li><code class="language-plaintext highlighter-rouge">INTERVAL</code></li> <li><code class="language-plaintext highlighter-rouge">CONCAT</code></li> <li><code class="language-plaintext highlighter-rouge">INSTR / POSITION</code></li> <li><code class="language-plaintext highlighter-rouge">GROUP BY</code></li> <li><code class="language-plaintext highlighter-rouge">MATCH</code> or anything having to do with “fulltext” indexes.</li> </ul> <p>Also, if you absolutely need to do a case-insensitive string comparison, use <code class="language-plaintext highlighter-rouge">sql_istrcmp</code> instead of <code class="language-plaintext highlighter-rouge">=</code> or other operators.</p> <p>The following SQL constructs should <em>never</em> be used. Replace them with something else instead:</p> <ul> <li><code class="language-plaintext highlighter-rouge">ENCRYPT()</code> - use Perl’s <code class="language-plaintext highlighter-rouge">bz_crypt</code> function instead.</li> <li>bit arithmetic (<code class="language-plaintext highlighter-rouge">AND</code>, <code class="language-plaintext highlighter-rouge">OR</code> and <code class="language-plaintext highlighter-rouge">NOT</code> on numbers)</li> <li><code class="language-plaintext highlighter-rouge">REPLACE INTO</code> - instead use a check if the row is there, and then either an INSERT or an UPDATE.</li> <li>double quotes (“) for string literals - instead use single quotes or <code class="language-plaintext highlighter-rouge">$dbh-&gt;quote()</code>.</li> <li><code class="language-plaintext highlighter-rouge">IFNULL</code> (use <code class="language-plaintext highlighter-rouge">COALESCE</code>)</li> <li>Columns of MySQL type <code class="language-plaintext highlighter-rouge">ENUM</code> or <code class="language-plaintext highlighter-rouge">TIMESTAMP</code>.</li> <li>Any boolean comparison that is <em>inside the fields of a SELECT statement</em>. For example <code class="language-plaintext highlighter-rouge">SELECT (column IS NOT NULL) FROM b</code> will <em>not</em> work. (Use <code class="language-plaintext highlighter-rouge">CASE WHEN column IS NOT NULL THEN 1 ELSE 0 END</code> instead.)</li> <li><code class="language-plaintext highlighter-rouge">WHERE</code> statements without an operator. For example, <code class="language-plaintext highlighter-rouge">SELECT column FROM b WHERE a</code> will <em>not</em> work. That needs to have: <code class="language-plaintext highlighter-rouge">WHERE a = 1</code>.</li> <li><code class="language-plaintext highlighter-rouge">IF</code> - use <code class="language-plaintext highlighter-rouge">CASE WHEN</code> instead.</li> <li><code class="language-plaintext highlighter-rouge">INSERT</code> statements with multiple groups of parentheses after VALUES. Instead, just call INSERT multiple times.</li> </ul> <p>The following query features are not currently available in all databases that Bugzilla supports, and so should be avoided:</p> <ul> <li>Transactions (<code class="language-plaintext highlighter-rouge">START TRANSACTION</code> or <code class="language-plaintext highlighter-rouge">COMMIT</code>)</li> <li>Subselects (<code class="language-plaintext highlighter-rouge">SELECT * FROM (SELECT * FROM b)</code>)</li> </ul> <h4 id="a-note-about-inner-join">A Note About INNER JOIN</h4> <p>In general, <em>implicit</em> INNER JOINs (<code class="language-plaintext highlighter-rouge">FROM a, b WHERE a.user_id = b.user_id</code>) are faster than <em>explicit</em> INNER JOINs (<code class="language-plaintext highlighter-rouge">FROM a INNER JOIN b ON a.user_id = b.user_id</code>). This is because explicit INNER JOINs bypass the query optimizer on some databases and specify that the database <em>must</em> use that table order.</p> <p>However, you can’t mix explicit and implicit joins. It works on older MySQL versions (before 5.0), but that’s it. So, if you have any LEFT JOIN statements in your SQL, you cannot have any “comma” joins.</p> <p>In general, the performance penalty for explicit INNER JOINs should only happen on older databases (PostgreSQL before 7.4, for example), so don’t worry about it too much. When in doubt, do an explicit join.</p> <h3 id="style-1">Style</h3> <ul> <li> <p>Bugzilla uses a capitalisation convention for SQL. SQL keywords such as <code class="language-plaintext highlighter-rouge">SELECT</code>, <code class="language-plaintext highlighter-rouge">WHERE</code> and <code class="language-plaintext highlighter-rouge">AND</code> should be all upper case. Even function names like <code class="language-plaintext highlighter-rouge">NOW()</code> should be upper-case. Database, table and field names should be all lower case, for example:</p> <p><code class="language-plaintext highlighter-rouge">SELECT fieldname1 FROM tablename WHERE fieldname2 = 2 AND fieldname3 = 'HELLO' AND time = NOW()</code></p> </li> <li> <p><strong>Indentation:</strong> For short SQL statements, you can indent them in whichever way you like. However, for long SQL statements, the recommended formatting is like this:</p> <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="k">DISTINCT</span> <span class="n">groups</span><span class="p">.</span><span class="n">id</span><span class="p">,</span> <span class="n">groups</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">groups</span><span class="p">.</span><span class="n">description</span> <span class="k">FROM</span> <span class="n">groups</span> <span class="k">CROSS</span> <span class="k">JOIN</span> <span class="n">user_group_map</span> <span class="k">CROSS</span> <span class="k">JOIN</span> <span class="n">group_group_map</span> <span class="k">AS</span> <span class="n">ggm</span> <span class="k">WHERE</span> <span class="n">user_group_map</span><span class="p">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="o">?</span> <span class="k">AND</span> <span class="p">((</span><span class="n">user_group_map</span><span class="p">.</span><span class="n">isbless</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">AND</span> <span class="n">groups</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">user_group_map</span><span class="p">.</span><span class="n">group_id</span><span class="p">)</span> <span class="k">OR</span> <span class="p">(</span><span class="n">groups</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">ggm</span><span class="p">.</span><span class="n">grantor_id</span> <span class="k">AND</span> <span class="n">ggm</span><span class="p">.</span><span class="n">grant_type</span> <span class="o">=</span> <span class="o">?</span> <span class="k">AND</span> <span class="n">ggm</span><span class="p">.</span><span class="n">member_id</span> <span class="k">IN</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">))))</span> <span class="k">ORDER</span> <span class="k">BY</span> <span class="n">groups</span><span class="p">.</span><span class="n">id</span> </code></pre></div> </div> <p>Note how all the major keywords are right-aligned. This makes it very easy to scan through the statement. Also, look at how the WHERE clause is aligned–in such a way as to show the logical groupings, but without a lot of extra spacing.</p> <p>Also, because it was such a complex query, we put the table name at the beginning of each column name. This makes it much easier to see where data is coming from, even if it’s not always necessary. (For simple queries that query only one table, don’t do that. It’s obvious where the data is coming from.)</p> </li> </ul> <h2 id="templates">Templates</h2> <h3 id="general-2">General</h3> <ul> <li> <p>Bugzilla uses the Template Toolkit (TT) (module name <code class="language-plaintext highlighter-rouge">Template</code>) to allow administrators to easily customise output and more importantly to allow Bugzilla to be fully localized. To learn more about TT, try <code class="language-plaintext highlighter-rouge">man Template::Manual</code> or go to <a href="http://www.template-toolkit.org/">http://www.template-toolkit.org/</a>.</p> </li> <li> <p>The testing suite will check that templates have no errors. This is done by running the template with the normal filters and no input. While this won’t produce a sensible page, it will find many errors. Occasionally having no input can produce an error where none would have otherwise occurred, but this can generally be worked around by changing the template.</p> </li> <li> <p>The testing suite will make sure you don’t use the term “bug” or similar terms in templates. This term is customisable by the administrator in Bugzilla and you need to ensure your template uses the correct setting.</p> <p>Instead, you should use the terms hash, for example:</p> <p>` [% terms.bug %] `</p> <p>The terms hash is defined in the <code class="language-plaintext highlighter-rouge">global/variables.none.tmpl</code> file.</p> </li> <li> <p>You should use the supplied filters where available. See <code class="language-plaintext highlighter-rouge">man Template::Manual::Filters</code> for information on filters and list of the standard TT filters, and also see the template handling code in <code class="language-plaintext highlighter-rouge">Bugzilla/Template.pm</code> for information on the extra filters defined by Bugzilla.</p> </li> <li> <p>If in doubt as to whether something should be done in a template or code, a good rule of thumb is “Code is for computing data, whereas templates are for presentation of that data.”.</p> </li> <li> <p>If you find that you need to do a presentation task that can’t be easily done in TT, and needs Perl, then introduction of a new filter would likely be the appropriate solution.</p> </li> <li> <p>See the current templates for ideas on how to do stuff.</p> </li> <li> <p>The testing suite will make sure every variable you insert into a template is filtered, except for text templates. This is to avoid bugs caused by forgetting to filter variables, especially cross-site scripting security holes.</p> <p>If you really don’t need to filter a variable, add it to the appropriate <code class="language-plaintext highlighter-rouge">template/\&lt;lang\&gt;/default/filterexceptions.pl</code> file. This is generally not a good idea and should be avoided.</p> <p>See the section on HTML templates for more information about appropriate filtering practices in HTML templates.</p> </li> <li> <p>For repeated code, you should use blocks, including parameterised blocks. This means directives such as <code class="language-plaintext highlighter-rouge">PROCESS</code> and <code class="language-plaintext highlighter-rouge">INCLUDE</code>.</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">PROCESS</code> is preferred for performance reasons over <code class="language-plaintext highlighter-rouge">INCLUDE</code>. Only use <code class="language-plaintext highlighter-rouge">INCLUDE</code> when you need the template fragment to be able to change your variables.</p> </li> <li> <p>To set up a template, make sure this code is near the start of your script:</p> <p><code class="language-plaintext highlighter-rouge">my $template = Bugzilla-&gt;template; my $vars = {};</code></p> <p>and then you need to populate the <code class="language-plaintext highlighter-rouge">$vars</code> variable, for example:</p> <p>` $vars-&gt;{‘id’} = 3; $vars-&gt;{‘array’} = [ ‘g’, ‘o’ ]; `</p> <p>See the appropriate sections below for information on how to output the template.</p> </li> </ul> <h3 id="filenames-and-paths">Filenames and Paths</h3> <ul> <li>CGIs can use templates with the same interface of different “formats”. These templates are called multiple format templates. Full multiple-format templates should be named <code class="language-plaintext highlighter-rouge">stubname-formatname.contentshortname.tmpl</code>, eg <code class="language-plaintext highlighter-rouge">foo-traditional.html.tmpl</code>.</li> <li>Template “fragments” (files which are used by other files and are not intended to be used by themselves) and full single format templates should be named <code class="language-plaintext highlighter-rouge">stubname.contentshortname.tmpl</code>, eg <code class="language-plaintext highlighter-rouge">bar.html.tmpl</code>.</li> <li>All English templates should go in the <code class="language-plaintext highlighter-rouge">template/en/default</code> hierachy. If you are introducing a new template, especially if you are introducing a new directory, please seek advice from the Bugzilla team for the proper exact location of the template.</li> <li>Stubnames should not use spaces or underscores, instead they should use hyphens to separate words.</li> <li>When choosing an appropriate stubname, you often want to use a verb-object name, eg <code class="language-plaintext highlighter-rouge">destroy-widget.html.tmpl</code>. When the object is obvious from the directory, you can omit it, eg <code class="language-plaintext highlighter-rouge">widget/destroy.html.tmpl</code>.</li> <li>The terminology create/list/edit/created/delete is generally used in stubnames. See the existing templates for stubname guidance.</li> </ul> <h3 id="html-templates">HTML Templates</h3> <ul> <li> <p>To output an HTML template to the browser, you can use this code:</p> <p>` use Bugzilla; … my $cgi = Bugzilla-&gt;cgi; … print $cgi-&gt;header(); $template-&gt;process(“file-name.html.tmpl”, $vars)   || ThrowTemplateError($template-&gt;error()); `</p> <p>where <code class="language-plaintext highlighter-rouge">file-name.html.tmpl</code> is replaced by the appropriate filename. You can also pass a parameter to <code class="language-plaintext highlighter-rouge">header()</code> specifying the MIME type if it’s not text/html.</p> </li> <li> <p>Template Comments - You should generally use template comments rather than Javascript or HTML comments, so they don’t bloat the output.</p> </li> <li> <p>Indentation - HTML and XML Templates should have a 2-space indent.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;fred&gt; [% IF foo %] &lt;bar&gt; [% FOREACH x = barney %] &lt;tr&gt; &lt;td&gt; [% x %] &lt;/td&gt; &lt;tr&gt; [% END %] [% END %] &lt;/fred&gt; </code></pre></div> </div> <p>If you use VIm, then you can enter the following into <code class="language-plaintext highlighter-rouge">.vimrc</code> to help avoid tab symbols:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> au BufNewFile,BufRead *.tmpl set et ts=2 sw=2 tw=80 </code></pre></div> </div> </li> <li> <p>Line Length - You shouldn’t extend lines past 80 characters - instead, break lines, in the middle of multi-attributed HTML tags if necessary.</p> </li> <li> <p>Header/Footer - Full HTML Templates should use the <code class="language-plaintext highlighter-rouge">global/header.html.tmpl</code> and <code class="language-plaintext highlighter-rouge">global/footer.html.tmpl</code> templates.</p> </li> <li> <p>You probably want to use the <code class="language-plaintext highlighter-rouge">html</code> filter on most variables, to ensure characters which have special meaning to HTML (like like &lt;, &gt; and &amp;), will be interpreted as normal characters. For example:</p> <p><code class="language-plaintext highlighter-rouge">[% myvar FILTER html %]</code></p> <p>instead of:</p> <p><code class="language-plaintext highlighter-rouge">[% myvar %]</code></p> <p>So if myvar was “&lt;b&gt;hello&lt;/b&gt;”, after filtering that string would appear as is on the page, rather than as bold text.</p> <p>If in doubt as to whether to use the <code class="language-plaintext highlighter-rouge">html</code> filter on a variable, because the variable probably never contains special characters, but should be filtered if it did, you should generally err on the side of caution and filter it anyway.</p> </li> <li> <p>When URIs appear in HTML, the <code class="language-plaintext highlighter-rouge">html</code> filter is not enough to properly escape strings into the format that can appear in a URI.</p> <p>TT provides a filter called <code class="language-plaintext highlighter-rouge">uri</code> that escapes characters such as spaces that are invalid in URIs. For example:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=[% myvar FILTER uri %]"&gt;</code></p> <p>This will escape most non-alphanumeric characters, even those that are valid and have no special meaning in URIs. In particular, it will encode all the HTML special characters (such as &lt; and &gt;), so you do not need to call the <code class="language-plaintext highlighter-rouge">html</code> filter as well afterwards.</p> <p>For example, consider the variable <code class="language-plaintext highlighter-rouge">myuri</code> containing the string “hello &amp;”:</p> <ul> <li> <p>When not filtered:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=[% mytext %]"&gt;</code></p> <p>would become:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=hello &amp;"&gt;</code></p> <p>which is not valid HTML as the ampersand is not allowed raw in an attribute.</p> </li> <li> <p>When <code class="language-plaintext highlighter-rouge">html</code> filtered:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=[% mytext FILTER html %]"&gt;</code></p> <p>would become:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=hello &amp;amp;"&gt;</code></p> <p>which would be valid HTML but an invalid URI:</p> <p><code class="language-plaintext highlighter-rouge">hello.cgi?name=hello &amp;</code></p> <p>as it contains a space.</p> </li> <li> <p>When <code class="language-plaintext highlighter-rouge">uri</code> filtered:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=[% mytext FILTER uri %]"&gt;</code></p> <p>would become:</p> <p><code class="language-plaintext highlighter-rouge">&lt;a href="hello.cgi?name=hello%20%26"&gt;</code></p> <p>which is valid HTML and a valid URI:</p> <p><code class="language-plaintext highlighter-rouge">hello.cgi?name=hello%20%26</code></p> <p>and has the desired result of passing the whole <code class="language-plaintext highlighter-rouge">mytext</code> variable as the “name” parameter.</p> </li> </ul> </li> </ul> <h3 id="web-technologies">Web Technologies</h3> <ul> <li>HTML output should be valid <a href="http://www.w3.org/TR/html5/">HTML 5</a>. You can test the output of a Bugzilla page by using the <a href="http://validator.w3.org/">W3C’s HTML validator</a>.</li> <li>In addition, the HTML should use a style that makes them as XHTML compatible as is possible without failing to validate or causing compatibility problems. This means in particular using lower case tags and double quoting all attributes.</li> <li>Use the <code class="language-plaintext highlighter-rouge">&lt;label&gt;</code> tag on checkboxes and radio buttons where appropriate. This will also help with accessibility.</li> <li>Bugzilla should work reasonably on all browsers.</li> <li>While Bugzilla uses Javascript and cookies to make the user experience easier, no patch to Bugzilla should <em>require</em> either. The only exception is that the administrative interfaces (the edit* CGIs) may require JavaScript, as we expect the administrator to have a JavaScript-capable browser.</li> </ul> <h2 id="security">Security</h2> <p>Please consider the security implications of your code. In particular, thoroughly check user permissions, and thoroughly check data is in the right format.</p> <p>Perl is a reasonably secure language. Buffer overflow and format string attacks are believed to not be possible.</p> <p>Furthermore, Perl’s taint mode can catch a wide range of failure-to-validate errors and transform them from potential security holes into mere errors.</p> <p>But there will always be security problems (see below), as no language can prevent all types of problem.</p> <h3 id="dont-trust-url-parameters-and-cookies">Don’t Trust URL Parameters And Cookies!</h3> <p>URL parameters and cookies are under the control of the user, and hence should not be trusted. Make sure they are in the correct format before using them, and don’t trust anything they say about the user’s authority or identity, other than through the standard Bugzilla authentication mechanisms.</p> <p>To find a user’s identity, examine the properties of the User object returned by <code class="language-plaintext highlighter-rouge">Bugzilla-&gt;user</code> after the appropriate call to <code class="language-plaintext highlighter-rouge">Bugzilla-&gt;login</code>. See <a href="tip/html/api/Bugzilla/User.html">Bugzilla::User</a> for details.</p> <h3 id="confidential-information-leakage">Confidential Information Leakage</h3> <p>Bugzilla has various facilities to restrict products and bugs to users in certain groups. When users can bypass this mechanism, it is a security hole.</p> <p>This can occur when you fail to check appropriate permissions before doing something. Please consider this in your code.</p> <p>Also, arbitrary user-passed SQL can be used in bug lists to return unexpected bugs, so you shouldn’t allow it.</p> <p>Note that even the name of a product can be confidential. Whenever possible, don’t inform the user that something exists, if they can’t access it.</p> <h3 id="unauthorised-access-to-perform-an-action">Unauthorised Access to Perform an Action</h3> <p>This is allowing a user to do something they shouldn’t be able to do, like edit a bug they don’t have access to. Similar to confidential information leakage, this also often occurs when you fail to do an appropriate check.</p> <h3 id="arbitrary-code-execution">Arbitrary Code Execution</h3> <p>This is a very serious hole, as it often has the potential to let the attacker do a wide range of things with a system. It occurs when a user can pass code that then runs, including Perl, HTML, shell-script or SQL.</p> <p>Generally this isn’t intentional, but instead occurs when you fail to properly validate or escape a string you pass as a part of some code.</p> <p>Some instances of this should be picked up by taint mode:</p> <ul> <li>Passing untrusted strings to SQL. Use placeholders instead of passing strings directly into SQL, and remember to <a href="#perl-taint">detaint the data</a> before passing it into the placeholder.</li> <li>Passing untrusted numbers to code. Use <code class="language-plaintext highlighter-rouge">detaint_natural</code> to validate and detaint the number.</li> </ul> <p>Some aren’t, but will probably be picked up by the testing suite:</p> <ul> <li>Using the multiple parameter form of <code class="language-plaintext highlighter-rouge">system</code> or <code class="language-plaintext highlighter-rouge">exec</code>.</li> <li>Passing untrusted strings to HTML. Use the <code class="language-plaintext highlighter-rouge">html</code> filter in TT.</li> </ul> </div> </main> <footer id="footer"> <div class="inner"> <ul id="socials"><li><a href="https://github.com/bugzilla" title="GitHub" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>GitHub</title><path d="M5.883 18.653c-.3-.2-.558-.455-.86-.816a50.32 50.32 0 0 1-.466-.579c-.463-.575-.755-.84-1.057-.949a1 1 0 0 1 .676-1.883c.752.27 1.261.735 1.947 1.588-.094-.117.34.427.433.539.19.227.33.365.44.438.204.137.587.196 1.15.14.023-.382.094-.753.202-1.095C5.38 15.31 3.7 13.396 3.7 9.64c0-1.24.37-2.356 1.058-3.292-.218-.894-.185-1.975.302-3.192a1 1 0 0 1 .63-.582c.081-.024.127-.035.208-.047.803-.123 1.937.17 3.415 1.096A11.731 11.731 0 0 1 12 3.315c.912 0 1.818.104 2.684.308 1.477-.933 2.613-1.226 3.422-1.096.085.013.157.03.218.05a1 1 0 0 1 .616.58c.487 1.216.52 2.297.302 3.19.691.936 1.058 2.045 1.058 3.293 0 3.757-1.674 5.665-4.642 6.392.125.415.19.879.19 1.38a300.492 300.492 0 0 1-.012 2.716 1 1 0 0 1-.019 1.958c-1.139.228-1.983-.532-1.983-1.525l.002-.446.005-.705c.005-.708.007-1.338.007-1.998 0-.697-.183-1.152-.425-1.36-.661-.57-.326-1.655.54-1.752 2.967-.333 4.337-1.482 4.337-4.66 0-.955-.312-1.744-.913-2.404a1 1 0 0 1-.19-1.045c.166-.414.237-.957.096-1.614l-.01.003c-.491.139-1.11.44-1.858.949a1 1 0 0 1-.833.135A9.626 9.626 0 0 0 12 5.315c-.89 0-1.772.119-2.592.35a1 1 0 0 1-.83-.134c-.752-.507-1.374-.807-1.868-.947-.144.653-.073 1.194.092 1.607a1 1 0 0 1-.189 1.045C6.016 7.89 5.7 8.694 5.7 9.64c0 3.172 1.371 4.328 4.322 4.66.865.097 1.201 1.177.544 1.748-.192.168-.429.732-.429 1.364v3.15c0 .986-.835 1.725-1.96 1.528a1 1 0 0 1-.04-1.962v-.99c-.91.061-1.662-.088-2.254-.485z"/></svg> <span class="sr-only">GitHub</span></a></li><li><a href="https://x.com/@bugzilla" title="X" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24" fill="currentColor"><path d="M10.4883 14.651L15.25 21H22.25L14.3917 10.5223L20.9308 3H18.2808L13.1643 8.88578L8.75 3H1.75L9.26086 13.0145L2.31915 21H4.96917L10.4883 14.651ZM16.25 19L5.75 5H7.75L18.25 19H16.25Z"></path></svg> <span class="sr-only">X</span></a></li><li><a href="https://techhub.social/@bugzilla" title="Mastodon" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>Mastodon</title><path fill="none" d="M0 0h24v24H0z"/><path d="M21.258 13.99c-.274 1.41-2.456 2.955-4.962 3.254-1.306.156-2.593.3-3.965.236-2.243-.103-4.014-.535-4.014-.535 0 .218.014.426.04.62.292 2.215 2.196 2.347 4 2.41 1.82.062 3.44-.45 3.44-.45l.076 1.646s-1.274.684-3.542.81c-1.25.068-2.803-.032-4.612-.51-3.923-1.039-4.598-5.22-4.701-9.464-.031-1.26-.012-2.447-.012-3.44 0-4.34 2.843-5.611 2.843-5.611 1.433-.658 3.892-.935 6.45-.956h.062c2.557.02 5.018.298 6.451.956 0 0 2.843 1.272 2.843 5.61 0 0 .036 3.201-.397 5.424zm-2.956-5.087c0-1.074-.273-1.927-.822-2.558-.567-.631-1.308-.955-2.229-.955-1.065 0-1.871.41-2.405 1.228l-.518.87-.519-.87C11.276 5.8 10.47 5.39 9.405 5.39c-.921 0-1.663.324-2.229.955-.549.631-.822 1.484-.822 2.558v5.253h2.081V9.057c0-1.075.452-1.62 1.357-1.62 1 0 1.501.647 1.501 1.927v2.79h2.07v-2.79c0-1.28.5-1.927 1.5-1.927.905 0 1.358.545 1.358 1.62v5.1h2.08V8.902z"/></svg> <span class="sr-only">Mastodon</span></a></li><li><a href="https://bsky.app/profile/bugzilla.org" title="Bluesky" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24" fill="currentColor"><path d="M4.90727 3.43929C5.61594 3.56009 6.44016 3.94139 7.48302 4.68685C9.27977 5.97119 10.7687 7.73907 12 9.56361C13.2313 7.73907 14.7202 5.97119 16.517 4.68685C17.5598 3.94139 18.3841 3.56009 19.0927 3.43929C19.8605 3.3084 20.3825 3.50358 20.7082 3.63931C21.7166 4.05956 22 5.22508 22 6.21461C22 6.41649 21.9144 7.5166 21.8148 8.57508C21.7634 9.12088 21.7057 9.68306 21.6486 10.1515C21.5963 10.5804 21.5337 11.0321 21.4587 11.2849C21.1161 12.4395 20.3965 13.2618 19.508 13.8021C20.4453 14.5092 20.7854 15.6583 20.4359 16.7856C19.8393 18.71 17.6991 21.1833 15.6005 21.4037C13.8281 21.5898 12.6662 20.0794 12 18.6449C11.3338 20.0794 10.1719 21.5898 8.39954 21.4037C6.30095 21.1833 4.1607 18.71 3.56408 16.7856C3.21457 15.6583 3.55466 14.5092 4.49197 13.8021C3.60345 13.2618 2.88394 12.4395 2.54132 11.2849C2.46631 11.0321 2.40367 10.5804 2.35139 10.1515C2.29429 9.68306 2.23658 9.12088 2.18521 8.57508C2.08559 7.5166 2 6.41649 2 6.21461C2 5.22508 2.28343 4.05956 3.29182 3.63931C3.61753 3.50358 4.13949 3.3084 4.90727 3.43929ZM4.04911 6.91709C4.11331 7.73486 4.22889 9.02507 4.33669 9.90947C4.36927 10.1767 4.39214 10.4536 4.45863 10.7156C4.85637 12.056 6.38779 12.7978 8.14506 12.603C8.68686 12.5429 9.17695 12.9278 9.24697 13.4684C9.31698 14.009 8.94113 14.5061 8.40191 14.586C7.64608 14.6981 5.08656 14.9425 5.47438 16.1934C5.8312 17.3443 7.32212 19.2796 8.60842 19.4146C9.53606 19.5121 10.1084 18.0211 10.3741 17.3697C10.6489 16.6958 10.8622 15.9903 11.0417 15.3885C11.1681 14.9648 11.5578 14.6744 12 14.6744C12.4422 14.6744 12.8319 14.9648 12.9583 15.3885C13.1378 15.9903 13.3511 16.6958 13.6259 17.3697C13.8916 18.0211 14.4639 19.5121 15.3916 19.4146C16.6779 19.2796 18.1688 17.3443 18.5256 16.1934C18.9134 14.9425 16.3539 14.6981 15.5981 14.586C15.0589 14.5061 14.683 14.009 14.753 13.4684C14.8231 12.9278 15.3131 12.5429 15.8549 12.603C17.6122 12.7978 19.1436 12.0563 19.5413 10.7159C19.61 10.45 19.63 10.18 19.6633 9.90948C19.7711 9.02507 19.8867 7.73486 19.9509 6.91709C19.9876 6.44922 20.1985 5.27964 19.4288 5.41084C19.1429 5.45959 18.6059 5.65205 17.68 6.31391C15.7374 7.70252 13.9749 9.82666 12.891 11.954C12.7203 12.289 12.376 12.5 12 12.5C11.624 12.5 11.2797 12.289 11.109 11.954C10.0251 9.82666 8.26258 7.70252 6.31998 6.31391C5.39406 5.65205 4.85713 5.45959 4.57117 5.41084C3.7874 5.27724 4.01205 6.44504 4.04911 6.91709Z"></path></svg> <span class="sr-only">Bluesky</span></a></li><li><a href="https://www.facebook.com/BugzillaProject" title="Facebook" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>Facebook</title><path d="M13 19.938A8.001 8.001 0 0 0 12 4a8 8 0 0 0-1 15.938V14H9v-2h2v-1.654c0-1.337.14-1.822.4-2.311A2.726 2.726 0 0 1 12.536 6.9c.382-.205.857-.328 1.687-.381.329-.021.755.005 1.278.08v1.9H15c-.917 0-1.296.043-1.522.164a.727.727 0 0 0-.314.314c-.12.226-.164.45-.164 1.368V12h2.5l-.5 2h-2v5.938zM12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10z"/></svg> <span class="sr-only">Facebook</span></a></li><li><a href="https://www.linkedin.com/company/bugzilla-project" title="LinkedIn" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>LinkedIn</title><path d="M4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h14V5H5zm2.5 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm-1 1h2v7.5h-2V10zm5.5.43c.584-.565 1.266-.93 2-.93 2.071 0 3.5 1.679 3.5 3.75v4.25h-2v-4.25a1.75 1.75 0 0 0-3.5 0v4.25h-2V10h2v.43z"/></svg> <span class="sr-only">LinkedIn</span></a></li><li><a href="https://web.libera.chat/#/#bugzilla" title="IRC on Libera.Chat" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>IRC</title><path d="M7.784 14l.42-4H4V8h4.415l.525-5h2.011l-.525 5h3.989l.525-5h2.011l-.525 5H20v2h-3.784l-.42 4H20v2h-4.415l-.525 5h-2.011l.525-5H9.585l-.525 5H7.049l.525-5H4v-2h3.784zm2.011 0h3.99l.42-4h-3.99l-.42 4z"/></svg> <span class="sr-only">IRC on Libera.Chat</span></a></li><li><a href="https://discord.gg/ZhN6uBA" title="Discord" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"width="24" height="24" fill="currentColor"><title>Discord</title><path d="M19.3034 5.33716C17.9344 4.71103 16.4805 4.2547 14.9629 4C14.7719 4.32899 14.5596 4.77471 14.411 5.12492C12.7969 4.89144 11.1944 4.89144 9.60255 5.12492C9.45397 4.77471 9.2311 4.32899 9.05068 4C7.52251 4.2547 6.06861 4.71103 4.70915 5.33716C1.96053 9.39111 1.21766 13.3495 1.5891 17.2549C3.41443 18.5815 5.17612 19.388 6.90701 19.9187C7.33151 19.3456 7.71356 18.73 8.04255 18.0827C7.41641 17.8492 6.82211 17.5627 6.24904 17.2231C6.39762 17.117 6.5462 17.0003 6.68416 16.8835C10.1438 18.4648 13.8911 18.4648 17.3082 16.8835C17.4568 17.0003 17.5948 17.117 17.7434 17.2231C17.1703 17.5627 16.576 17.8492 15.9499 18.0827C16.2789 18.73 16.6609 19.3456 17.0854 19.9187C18.8152 19.388 20.5875 18.5815 22.4033 17.2549C22.8596 12.7341 21.6806 8.80747 19.3034 5.33716ZM8.5201 14.8459C7.48007 14.8459 6.63107 13.9014 6.63107 12.7447C6.63107 11.5879 7.45884 10.6434 8.5201 10.6434C9.57071 10.6434 10.4303 11.5879 10.4091 12.7447C10.4091 13.9014 9.57071 14.8459 8.5201 14.8459ZM15.4936 14.8459C14.4535 14.8459 13.6034 13.9014 13.6034 12.7447C13.6034 11.5879 14.4323 10.6434 15.4936 10.6434C16.5442 10.6434 17.4038 11.5879 17.3825 12.7447C17.3825 13.9014 16.5548 14.8459 15.4936 14.8459Z"></path></svg> <span class="sr-only">Discord</span></a></li><li><a href="https://matrix.to/#/#bugzilla:mozilla.org" title="Matrix" rel="me" target="_blank"><svg width="24" height="24" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><title>Matrix</title><path d="m3 3c-0.5 0-1 0.5-1 1v16c0 0.5 0.5 1 1 1h3v-2h-2v-14h2v-2h-3zm15 0v2h2v14h-2v2h3c0.5 0 1-0.5 1-1v-16c0-0.5-0.5-1-1-1h-3zm-8 6c-0.75 0-1.5 0.25-2 0.75v-0.5h-2v6.75h2v-3.5c0-0.75 0.75-1.5 1.5-1.5 0.75 0 1.5 0.75 1.5 1.5v2.5h2v-2.5c0-0.75 0.75-1.5 1.5-1.5s1.5 0.75 1.5 1.5v3.5h2v-4c0-1.5-1.25-3-3-3-1 0-2 0.5-2.5 1.25-0.5-0.75-1.5-1.25-2.5-1.25z"/></svg> <span class="sr-only">Matrix</span></a></li><li><a href="https://www.youtube.com/@bugzillaproject" title="YouTube" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>YouTube</title><path d="M19.6069 6.99482C19.5307 6.69695 19.3152 6.47221 19.0684 6.40288C18.6299 6.28062 16.501 6 12.001 6C7.50098 6 5.37252 6.28073 4.93225 6.40323C4.68776 6.47123 4.4723 6.69593 4.3951 6.99482C4.2863 7.41923 4.00098 9.19595 4.00098 12C4.00098 14.804 4.2863 16.5808 4.3954 17.0064C4.47126 17.3031 4.68676 17.5278 4.93251 17.5968C5.37252 17.7193 7.50098 18 12.001 18C16.501 18 18.6299 17.7194 19.0697 17.5968C19.3142 17.5288 19.5297 17.3041 19.6069 17.0052C19.7157 16.5808 20.001 14.8 20.001 12C20.001 9.2 19.7157 7.41923 19.6069 6.99482ZM21.5442 6.49818C22.001 8.28 22.001 12 22.001 12C22.001 12 22.001 15.72 21.5442 17.5018C21.2897 18.4873 20.547 19.2618 19.6056 19.5236C17.8971 20 12.001 20 12.001 20C12.001 20 6.10837 20 4.39637 19.5236C3.45146 19.2582 2.70879 18.4836 2.45774 17.5018C2.00098 15.72 2.00098 12 2.00098 12C2.00098 12 2.00098 8.28 2.45774 6.49818C2.71227 5.51273 3.45495 4.73818 4.39637 4.47636C6.10837 4 12.001 4 12.001 4C12.001 4 17.8971 4 19.6056 4.47636C20.5505 4.74182 21.2932 5.51636 21.5442 6.49818ZM10.001 15.5V8.5L16.001 12L10.001 15.5Z"></path></svg> <span class="sr-only">YouTube</span></a></li><li><a href="https://twitch.tv/bugzillaproject" title="Twitch" rel="me" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>Twitch</title><path d="M21.001 3V14.7391L16.3053 19.4348H12.3923L9.95523 21.7826H6.91402V19.4348H3.00098V6.13043L4.2281 3H21.001ZM19.4358 4.56522H6.13141V16.3043H9.26185V18.6522L11.6097 16.3043H16.3053L19.4358 13.1739V4.56522ZM16.3053 7.69565V12.3913H14.7401V7.69565H16.3053ZM12.3923 7.69565V12.3913H10.8271V7.69565H12.3923Z"></path></svg> <span class="sr-only">Twitch</span></a></li></ul> <hr/> <div id="bottom-footer"> <span>Copyright &copy; 1998-2025 bugzilla.org contributors</span> <span class="copyright"><span class="sr-only">Creative Commons Attribution-ShareAlike 2.0</span><a id="license" rel="license" href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>Creative Commons</title><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zM9 8c1.105 0 2.105.448 2.829 1.173l-1.414 1.414a2 2 0 1 0-.001 2.828l1.414 1.413A4.001 4.001 0 0 1 5 12c0-2.208 1.792-4 4-4zm7 0c1.105 0 2.105.448 2.829 1.173l-1.414 1.414a2 2 0 1 0-.001 2.828l1.414 1.413A4.001 4.001 0 0 1 12 12c0-2.208 1.792-4 4-4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>Attribution</title><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm2 6a1 1 0 0 1 1 1v4h-1.5v4h-3v-4H9v-4a1 1 0 0 1 1-1h4zm-2-5a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><title>ShareAlike</title><path d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zm0 2c-4.415 0-8 3.585-8 8s3.585 8 8 8 8-3.585 8-8-3.585-8-8-8zm0 2c2.761 0 5 2.686 5 6s-2.239 6-5 6c-2.177 0-4.029-1.67-4.715-4l2.117.001C9.92 15.196 10.89 16 12 16c1.657 0 3-1.79 3-4s-1.343-4-3-4c-1.11 0-2.08.805-2.599 2H11l-2.5 3L6 10h1.284C7.971 7.67 9.823 6 12 6z"/></svg></a><span> </div> </div> </footer> <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script></body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10