<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/mysql/</id>
  <link href="https://www.endpointdev.com/blog/tags/mysql/"/>
  <link href="https://www.endpointdev.com/blog/tags/mysql/" rel="self"/>
  <updated>2021-03-24T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Spatial queries with MySQL</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/03/spatial-queries-with-mysql/"/>
      <id>https://www.endpointdev.com/blog/2021/03/spatial-queries-with-mysql/</id>
      <published>2021-03-24T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/03/spatial-queries-with-mysql/spatial-queries-with-mysql.jpg&#34; alt=&#34;Spatial queries with MySQL&#34;&gt;
&lt;a href=&#34;https://flic.kr/p/nQNYxQ&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/119810478@N08/&#34;&gt;Francois Powell&lt;/a&gt;, &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC BY 2.0&lt;/a&gt;, cropped&lt;/p&gt;
&lt;p&gt;MySQL is one of the most widely used relational databases. Most PHP websites rely on MySQL for persisting their information, which makes it one of the &lt;a href=&#34;https://towardsdatascience.com/top-10-databases-to-use-in-2021-d7e6a85402ba&#34;&gt;DB-Engines top four most popular databases&lt;/a&gt; along with Oracle, SQL Server, and PostgreSQL.&lt;/p&gt;
&lt;p&gt;One of its capabilities that is not very well known is that the engine supports working with spatial data, allowing you to save different shapes (points, lines, polygons) and querying information based on intersections, distances, or overlaps. This capability was included in MySQL a long time ago, but it became easier to use starting in version &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.6/en/&#34;&gt;5.6&lt;/a&gt;, when the distance and point intersection functions were added.&lt;/p&gt;
&lt;p&gt;Spatial data can be useful for many needs, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Searching for places based on latitude/​longitude coordinates&lt;/li&gt;
&lt;li&gt;Displaying information and areas as layers on maps&lt;/li&gt;
&lt;li&gt;Architecture or home design applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My first experience with spatial queries was for a weather website I developed that displays local alerts/​warnings on a map using MySQL spatial functions to return active weather alerts for a given location, or to inform if lightning has been observed near the user’s current coordinates. So far, MySQL has given me all the resources I need to do such operations with relatively good performance and without needing to write lots of custom code.&lt;/p&gt;
&lt;h3 id=&#34;adding-spatial-information&#34;&gt;Adding spatial information&lt;/h3&gt;
&lt;p&gt;There are many resources available to import spatial information into our database. From the United States Census Bureau we can find a &lt;a href=&#34;https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html&#34;&gt;set of shapefiles with all US states and counties&lt;/a&gt;. The &lt;a href=&#34;https://www.back4app.com/database&#34;&gt;Back4App social database platform&lt;/a&gt; also has many datasets available to download for free.&lt;/p&gt;
&lt;p&gt;Of course, we can also create a table ourselves that contains any kind of spatial information. In the example below, we will create a table named restaurants which will have a name and location (lat/long) geometry for each row.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;restaurants&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;VARCHAR&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;location&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;GEOMETRY&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NOT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;SPATIAL&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INDEX&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;location&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that we are adding an index of type SPATIAL to the location field. This is mainly for performance reasons. Once the table is created, we can populate it with some data using the &lt;code&gt;ST_GeomFromText()&lt;/code&gt; function that will convert a string representation of any shape into a geometry:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;restaurants&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Restaurant 1&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_GeomFromText(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;POINT(-26.66115 40.95858)&amp;#39;&lt;/span&gt;));&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;restaurants&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Restaurant 2&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_GeomFromText(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;POINT(-26.68685 40.93992)&amp;#39;&lt;/span&gt;));&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;restaurants&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Restaurant 3&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_GeomFromText(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;POINT(-31.11924 42.39557)&amp;#39;&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;querying-and-filtering-with-spatial-data&#34;&gt;Querying and filtering with spatial data&lt;/h3&gt;
&lt;p&gt;Let’s suppose we imported a list of US states from the website above, along with a shape field that holds the geometry associated with each state into a &lt;code&gt;states&lt;/code&gt; table. Then, we will be able to get the geometry by just running a SELECT statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;state_name,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_AsText(shape)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;states&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ORDER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;state_name;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;*&lt;span style=&#34;color:#888&#34;&gt;-----------------------------*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;state_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;shape&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;*&lt;span style=&#34;color:#888&#34;&gt;-----------------------------*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Alabama&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;POLYGON((...))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Alaska&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;POLYGON((...))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Arizona&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;POLYGON((...))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;...&lt;span style=&#34;color:#bbb&#34;&gt;                         &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;*&lt;span style=&#34;color:#888&#34;&gt;-----------------------------*&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;ST_AsText()&lt;/code&gt; function will convert the shape contents into a string representation for us to read and parse, doing the inverse process of the &lt;code&gt;ST_GeomFromText()&lt;/code&gt; function we used above. In our application, we can then parse that string and process it the way we need.&lt;/p&gt;
&lt;h3 id=&#34;intersecting&#34;&gt;Intersecting&lt;/h3&gt;
&lt;p&gt;For example, we can get a list of states that intersects with a given shape. To do that, we will use the &lt;code&gt;ST_Intersects()&lt;/code&gt; function, which will return a boolean value indicating if two shapes intersect or not.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;s.state_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;states&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;s&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_Intersects(s.shape,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_GeomFromText(?))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ORDER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;s.state_name;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can define our geometry to be any shape we need. For example, if we want to know which state contains a given point, we can set our parameter to something like &lt;code&gt;&#39;POINT(40.69 -74.25)&#39;&lt;/code&gt; which should return the state of New York.&lt;/p&gt;
&lt;p&gt;If we have a polygon, we can set the parameter to specify a polygon shape, i.e. &lt;code&gt;&#39;POLYGON((40.69 -74.25, 41.10 -74.25, 41.10 -76.11, 40.69 -76.11, 40.69 -74.25))&#39;&lt;/code&gt; will mean a rectangle that intersects the states of New York and Pennsylvania. In that case, our query will return two rows with both state names.&lt;/p&gt;
&lt;h3 id=&#34;spherical-distance&#34;&gt;Spherical distance&lt;/h3&gt;
&lt;p&gt;Let’s get back to our &lt;code&gt;restaurants&lt;/code&gt; table. If we want to get a list of restaurants that are close to our location, we can use the &lt;code&gt;ST_Distance_Sphere()&lt;/code&gt; function that will return a distance in meters between two points in a sphere (defaulting to Earth’s radius):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;restaurants&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_Distance_Sphere(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;location&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ST_GeomFromText(?))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&amp;lt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ORDER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;name;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This query will give us a list of restaurants, sorted by name, that are within 10 kilometers from the point we pass as a parameter. We should set the parameter as &lt;code&gt;&#39;POINT(lat long)&#39;&lt;/code&gt; where lat and long represent our current geolocation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/03/spatial-queries-with-mysql/restaurants-query-example.jpg&#34; alt=&#34;Example of a query to the restaurants table&#34;&gt;&lt;/p&gt;
&lt;p&gt;We can use the distance function for many other purposes, like finding people that are close to each other or planning a road trip for the user.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;We can avoid writing our own routines to handle spatial data by using MySQL’s built-in spatial types and methods. For most websites, MySQL offers a set of functions that will provide support for most common scenarios. And starting with version 8.0 MySQL includes extended support for geographic and ellipsoid computations.&lt;/p&gt;
&lt;p&gt;We should consider that, if we need a complex solution for a robust spatial-based enterprise application, there are alternatives like &lt;a href=&#34;https://postgis.net/&#34;&gt;PostgreSQL’s PostGIS&lt;/a&gt; that might be better suited to our purpose. MySQL lacks some advanced features like transformations, custom topology handling, or BRIN indexes, that might have an impact on our development process, depending on our needs.&lt;/p&gt;
&lt;p&gt;The image below shows several weather alerts rendered on a &lt;a href=&#34;https://leafletjs.com/&#34;&gt;Leaflet.js&lt;/a&gt; map that were fetched from a field of type &lt;code&gt;GEOMETRY&lt;/code&gt; in MySQL:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/03/spatial-queries-with-mysql/polygons-in-leafletjs-map.jpg&#34; alt=&#34;map of Argentina with areas shaded yellow&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;resources&#34;&gt;Resources&lt;/h3&gt;
&lt;p&gt;There are many other functions we can use to work with our spatial data, from getting the centroid associated to a shape in order to automatically center a map view, to simplifying/​optimizing existing geometry. I recommend reviewing the following links to start trying things out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html&#34;&gt;MySQL spatial function reference (8.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dev.mysql.com/doc/refman/8.0/en/opengis-geometry-model.html&#34;&gt;Supported geometry (based on OpenGIS model) (8.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dev.mysql.com/doc/refman/5.7/en/spatial-geojson-functions.html&#34;&gt;GeoJSON support functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Containerizing Magento with Docker Compose: Elasticsearch, MySQL and Magento</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/"/>
      <id>https://www.endpointdev.com/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/</id>
      <published>2020-08-27T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/banner.jpg&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://business.adobe.com/products/magento/open-source.html&#34;&gt;Magento&lt;/a&gt; is a complex piece of software, and as such, we need all the help we can get when it comes to developing customizations for it. A fully featured local development environment can do just that, but these can often times be very complex as well. It’d be nice to have some way to completely capture all the setup for such an environment and be able to get it all up and running quickly, repeatably&amp;hellip; even with a single command. Well, &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; containers can help with that. And they can be easily provisioned with the &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;Docker Compose&lt;/a&gt; tool.&lt;/p&gt;
&lt;p&gt;In this post, we’re going to go in depth into how to fully containerize a Magento 2.4 installation for development, complete with its other dependencies &lt;a href=&#34;https://www.elastic.co/&#34;&gt;Elasticsearch&lt;/a&gt; and &lt;a href=&#34;https://www.mysql.com/&#34;&gt;MySQL&lt;/a&gt;. By the end of it, we’ll have a single command that sets up all the infrastructure needed to install and run Magento, and develop for it. Let’s get started.&lt;/p&gt;
&lt;h3 id=&#34;magento-24-application-components&#34;&gt;Magento 2.4 application components&lt;/h3&gt;
&lt;p&gt;The first thing that we need to know is what the actual components of a Magento application are. Starting with 2.4, &lt;a href=&#34;https://devdocs.magento.com/guides/v2.4/install-gde/prereq/elasticsearch.html&#34;&gt;Magento requires access to an Elasticsearch&lt;/a&gt; service to power catalog searches. Other than that, we have the usual suspects for typical PHP applications. Here’s what we need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Elasticsearch&lt;/li&gt;
&lt;li&gt;A web server running the Magento application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In terms of infrastructure, this is pretty straightforward. It would cleanly translate into three separate machines talking to each other via the network, but in the Docker world, each of these machines become containers. Since we need multiple containers for our infrastructure, things like Docker Compose can come in handy to orchestrate the creation of all that. So let’s get to it.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-shared-network&#34;&gt;Creating a shared network&lt;/h3&gt;
&lt;p&gt;Since we want to create three separate containers that can talk to each other, we need to ask the Docker engine to create a network for them. This can be done with this self-explanatory command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network create magento-demo-network&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;magento-demo-network&lt;/code&gt; is the name I’ve chosen for my network but you can choose whatever is most appropriate.&lt;/p&gt;
&lt;p&gt;You can run the following command to check your newly created network:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network ls&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output usually looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker network ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NETWORK ID    NAME                  DRIVER  SCOPE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bd562b9cf5a4  bridge                bridge  local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb9ec2365c5  host                  host    local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2dba8d97410e  magento-demo-network  bridge  local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c3473c60ed52  none                  null    local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s our &lt;code&gt;magento-demo-network&lt;/code&gt; network among other networks that Docker creates by default.&lt;/p&gt;
&lt;h3 id=&#34;containerizing-mysql&#34;&gt;Containerizing MySQL&lt;/h3&gt;
&lt;p&gt;Getting a MySQL instance up and running is super easy these days thanks to Docker. There’s already &lt;a href=&#34;https://hub.docker.com/_/mysql&#34;&gt;an official image for MySQL&lt;/a&gt; in &lt;a href=&#34;https://hub.docker.com/&#34;&gt;Docker Hub&lt;/a&gt; so we will use that. We can set it up with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 3306:3306 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -v magento-demo-mysql-data:/var/lib/mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_ROOT_PASSWORD=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_USER=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_PASSWORD=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_DATABASE=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mysql:5.7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And just like that, we have a running MySQL instance. Running &lt;code&gt;docker ps&lt;/code&gt; can get you a list of currently running containers. The one we just created should show up there.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker ps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID  IMAGE      COMMAND                 CREATED         STATUS         PORTS                              NAMES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;b73739ad5d66  mysql:5.7  &amp;#34;docker-entrypoint.s…&amp;#34;  22 seconds ago  Up 21 seconds  0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp  magento-demo-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s go through each one of the options from that command now to understand it better.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Runs the container in detached mode. This means that it’s run in the background as a daemon. Control is returned to the console immediately.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-mysql&lt;/code&gt;: This is the name of our container. Normally, Docker will generate random names for containers. In this case, we want to give it a name to refer to it with other Docker commands.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Tells Docker to run the container as part of the &lt;code&gt;magento-demo-network&lt;/code&gt; network that we created earlier. This is the network that we will use for all of our containers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias mysql&lt;/code&gt;: This is the name of this container within the network. This is how other containers in the network will be able to reference it. We’ll see that come to life a bit later.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 3306:3306&lt;/code&gt;: Sets up our new MySQL container to allow connections over port &lt;code&gt;3306&lt;/code&gt;. This is MySQL’s default port, which Magento will use to connect to it. This basically says “requests coming over the network to port &lt;code&gt;3306&lt;/code&gt; of this container are going to be handled by the service installed in this container that listens to port &lt;code&gt;3306&lt;/code&gt;”. That service happens to be MySQL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v magento-demo-mysql-data:/var/lib/mysql&lt;/code&gt;: Creates a Docker volume. Specifically, we’re setting this one up to store the data files from MySQL. We need to do this so that the data stored in our MySQL container is persisted across shutdowns. &lt;code&gt;magento-demo-mysql-data&lt;/code&gt; is the name of the volume and &lt;code&gt;/var/lib/mysql&lt;/code&gt; is the directory within the MySQL container where that volume is mounted. In other words, any files stored in that directory are going to be stored within the volume instead. The volume is stored by Docker in the host machine, outside the container. &lt;code&gt;/var/lib/mysql&lt;/code&gt; is the default directory where MySQL stores databases.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_ROOT_PASSWORD=password&lt;/code&gt;: Is the password for the root user for MySQL. This is passed into the containerized MySQL via environment variables. Hence the &lt;code&gt;-e&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_USER=kevin&lt;/code&gt;: Creates a new login in MySQL with &lt;code&gt;kevin&lt;/code&gt; as its username.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_PASSWORD=password&lt;/code&gt;: Sets the word &lt;code&gt;password&lt;/code&gt; as the password for that &lt;code&gt;kevin&lt;/code&gt; user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_DATABASE=magento_demo&lt;/code&gt;: Creates a database named &lt;code&gt;magento_demo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql:5.7&lt;/code&gt;: This is the image that we’re using for our container. &lt;code&gt;5.7&lt;/code&gt; specifies the version that we want to run. &lt;a href=&#34;https://hub.docker.com/_/mysql&#34;&gt;The &lt;code&gt;mysql&lt;/code&gt; image in Docker Hub&lt;/a&gt; contains a few more versions. Or “tags”, in Docker words.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;connecting-to-this-container&#34;&gt;Connecting to this container&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker ps&lt;/code&gt; showed us that our container was running. We can also interact with it. Here are a couple of ways of doing it:&lt;/p&gt;
&lt;h4 id=&#34;connecting-from-within-the-container&#34;&gt;Connecting from within the container&lt;/h4&gt;
&lt;p&gt;The easiest way of connecting to the MySQL instance is by running &lt;code&gt;mysql&lt;/code&gt; CLI client from within the container itself. You can do that with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-mysql mysql -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s how that command works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker exec -it&lt;/code&gt; is used to run commands inside a container in interactive mode. Just what we need here in this case because we’re running &lt;code&gt;mysql&lt;/code&gt;, which is an interactive CLI.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;magento-demo-mysql&lt;/code&gt; is the name we gave our container in the &lt;code&gt;docker run&lt;/code&gt; command from before via the &lt;code&gt;--name magento-demo-mysql&lt;/code&gt; option. This is why it’s useful to give names to containers: so we can use them in commands like this.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql -u kevin -p&lt;/code&gt; is the command that’s run within the container. This is just the usual way of connecting to a MySQL server instance using the &lt;code&gt;mysql&lt;/code&gt; CLI client. We use &lt;code&gt;kevin&lt;/code&gt; because that’s what we set &lt;code&gt;MYSQL_USER&lt;/code&gt; to when we created our container before.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After running the previous command, the console will ask you for your password. We set that to &lt;code&gt;password&lt;/code&gt; via &lt;code&gt;MYSQL_PASSWORD&lt;/code&gt; so that’s what we need to type in. This will eventually result in the &lt;code&gt;mysql&lt;/code&gt; prompt showing up. Run &lt;code&gt;show databases&lt;/code&gt; to confirm that the &lt;code&gt;magento_demo&lt;/code&gt; database that we specified via &lt;code&gt;MYSQL_DATABASE&lt;/code&gt; got created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; show databases;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Database           |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| information_schema |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magento_demo       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 rows in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can &lt;code&gt;Ctrl + D&lt;/code&gt; your way out of that when you’re done exploring the containerized MySQL instance.&lt;/p&gt;
&lt;h4 id=&#34;connecting-directly-from-the-host-machine&#34;&gt;Connecting directly from the host machine&lt;/h4&gt;
&lt;p&gt;We can also connect to the MySQL instance running in the container, directly from our host machine. We can use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql -h localhost -P 3306 --protocol=tcp -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that it is required that the &lt;code&gt;mysql&lt;/code&gt; CLI client is installed in the host machine for this to work.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Same as before, &lt;code&gt;mysql&lt;/code&gt; will ask you for the password and, once typed in, it will give you its prompt.&lt;/p&gt;
&lt;h3 id=&#34;containerizing-elasticsearch&#34;&gt;Containerizing Elasticsearch&lt;/h3&gt;
&lt;p&gt;Like MySQL, there’s an official &lt;a href=&#34;https://hub.docker.com/_/elasticsearch&#34;&gt;Elasticsearch Docker image up in Docker Hub&lt;/a&gt;. As a result, getting a working Elasticsearch installation is a piece of cake. It’s done with a command like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 9200:9200 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 9300:9300 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e &amp;#34;discovery.type=single-node&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  elasticsearch:7.8.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can validate that the Elasticsearch is running with &lt;code&gt;curl localhost:9200/_cat/health&lt;/code&gt;. That should return something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl localhost:9200/_cat/health
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1597622135 23:55:35 docker-cluster green 1 1 0 0 0 0 0 0 - 100.0%&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alright! That was easy enough. Again, thanks to Docker, we have an application that’s somewhat complex to set up, up and running in a matter of seconds.&lt;/p&gt;
&lt;p&gt;Like before, let’s dissect that command that we used. Very similar to the MySQL one, only with some Elasticsearch specific settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Same as with the MySQL container, runs it in detached mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-elasticsearch&lt;/code&gt;: Gives the container a friendly name.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Puts the container in the same network as the rest of our infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias elasticsearch&lt;/code&gt;: Is the name by which other containers in the network can refer to this contianer.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 9200:9200&lt;/code&gt;: Opens port &lt;code&gt;9200&lt;/code&gt; so that other containers within the network can talk to this one.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 9300:9300&lt;/code&gt;: Same thing but for a different port.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e &amp;quot;discovery.type=single-node&amp;quot;&lt;/code&gt;: Sets up the &lt;code&gt;discovery.type&lt;/code&gt; environment variable that the image uses to configure Elasticsearch with.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elasticsearch:7.8.1&lt;/code&gt;: Specifies that our container will be running version &lt;code&gt;7.8.1&lt;/code&gt; of Elasticsearch.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;containerizing-magento&#34;&gt;Containerizing Magento&lt;/h3&gt;
&lt;p&gt;Now this is the step where things get a little bit more involved. Nothing crazy however, so let’s get into it.&lt;/p&gt;
&lt;h3 id=&#34;the-dockerfile&#34;&gt;The Dockerfile&lt;/h3&gt;
&lt;p&gt;There’s no image of Magento 2 that would be able to get us up and running as quickly as with MySQL or Elasticsearch, at least not that I could find, so we’re going to have to create our own. We can create our own images with the help of &lt;a href=&#34;https://docs.docker.com/engine/reference/builder/&#34;&gt;Dockerfiles&lt;/a&gt;. A Dockerfile is a file that contains all the specifications needed for a container. The Docker engine uses it to create images which can then be used as basis for running containers.&lt;/p&gt;
&lt;p&gt;Here’s a Dockerfile for Magento 2.4 that I came up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /path/to/project/Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Our image is based on Ubuntu.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FROM ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Here we define a few arguments to the Dockerfile. Specifically, the
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# user, user id and group id for a new account that we will use to work
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# as within our container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG USER=docker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG UID=1000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG GID=1000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install PHP, composer and all extensions needed for Magento.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y software-properties-common curl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN add-apt-repository ppa:ondrej/php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    php-mysql php-xml php-intl php-curl \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    php-bcmath php-gd php-mbstring php-soap php-zip \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    composer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install Xdebug for a better developer experience.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y php-xdebug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_enable=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_autostart=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install the mysql CLI client.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y mysql-client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Set up a non-root user with sudo access.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN groupadd --gid $GID $USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; useradd -s /bin/bash --uid $UID --gid $GID -m $USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; apt-get install -y sudo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; echo &amp;#34;$USER ALL=(root) NOPASSWD:ALL&amp;#34; &amp;gt; /etc/sudoers.d/$USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; chmod 0440 /etc/sudoers.d/$USER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Use the non-root user to log in as into the container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USER ${UID}:${GID}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Set this as the default directory when we connect to the container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WORKDIR /workspaces/magento-demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# This is a quick hack to make sure the container has something to run
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# when it starts, preventing it from closing itself automatically when
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# created. You could also remove this and run the container with `docker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# run -t -d` to get the same effect. More on `docker run` further below.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CMD [&amp;#34;sleep&amp;#34;, &amp;#34;infinity&amp;#34;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Feel free to go through the comments in the file above for more details, but essentially, this Dockerfile describes what a machine ready to run Magento should look like. It’s got PHP and all the necessary extensions, &lt;a href=&#34;https://xdebug.org/&#34;&gt;Xdebug&lt;/a&gt;, and &lt;a href=&#34;https://getcomposer.org/&#34;&gt;Composer&lt;/a&gt;. It also includes the &lt;code&gt;mysql&lt;/code&gt; CLI client.&lt;/p&gt;
&lt;p&gt;Importantly, it allows for creating a user account with sudo access. Later, we’ll use this capability to create a user account, inside the container that mimics the one we’re using in our host machine, effectively using the same user both inside and outside the container. The purpose of this is to make it possible to work on the Magento source code files from inside the container without having to deal with Linux permission issues when we try to do the same from outside the container (that is, directly via the host machine).&lt;/p&gt;
&lt;h3 id=&#34;the-image&#34;&gt;The image&lt;/h3&gt;
&lt;p&gt;Alright, now that we have our image defined in the form of our Dockerfile, let’s create it. To do that, we go into our project directory, create a new file named &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /path/to/project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch Dockerfile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then use a text editor to save the contents from above into it, and finally run this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg USER=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg UID=$(id -u) \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg GID=$(id -g) \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -t magento-demo-web .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s what this all means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker build&lt;/code&gt;: Is the command to build images from Dockerfiles.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg USER=kevin&lt;/code&gt;: Specifies the username for the account with sudo access that we will log into our container as. I’ve chosen &lt;code&gt;kevin&lt;/code&gt; here but you should use the one you’re logged in as on your machine.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg UID=$(id -u)&lt;/code&gt;: Uses the &lt;code&gt;id -u&lt;/code&gt; to pass in the Id of the currently logged in user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg GID=$(id -g)&lt;/code&gt;: Uses the &lt;code&gt;id -g&lt;/code&gt; to pass in the Group Id of the currently logged in user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t magento-demo-web .&lt;/code&gt;: Specifies the name of the resulting image to be &lt;code&gt;magento-demo-web&lt;/code&gt;. The &lt;code&gt;.&lt;/code&gt; is a reference to the current working directory from where we’re running the command, which is where our Dockerfile is located.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run &lt;code&gt;docker image ls&lt;/code&gt; and you should see our new home grown &lt;code&gt;magento-demo-web&lt;/code&gt; image along with the other ones that we’ve downloaded from Docker Hub:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REPOSITORY        TAG     IMAGE ID      CREATED         SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-web  latest  90d311df434f  22 minutes ago  452MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql             5.7     718a6da099d8  12 days ago     448MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ubuntu            latest  1e4467b07108  3 weeks ago     73.9MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;elasticsearch     7.8.1   a529963ec236  3 weeks ago     811MB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-container&#34;&gt;The container&lt;/h3&gt;
&lt;p&gt;Ok, now that we have an image that’s capable of running Magento, let’s put it to work by creating a container based on it. We do that with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-web \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias web \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 5000:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -v ${PWD}:/workspaces/magento-demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  magento-demo-web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Line by line, this is telling the Docker engine to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Run the container in detached mode. You could also add the &lt;code&gt;-t&lt;/code&gt; argument which makes sure the container stays up and running even if there’s no program or service running within it. We don’t need that in this case though, because we defined our Dockerfile with that nifty &lt;code&gt;sleep infinity&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-web&lt;/code&gt;: Set the name of our container to &lt;code&gt;magento-demo-web&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Make our container part of the same network as the MySQL and Elasticsearch ones.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias web&lt;/code&gt;: Set our container’s name within the network.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 5000:5000&lt;/code&gt;: Open port &lt;code&gt;5000&lt;/code&gt; to access our soon-to-be-running Magento app.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v ${PWD}:/workspaces/magento-demo&lt;/code&gt;: Create a new volume that makes our current working directory the same as the &lt;code&gt;/workspaces/magento-demo&lt;/code&gt; directory within the container. This is where we’ll store all the Magento files. Binding these directories makes it possible to access and modify the Magento files both from the container and from the host machine. This just makes things easier and more convenient for development purposes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;magento-demo-web&lt;/code&gt;: Use this image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running &lt;code&gt;docker container ls&lt;/code&gt; will show a list of all running containers, including the one we just created:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker container ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID  IMAGE                COMMAND                 CREATED        STATUS        PORTS                                           NAMES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4af35c42e0bb  magento-demo-web     &amp;#34;/bin/bash&amp;#34;             5 minutes ago  Up 5 minutes  0.0.0.0:5000-&amp;gt;5000/tcp                          magento-demo-web
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;6c5ea65a7bd6  elasticsearch:7.8.1  &amp;#34;/tini -- /usr/local…&amp;#34;  2 hours ago    Up 2 hours    0.0.0.0:9200-&amp;gt;9200/tcp, 0.0.0.0:9300-&amp;gt;9300/tcp  magento-demo-elasticsearch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;b73739ad5d66  mysql:5.7            &amp;#34;docker-entrypoint.s…&amp;#34;  3 hours ago    Up 3 hours    0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp               magento-demo-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;connecting-to-the-container&#34;&gt;Connecting to the container&lt;/h3&gt;
&lt;p&gt;With the container up and running, we can connect to it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You may remember this as the same command we used before to connect to the MySQL container. This time, however, we’re using it to connect to our &lt;code&gt;magento-demo-web&lt;/code&gt; container, referenced by the name we gave it, and running &lt;code&gt;bash&lt;/code&gt; on it in order to open a shell.&lt;/p&gt;
&lt;p&gt;After that, a prompt like this should show up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re now inside our container. Notice how we’re automatically taken to &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. This is just like we specified in our Dockerfile with the &lt;code&gt;WORKDIR&lt;/code&gt; command. Feel free to run &lt;code&gt;php -v&lt;/code&gt; or &lt;code&gt;composer -V&lt;/code&gt; to validate that the setup from our Dockerfile got all the way into our container:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$ php -v
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHP 7.4.9 (cli) (built: Aug  7 2020 14:30:01) ( NTS )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Copyright (c) The PHP Group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zend Engine v3.4.0, Copyright (c) Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Xdebug v2.9.6, Copyright (c) 2002-2020, by Derick Rethans
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$ composer -V
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Composer 1.10.1 2020-03-13 20:34:27&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;talking-to-other-containers-in-the-network&#34;&gt;Talking to other containers in the network&lt;/h3&gt;
&lt;p&gt;We also need to validate that our containers are actually able to talk to each other via the network that we set up. If all went according to plan, still from within our &lt;code&gt;magento-demo-web&lt;/code&gt; container, this command should open a &lt;code&gt;mysql&lt;/code&gt; session:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql -h mysql -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how this time we don’t use &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; to connect to our MySQL instance. This time, we use &lt;code&gt;mysql&lt;/code&gt;. This is the network alias we gave out MySQL container, so this is how our &lt;code&gt;magento-demo-web&lt;/code&gt; sees it. To &lt;code&gt;magento-demo-web&lt;/code&gt;, the MySQL container is just another machine in the same network.&lt;/p&gt;
&lt;p&gt;Same deal for the Elasticsearch container. We can do something like this to talk to it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl elasticsearch:9200/_cat/health&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, from the perspective of &lt;code&gt;magento-demo-web&lt;/code&gt;, this is just another machine in the network which it can reach by using the &lt;code&gt;elasticsearch&lt;/code&gt; network alias that we gave it when creating it.&lt;/p&gt;
&lt;h3 id=&#34;installing-magento-in-our-container&#34;&gt;Installing Magento in our container&lt;/h3&gt;
&lt;p&gt;Now that we have our environment ready for Magento, let’s install it. First order of business is to create the Composer project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition ./install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’re familiar with Composer, then this should look very familiar to you. This command will download all the Magento files as specified by the &lt;code&gt;magento/project-community-edition&lt;/code&gt; project from the &lt;code&gt;https://repo.magento.com/&lt;/code&gt; repository. There are a few gotchas though:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, Magento is not openly available to download just like that. As such, Composer will ask for authentication in order to do so. Follow &lt;a href=&#34;https://devdocs.magento.com/guides/v2.4/install-gde/prereq/connect-auth.html&#34;&gt;this guide&lt;/a&gt; to obtain the authentication keys from the Magento Marketplace. When Composer asks for a username, type in the public key; when it asks for password, type in the private key.&lt;/li&gt;
&lt;li&gt;Second, you’ll notice that I specified &lt;code&gt;./install&lt;/code&gt; at the end of that command. This is where all the files will be downloaded. I’ve chosen this (an &lt;code&gt;install&lt;/code&gt; directory inside our current one) because &lt;code&gt;composer create-project&lt;/code&gt; will refuse to download the files in a directory that’s not empty. Ours isn’t, because we’ve got our Dockerfile in it. But that’s nothing to worry about, once Composer finishes downloading everything, we’ll just copy the files over to their rightful location at &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. You can do so with some Linux sorcery like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(shopt -s dotglob; mv -v ./install/* .)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This Composer operation will take a good while, but when it’s done, make sure to move all the contents of &lt;code&gt;./install&lt;/code&gt; into &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. We now need to actually install Magento:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:install \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --base-url=http://localhost:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-firstname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-lastname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-email=admin@admin.com \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-user=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-password=admin123 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --language=en_US \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --currency=USD \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --timezone=America/New_York \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --use-rewrites=1 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even if you have never installed Magento before, the command above should be pretty straightforward. An interesting thing to note is how we’ve set up our database and Elasticsearch settings here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;--db-host&lt;/code&gt; is the hostname of the machine where the MySQL server is running. We use our container’s network alias here. &lt;code&gt;--db-name&lt;/code&gt; is the name of the database we created when initializing our container via the &lt;code&gt;MYSQL_DATABASE&lt;/code&gt; environment variable. &lt;code&gt;--db-user&lt;/code&gt; and &lt;code&gt;--db-password&lt;/code&gt; are the credentials for the login that we created in the same manner. &lt;code&gt;--elasticsearch-host&lt;/code&gt; is the network alias of our Elasticsearch container, and finally &lt;code&gt;--elasticsearch-port&lt;/code&gt; is the port that we configured it to listen to.&lt;/p&gt;
&lt;p&gt;As you can see, these are the same settings that we used to configure our MySQL and Elasticsearch containers. So make sure to do the same if you’ve been following along and decided to go with different values.&lt;/p&gt;
&lt;p&gt;Once that command is done, we’re ready. We have a working Magento. Try it out by running this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And navigating to &lt;code&gt;localhost:5000&lt;/code&gt; in your browser. You should see your empty Magento homepage.&lt;/p&gt;
&lt;h3 id=&#34;optional-installing-the-sample-data&#34;&gt;Optional: Installing the sample data&lt;/h3&gt;
&lt;p&gt;If you’re planning some custom extension, or to just play with Magento to get to know it better, you may want to add some sample data. Luckily, the Magento devs have graciously provided such a thing in the form of a Composer package. If you want, you can install it with this recipe:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento sampledata:deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento indexer:reindex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento cache:flush&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;bin/magento sampledata:deploy&lt;/code&gt; will also ask you for your Magento Makerplace keys so have them ready.&lt;/p&gt;
&lt;p&gt;So turn off the built-in PHP server, run these, wait a good while, and fire up the built in server once more. Your Magento app should now have a catalog and all sorts of other data loaded in.&lt;/p&gt;
&lt;h3 id=&#34;composing-it-all-together&#34;&gt;Composing it all together&lt;/h3&gt;
&lt;p&gt;Now that was a lot. It was much easier than having to set everything up from scratch without Docker, but still, I promised a minimal setup overhead. A single command. With Docker Compose we can do just that.&lt;/p&gt;
&lt;p&gt;For containers, the usual workflow is a three step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the Dockerfile (sometimes omitted if we have a readily available image like it was the case with MySQL and Elasticsearch).&lt;/li&gt;
&lt;li&gt;Create or download an image.&lt;/li&gt;
&lt;li&gt;Run the container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Docker Compose can help us by capturing all the settings needed to create containers in a single YAML file; which then can be taken by a CLI tool (i.e. &lt;code&gt;docker-compose&lt;/code&gt;) and it can set up the complete infrastructure. This single file is named &lt;code&gt;docker-compose.yml&lt;/code&gt; and this is what it may look like for our current setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;version&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Listing our three containers. Or &amp;#34;services&amp;#34;, as known by Docker Compose.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;services&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our MySQL container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;mysql&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;mysql&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mysql:5.7&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-mysql&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;3306:3306&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-mysql-data:/var/lib/mysql&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;password&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_USER&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;kevin&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_PASSWORD&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;password&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_DATABASE&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento_demo&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our Elasticsearch container&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;elasticsearch&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;elasticsearch&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;elasticsearch:7.8.1&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-elasticsearch&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;9200:9200&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;9300:9300&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;discovery.type&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;single-node&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our custom Magento 2 container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;web&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;web&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The build section tells Docker Compose how to build the image.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# This essentially runs a &amp;#34;docker build&amp;#34; command.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;build&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;context&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;dockerfile&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Dockerfile&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;args&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;USER&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;kevin&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;UID&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;GID&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-web&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5000:5000&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- .:/workspaces/magento-demo&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The volume that is used by the MySQL container&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;magento-demo-mysql-data&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The network where all the containers will live&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;magento-demo-network:&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, most of &lt;code&gt;docker-compose.yml&lt;/code&gt; is more or less rewriting the &lt;code&gt;docker run&lt;/code&gt; commands in a YAML format. With the exception of the &lt;code&gt;web&lt;/code&gt; container/​service which includes a &lt;code&gt;build&lt;/code&gt; section that reflects the &lt;code&gt;docker build&lt;/code&gt; command that was used to take the Dockerfile and turn it into an image.&lt;/p&gt;
&lt;p&gt;If you want to try it out, make sure to remove all the infrastructure we’ve created, to avoid any conflicts. You can do so from your host machine with these commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker container rm -f magento-demo-web magento-demo-elasticsearch magento-demo-mysql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker image rm magento-demo-web
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network rm magento-demo-network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume rm magento-demo-mysql-data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure you’re in the directory where the Dockerfile lives in the host machine. Then create a new &lt;code&gt;docker-compose.yml&lt;/code&gt; file and put all the content above into it. Finally, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will take a little while, but by the end of it, you’ll have a complete infrastructure with the three containers that we’ve created step by step throughout this article. With the &lt;code&gt;docker-compose.yml&lt;/code&gt; file, &lt;code&gt;docker-compose up&lt;/code&gt; essentially takes care of running all of our &lt;code&gt;docker build&lt;/code&gt; and &lt;code&gt;docker run&lt;/code&gt; commands.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; option means that the the command will run in the background and give you back control of your console. You can also run it without it if you want the console to show the logs from the containers.&lt;/p&gt;
&lt;p&gt;You can still see the logs even in detached mode with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose logs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also inspect the running containers. For that, you can use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output will look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker-compose ps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           Name                         Command               State                       Ports                     
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------------------------------------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-elasticsearch   /tini -- /usr/local/bin/do ...   Up      0.0.0.0:9200-&amp;gt;9200/tcp, 0.0.0.0:9300-&amp;gt;9300/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-mysql           docker-entrypoint.sh mysqld      Up      0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp             
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-web             sleep infinity                   Up      0.0.0.0:5000-&amp;gt;5000/tcp                        &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how &lt;code&gt;docker-compose ps&lt;/code&gt; gives us our container names just as we specified them in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose&lt;/code&gt; has many other utilities, check them out with &lt;code&gt;docker-compose --help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, same as before, we still need to open a terminal into our Magento container to run some installation commands on it. To do so, we can run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose exec web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how with &lt;code&gt;docker-compose&lt;/code&gt; we refer to the container via its service name. That is, the name we gave the container under the &lt;code&gt;services&lt;/code&gt; section of &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, we can still use the same command that we used before, when we created our container directly with &lt;code&gt;docker&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, once inside our container we need to install Magento again. Remember that we wiped out all the infrastructure we created manually, so these are fresh new containers; akin to new machines.&lt;/p&gt;
&lt;p&gt;If you were running this from scratch you would just go ahead and do&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition ./install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(shopt -s dotglob; mv -v ./install/* .)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, however, we already have all the Magento files in our directory, So we can save time and skip this step. We can reuse these files and just run &lt;code&gt;bin/magento setup:install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But since this is a new Magento installation, we do need to remove the config file before &lt;code&gt;setup:install&lt;/code&gt;’ing. So go ahead and…&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm app/etc/env.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;…then:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:install \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --base-url=http://localhost:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-firstname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-lastname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-email=admin@admin.com \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-user=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-password=admin123 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --language=en_US \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --currency=USD \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --timezone=America/New_York \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --use-rewrites=1 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a while, Magento will be fully installed in our new infrastructure created by Docker Compose and ready to be fired up via the PHP built in server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;bonus-interactive-debugging-with-visual-studio-code&#34;&gt;Bonus: Interactive debugging with Visual Studio Code&lt;/h3&gt;
&lt;p&gt;So this is a fully functioning Magento installation with files that we can edit to our heart’s content. In terms of a “fully featured” development environment, however, we need to spruce it up a bit.&lt;/p&gt;
&lt;p&gt;So install VS Code from &lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;https://code.visualstudio.com/&lt;/a&gt; and install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack&#34;&gt;Remote Development plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open a new VS Code window and open the command palette with &lt;code&gt;Ctrl + Shift + P&lt;/code&gt;. In there, type in &lt;code&gt;Remote-Containers: Attach to Running Container...&lt;/code&gt; and press &lt;code&gt;Enter&lt;/code&gt;. In the menu that shows up, select our &lt;code&gt;magento-demo-web&lt;/code&gt; container.&lt;/p&gt;
&lt;p&gt;That will result in a new VS Code instance that is connected to the container. Open an integrated terminal in VS Code and you’ll see:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/vscode.png&#34; alt=&#34;VS Code with Remote Development&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&#34;&gt;PHP Debug extension&lt;/a&gt; so that we can take advantage of that Xdebug that we installed in our container via our Dockerfile.&lt;/p&gt;
&lt;p&gt;Create a new launch configuration for interactive debugging with PHP by clicking on the “Run” button in the action bar to the left (&lt;code&gt;Ctrl + Shift + D&lt;/code&gt; also works). Click the “create a launch.json file” link in the pane that appears. Then, in the resulting menu at the top of the window, select the “PHP” option. Here’s a screen capture for guidance:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/opening_debug.jpg&#34; alt=&#34;Setting up debugging in VS Code&#34;&gt;&lt;/p&gt;
&lt;p&gt;That will result in a new &lt;code&gt;.vscode/launch.json&lt;/code&gt; file created that contains the launch configuration for the PHP debugger.&lt;/p&gt;
&lt;p&gt;Now let’s put a breakpoint anywhere, like in line 13 of the &lt;code&gt;pub/index.php&lt;/code&gt; file; press the “Start debugging” button in the “Run” pane, near the top left of the screen (making sure that the “Listen to XDebug” option is selected), and start up the PHP built in server from VS Code’s integrated terminal with &lt;code&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/code&gt;. Now navigate to &lt;code&gt;localhost:5000&lt;/code&gt; in your browser and enjoy VS Code’s interactive debugging experience:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/debugging.png&#34; alt=&#34;Debugging Magento in VS codeCode&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Whew! That was quite a bit. In this blog post, we’ve done a deep dive into how to set up all the pieces of a Magento application using Docker containers: MySQL, Elasticsearch, and Magento itself. Then, we captured all that knowledge into a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file which can be run with a single &lt;code&gt;docker-compose up&lt;/code&gt; command to provision all the infrastructure in our local machine. As a cherry on top, we set up interactive debugging of our brand new Magento application with VS Code. Thanks to the safety net provided by these tools, I feel like I’m ready to really dig into Magento and start developing customizations, or debugging existing websites. If you’ve been following along this far, dear reader, I hope you do too.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to set up a local development environment for WordPress from scratch</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/08/set-up-local-development-environment-for-wordpress/"/>
      <id>https://www.endpointdev.com/blog/2019/08/set-up-local-development-environment-for-wordpress/</id>
      <published>2019-08-07T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/banner.png&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;I recently got pulled into a project for a client who wanted to have a new WordPress website developed for them. I started by setting up a development environment with the niceties that I’m used to from my other application development work. That is, a development server, interactive debugging, linting, and a good editor.&lt;/p&gt;
&lt;p&gt;Another thing that I wanted was not to have to deal with LAMP or WAMP or XAMPP or any of that. I wanted a clean, from scratch installation where I knew and controlled everything that was there. I’ve got nothing against those packages, but I think that, by setting up everything manually, I’d be able to better learn the technology as I would know exactly how everything is set up under the hood. The shortcuts could come later.&lt;/p&gt;
&lt;p&gt;Luckily for me, there aren’t many pieces when it comes to setting up a basic, running development environment for WordPress. You only need three things: 1. MySQL, 2. PHP, and 3. WordPress itself. I also wanted a few other goodies and we’ll get there.&lt;/p&gt;
&lt;p&gt;Let’s go through the steps that I took to set all of this up:&lt;/p&gt;
&lt;h3 id=&#34;1-set-up-php&#34;&gt;1. Set up PHP&lt;/h3&gt;
&lt;p&gt;In Ubuntu, installing PHP is easy enough. Just run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After that’s done, run &lt;code&gt;php -v&lt;/code&gt; to validate that it was successfully installed. It should result in something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHP 7.2.19-0ubuntu0.18.04.1 (cli) (built: Jun  4 2019 14:48:12) ( NTS )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Copyright (c) 1997-2018 The PHP Group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Zend OPcache v7.2.19-0ubuntu0.18.04.1, Copyright (c) 1999-2018, by Zend Technologies&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s one particular PHP extension that we’re going to need. Let’s install it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;php-mysql&lt;/code&gt; extension is necessary for our PHP installation to interact with MySQL. That’s all that’s needed to run WordPress as far as PHP is concerned.&lt;/p&gt;
&lt;h3 id=&#34;2-set-up-mysql&#34;&gt;2. Set up MySQL&lt;/h3&gt;
&lt;p&gt;WordPress uses MySQL for all of its data storage concerns. So, let’s install it. Again, in Ubuntu, installing and setting up MySQL is super easy. First, we need to run this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install mysql-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will install both the MySQL database engine and a command-line client for us to connect to it and do some initial configuration. We now need to log into our freshly installed MySQL instance. But first, make sure that it’s running with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo service mysql start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that our instance is running, log into it as &lt;code&gt;root&lt;/code&gt; with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo mysql -u root&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will open up the MySQL command-line client where we can do some initial configuration to support WordPress.&lt;/p&gt;
&lt;p&gt;We now need to create a new MySQL user that will be used by WordPress to log into the database. You can do so with a command like this from within the MySQL CLI client:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt;@&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;IDENTIFIED&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Obviously, choose a username and password that work for you. I like to keep things simple and obvious so that’s what I use. Also obviously, use a strong, unique password in a production environment.&lt;/p&gt;
&lt;p&gt;Now, create a new database that will be used by WordPress with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DATABASE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;wordpress_dev;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, feel free to choose a name that suits your needs.&lt;/p&gt;
&lt;p&gt;Now, we need to allow the user that we created a few steps ago to access and control that new database. Since this is only a dev environment, let’s just give our WordPress user access to everything. This can be done with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;GRANT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ALL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt;@&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With that, we’re done with MySQL, we can close the CLI client with the exit command.&lt;/p&gt;
&lt;h3 id=&#34;3-set-up-wordpress&#34;&gt;3. Set up WordPress&lt;/h3&gt;
&lt;p&gt;Now that we have our prerequisites ready, we can proceed to setting up the actual WordPress site. It turns out, this is pretty easy as well. Get a new directory ready and let’s get started.&lt;/p&gt;
&lt;p&gt;First, we need to download the package of WordPress files from the official site. In Ubuntu, this can be done with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://wordpress.org/latest.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will result in a new &lt;code&gt;latest.tar.gz&lt;/code&gt; file being created in your directory. Now, extract it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tar xvzf latest.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, enter the new &lt;code&gt;wordpress&lt;/code&gt; directory that gets created as a result of the last operation. Explore the &lt;code&gt;wordpress&lt;/code&gt; directory and you should be able to see a bunch of &lt;code&gt;wp-*&lt;/code&gt; files and directories. These are all the files that WordPress needs to run.&lt;/p&gt;
&lt;p&gt;Before running WordPress though, we need to configure it so that it uses the MySQL database that we just created. We do this by specifying that configuration in a &lt;code&gt;wp-config.php&lt;/code&gt; file. This file does not exist yet, but we have a &lt;code&gt;wp-config-sample.php&lt;/code&gt; file that we can use as a template. Create the new &lt;code&gt;wp-config.php&lt;/code&gt; file based on &lt;code&gt;wp-config-sample.php&lt;/code&gt; with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp wp-config-sample.php wp-config.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, in the new &lt;code&gt;wp-config.php&lt;/code&gt; file, put in your MySQL database information. Starting around line 23, it should look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** The name of the database for WordPress */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_NAME&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_dev&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL database username */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_USER&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL database password */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_PASSWORD&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL hostname */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_HOST&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** Database Charset to use in creating database tables. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_CHARSET&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** The Database Collate type. Don&amp;#39;t change this if in doubt. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_COLLATE&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;4-run-wordpress&#34;&gt;4. Run WordPress&lt;/h3&gt;
&lt;p&gt;Now we’re finally ready to actually run WordPress. WordPress, as a web application, needs a web server like Apache to run. We don’t have Apache though; what we have is PHP’s built-in development web server. From within our &lt;code&gt;wordpress&lt;/code&gt; directory, we can fire up the built-in web server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S localhost:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now it’s just a matter of navigating to the &lt;code&gt;localhost:3000/wp-admin/install.php&lt;/code&gt; page in your browser of choice. This page should show up on screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/wordpress-language-select.png&#34; alt=&#34;WordPress Language Select&#34;&gt;&lt;/p&gt;
&lt;p&gt;Just follow the steps within the wizard at &lt;code&gt;install.php&lt;/code&gt; and your new development WordPress site will be ready to go in no time.&lt;/p&gt;
&lt;h3 id=&#34;bonus-1-set-up-a-linter-php-code-sniffer&#34;&gt;Bonus 1: Set up a linter: PHP Code Sniffer&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Prerequisites: Composer and the &lt;code&gt;php-xml&lt;/code&gt; extension&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You can install Composer by following &lt;a href=&#34;https://getcomposer.org/download/&#34;&gt;these instructions&lt;/a&gt;. In Ubuntu, installing the &lt;code&gt;php-xml&lt;/code&gt; extension can be done with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php-xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, to set up the PHP Code Sniffer linter, just follow these steps, from your &lt;code&gt;wordpress&lt;/code&gt; directory:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install PHP Code Sniffer with &lt;code&gt;composer require --dev squizlabs/php_codesniffer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Download the WordPress PHP Code Sniffer standards with &lt;code&gt;composer require --dev wp-coding-standards/wpcs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install it into &lt;code&gt;phpcs&lt;/code&gt; with: &lt;code&gt;vendor/bin/phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sniff something with &lt;code&gt;vendor/bin/phpcs --standard=WordPress index.php&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If all goes well, you should be able to see reports like this (which is exaggerated for demonstration purposes; your default index.php file will not show as many warnings):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FILE: /home/kevin/projects/random/wordpress/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FOUND 5 ERRORS AND 1 WARNING AFFECTING 2 LINES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 14 | ERROR   | [x] Expected 1 spaces after opening bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 14 | ERROR   | [x] Expected 1 spaces before closing bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | WARNING | [x] &amp;#34;require&amp;#34; is a statement not a function; no parentheses are required
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Expected 1 spaces after opening bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Expected 1 spaces before closing bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Concat operator must be surrounded by a single space
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPCBF CAN FIX THE 6 MARKED SNIFF VIOLATIONS AUTOMATICALLY
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: 103ms; Memory: 10MB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;bonus-2-setup-interactive-debugging-with-vs-code&#34;&gt;Bonus 2: Setup interactive debugging with VS Code&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Prerequisites: VS Code, XDebug and the PHP Debug VS Code extension&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For writing PHP, my editor of choice is VS Code. You can get the editor from the &lt;a href=&#34;https://code.visualstudio.com/download&#34;&gt;official download site&lt;/a&gt;. VS Code has a huge extensions ecosystem where you can find almost anything. Naturally, there’s a debugger for PHP. It’s aptly called PHP Debug. See the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&#34;&gt;instructions on how to set it up&lt;/a&gt;. The PHP Debug extension works on top of XDebug, a debugger for PHP. Luckily for us, PHP Debug’s instructions page includes all the details on how to install and set up both itself and XDebug.&lt;/p&gt;
&lt;p&gt;If you followed the instructions, you should now have a new &lt;code&gt;.vscode/launch.json&lt;/code&gt; file within your &lt;code&gt;wordpress&lt;/code&gt; directory with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Listen for XDebug&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is what’s called a &lt;code&gt;launch configuration&lt;/code&gt; in VS Code speak. This tells VS Code’s debugger all the info it needs to attach to a running PHP process. To see it in action, fire up the built-in web development server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S localhost:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, click the &lt;code&gt;Start Debugging&lt;/code&gt; button. That’s the green triangle icon near the top of the screen, when you select the Debug sidebar in VS Code. It should have the &lt;code&gt;Listen for XDebug&lt;/code&gt; option selected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-start-debugger.png&#34; alt=&#34;VS Code Start Debugger&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now it’s just a matter of putting a breakpoint anywhere within the source code and request the page from your browser. You can set a breakpoint by clicking right next to the line number indicator, within any file. Here, I’ve put a breakpoint on line 14 in &lt;code&gt;index.php&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-breakpoint.png&#34; alt=&#34;VS Code Breakpoint&#34;&gt;&lt;/p&gt;
&lt;p&gt;When the code execution hits the breakpoint, it should stop right there and allow you to inspect variables and the like, just like any other debugger.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-debugging.png&#34; alt=&#34;VS Code Breakpoint&#34;&gt;&lt;/p&gt;
&lt;p&gt;And that’s all for now! Hopefully this little write-up can help you jumpstart your next WordPress development project.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Running Magento 2 in Windows with XAMPP</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/"/>
      <id>https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/</id>
      <published>2019-03-22T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/e-commerce-safe.jpg&#34; alt=&#34;Ecommerce&#34; /&gt;&lt;br&gt;&lt;a href=&#34;https://burst.shopify.com/photos/computer-security-lock-and-payment?q=e-commerce&#34;&gt;Photo by Nicole De Khors&lt;/a&gt; · &lt;a href=&#34;https://burst.shopify.com/licenses/shopify-some-rights-reserved&#34;&gt;Burst, Some Rights Reserved&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Magento is an open source ecommerce platform, written in PHP and relying on MySQL/​MariaDB for persistence. According to &lt;a href=&#34;https://trends.builtwith.com/shop&#34;&gt;BuiltWith&lt;/a&gt;, Magento is the third most used platform in ecommerce websites. It began its life in 2008 with its first general release, and a major update (Magento 2) was released in 2015.&lt;/p&gt;
&lt;p&gt;And now, more than three years after, Magento 1 is slowly dying: There won’t be any more quality fixes or security updates from June 2020, and there won’t be extended support for fixes or new payment methods. So the obvious choice will be Magento 2 from now on.&lt;/p&gt;
&lt;p&gt;But is it fully tested yet? Is it stable enough? If we already have a website running with Magento 1, what should we do? Migrating to Magento 2 is not just hitting an “Update” button: Themes are incompatible, most extensions won’t work, and of course, there’s a big set of changes to get familiar with.&lt;/p&gt;
&lt;p&gt;So a good approach might be to get a clean Magento 2 version deployed locally, to look what we need to do to get our website updated and running, test the backend, find where the configuration sections are located, and so on. And many business users, and even some developers like myself, have Microsoft Windows installed on our computers.&lt;/p&gt;
&lt;h3 id=&#34;environment-setup&#34;&gt;Environment setup&lt;/h3&gt;
&lt;p&gt;The environment I used for this testing installation was Windows 10 Professional. As a first step, we’ll need to make sure that &lt;code&gt;localhost&lt;/code&gt; is published in our local hosts file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to the folder &lt;code&gt;%SystemRoot%\system32\drivers\etc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup the existing hosts file&lt;/li&gt;
&lt;li&gt;Open a text editor with administrator rights&lt;/li&gt;
&lt;li&gt;Open the hosts file&lt;/li&gt;
&lt;li&gt;Make sure the first line after the commented (#) lines is &lt;code&gt;127.0.0.1 localhost&lt;/code&gt; and the second is &lt;code&gt;::1 localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open a cmd window with administrator rights and run the command &lt;code&gt;ipconfig /flushdns&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we’re ready to install the environment needed to run Magento. I recommend using &lt;a href=&#34;https://www.apachefriends.org/&#34;&gt;XAMPP&lt;/a&gt;, a free Apache distribution for Windows that includes MariaDB, PHP, and Perl in a single package. Magento 2 currently runs with PHP 7.2 and it will not work with newer versions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install XAMPP 7.2 with default settings: &lt;a href=&#34;https://www.apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&#34;&gt;apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download Magento 2. You will need to register on the website first: &lt;a href=&#34;https://magento.com/tech-resources/download&#34;&gt;magento.com/tech-resources/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a new “magento” subfolder (or whatever name you prefer) inside the htdocs folder in the XAMPP installation (usually &lt;code&gt;C:\xampp&lt;/code&gt;) and uncompress the Magento 2 archive there.&lt;/li&gt;
&lt;li&gt;Start the XAMPP Control Panel from the Windows start menu. In the “Apache” section, click the “Config” button and, on the menu that appears, select “PHP (php.ini)”. Remove the semicolon before the &lt;code&gt;extension=intl&lt;/code&gt;, &lt;code&gt;extension=soap&lt;/code&gt;, and &lt;code&gt;extension=xsl&lt;/code&gt; texts to enable the intl, soap and xsl extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/xampp-control-panel.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Screenshot of the XAMPP Control Panel with the PHP config menu displayed.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;mysql-and-magento-setup&#34;&gt;MySQL and Magento setup&lt;/h3&gt;
&lt;p&gt;We have all the files in place and the environment ready to start configuring the database and install Magento 2.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Start the Apache and MySQL services from the XAMPP Control Panel. Wait for the green status texts to appear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a “magento” (or whatever name you prefer) database in MySQL from phpMyAdmin, installed already on XAMPP: &lt;a href=&#34;http://localhost/phpmyadmin&#34;&gt;localhost/phpmyadmin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the Magento 2 setup from &lt;a href=&#34;http://localhost/magento&#34;&gt;localhost/magento&lt;/a&gt; (replace the “magento” part of the URL with whatever name you have chosen to host Magento). If the setup program requires to do any additional configuration change, do it as instructed. Do a screenshot or save the final page contents for later use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-installation-success.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Example of the page that will be displayed when the Magento 2 installation finishes.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;fixing-known-issues&#34;&gt;Fixing known issues&lt;/h3&gt;
&lt;p&gt;When we finish the installation process, we will have a Magento 2 instance running on our host. But we’re not ready yet! There are a couple known bugs with Magento 2 and XAMPP at the moment I’m writing this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Magento admin page may not work (it shows a blank page): Fix this issue by updating the “isPathInDirectories” function inside the &lt;code&gt;Validator.php&lt;/code&gt; file as instructed in &lt;a href=&#34;https://magento.stackexchange.com/questions/252188/magento-2-2-7-admin-panel-blank-page&#34;&gt;this article.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We might not be able to upload images (like a custom logo or product pictures): Fix this issue by updating the &lt;code&gt;design_config_form.xml&lt;/code&gt; file as shown &lt;a href=&#34;https://community.magento.com/t5/Magento-2-x-Technical-Issues/A-technical-problem-with-the-server-created-an-error-Try-again/m-p/115085#M7549&#34;&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we finish making these changes, we will need to &lt;b&gt;restart the Apache service&lt;/b&gt; from the XAMPP control panel. And that’s it! We should be ready to open the Magento front page, and login into the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-front-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;An empty home page with the default theme enabled in Magento 2.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-back-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;This is how the Magento 2 back-end dashboard looks like once we login.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;setting-up-the-store-a-roadmap&#34;&gt;Setting up the store: A roadmap&lt;/h3&gt;
&lt;p&gt;The purpose of this article is to get Magento 2 up and running on Windows computer, and that’s what I hope we’ve achieved so far. But just to point in a direction on what to do next, below is my usual roadmap to set up the store, add a few products and publish the catalog on the home page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the default store name and logo from the section Content &amp;gt; Design &amp;gt; Configuration and choose “Edit” in the “Default store view”.&lt;/li&gt;
&lt;li&gt;Set up the default store configuration from the section Stores &amp;gt; Configuration.&lt;/li&gt;
&lt;li&gt;Create the product categories from the section Catalog &amp;gt; Categories. Set up a category name and its dependency from another category, or from the default master category.&lt;/li&gt;
&lt;li&gt;Create/edit the product attributes from the section Stores &amp;gt; Attributes (check the option “visible in storefront” to show the products in the product page). For example, custom attributes for book products would be Author, Publisher&amp;hellip;&lt;/li&gt;
&lt;li&gt;Add the attributes to the default attribute set from the section Stores &amp;gt; Attribute. This is required for the attributes to show up when adding a new product by default.&lt;/li&gt;
&lt;li&gt;Create a set of products from the section Catalog &amp;gt; Products. Set the custom variations for each product from the “Customizable options” section. Set up a short and long description, a custom SKU for each customizable option, and a tax mode.&lt;/li&gt;
&lt;li&gt;Add a widget to the homepage to show the product catalog from the section Content &amp;gt; Homepage &amp;gt; Edit &amp;gt; Show/Hide Editor &amp;gt; Add widget &amp;gt; Products list. Here we can also add any content we like, including HTML tags, images, and third-party content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It’s possible—​and pretty straightforward—​to run a Magento 2 instance in Windows with XAMPP. But this is just a kick-off, a simple overview: there’s a lot more to do if we want to get a full ecommerce website that is ready to scale up and support enterprise-​level traffic, like caching optimization, load balancing, and advanced server-​side monitoring.&lt;/p&gt;
&lt;p&gt;At End Point, we have professionals with a strong background in deploying powerful, reliable, fully responsive, fast, and SEO-​optimized ecommerce websites. We have experience in migrating Magento to newer versions, and even creating full Magento 2 websites from scratch. &lt;a href=&#34;/contact/&#34;&gt;Drop us a line if you want to hear more.&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>mysqldump issues after Percona 5.7 update</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/04/mysqldump-issues-after-percona-57-update/"/>
      <id>https://www.endpointdev.com/blog/2017/04/mysqldump-issues-after-percona-57-update/</id>
      <published>2017-04-07T00:00:00+00:00</published>
      <author>
        <name>Marco Matarazzo</name>
      </author>
      <content type="html">
        &lt;p&gt;During a recent CentOS 7 update, among other packages, we updated our Percona 5.7 installation to version 5.7.17-13.&lt;/p&gt;
&lt;p&gt;Quickly after that, we discovered that mysqldump stopped working, thus breaking our local mysql backup script (that complained loudly).&lt;/p&gt;
&lt;h3 id=&#34;what-happened&#34;&gt;What happened?&lt;/h3&gt;
&lt;p&gt;The error we received was:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysqldump: Couldn&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;t execute &amp;#39;&lt;/span&gt;SELECT COUNT(*) FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rocksdb\_skip\_fill\_cache&amp;#39;&amp;#39;: The &amp;#39;&lt;/span&gt;INFORMATION_SCHEMA.SESSION_VARIABLES&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; feature is disabled; see the documentation for &amp;#39;&lt;/span&gt;show_compatibility_56&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;#39;&lt;/span&gt; (3167)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a bit of investigation, we discovered this was caused by &lt;a href=&#34;https://bugs.launchpad.net/percona-server/+bug/1676401&#34;&gt;this regression bug&lt;/a&gt;, apparently already fixed but not yet available on CentOS:&lt;/p&gt;
&lt;p&gt;Everything revolves around INFORMATION_SCHEMA being deprecated in version 5.7.6, when Performance Schema tables has been added as a replacement.&lt;/p&gt;
&lt;p&gt;Basically, a regression caused mysqldump to try and use deprecated INFORMATION_SCHEMA tables instead of the new Performance Schema.&lt;/p&gt;
&lt;h3 id=&#34;how-to-fix-it&#34;&gt;How to fix it?&lt;/h3&gt;
&lt;p&gt;Immediate workaround is to add this line to /etc/my.cnf or (more likely) /etc/percona-server.conf.d/mysqld.cnf, depending on how your configuration files are organized:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;show_compatibility_56&lt;/span&gt;=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This flag was both introduced and deprecated in 5.7.6. It will be there for some time to help with the transition.&lt;/p&gt;
&lt;p&gt;It seems safe and, probably, good to keep if you have anything still actively using INFORMATION_SCHEMA tables, that would obviously be broken if not updated to the new Performance Schema since 5.7.6.&lt;/p&gt;
&lt;p&gt;With this flag, it is possible to preserve the old behavior and keep your old code in a working state, while you upgrade it. Also, according to the documentation, it should not impact or turn off the new behavior with Performance Schema.&lt;/p&gt;
&lt;p&gt;More information on how to migrate to the new Performance Schema can be found &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.7/en/performance-schema-variable-table-migration.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Making cross-blogs queries in multi-site WordPress performant</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/11/making-cross-blogs-queries-in-multi/"/>
      <id>https://www.endpointdev.com/blog/2016/11/making-cross-blogs-queries-in-multi/</id>
      <published>2016-11-02T00:00:00+00:00</published>
      <author>
        <name>Kamil Ciemniewski</name>
      </author>
      <content type="html">
        &lt;p&gt;Some time ago I was working on customizing a WordPress system for a client. The system was running in a multi-site mode, being a host of a large number of blogs.&lt;/p&gt;
&lt;p&gt;Because some blogs had not been updated in a long while, we wanted to pull information about recent posts from all of the blogs. This in turn was going to be used for pruning any blogs that weren’t considered “active”.&lt;/p&gt;
&lt;p&gt;While the above description may sound simple, the scale of the system made the task a bit more involving that it would be usually.&lt;/p&gt;
&lt;h3 id=&#34;how-wordpress-handles-the-multi-site-scenario&#34;&gt;How WordPress handles the “multi-site” scenario&lt;/h3&gt;
&lt;p&gt;The goal of computing the summary of posts for many blogs residing in the hypotethical blogging platform, &lt;strong&gt;in the same database&lt;/strong&gt; doesn’t seem so complicated. Tasks like that are being performed all the time using relational databases.&lt;/p&gt;
&lt;p&gt;The problem in WordPress arises though because of the very &lt;strong&gt;unusual&lt;/strong&gt; way that it organises blogs data. Let’s see how the database tables look like in the &amp;ldquo;normal&amp;rdquo; mode first:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It has a number of tables that start with user configurable prefix. In the case of the screenshot above, the prefix was wp_.&lt;/p&gt;
&lt;p&gt;We can see there’s a wp_posts table which contains rows related to blog posts. Thinking about the multi-blog setup, one would expect some kind of a blog_id column in the wp_posts column. Selecting data for the given blog would still be a cinch. It would also be very performant after adding an index on that column.&lt;/p&gt;
&lt;p&gt;Instead, this is what we’re getting when setting up WordPress in a multi-site mode:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;WordPress just creates a new set of tables we seen before appending the index of the blog to the tables prefix! Instead of having a nice, clean and easy to use wp_posts with the blog_id column, we get a number of tables—​one for each blog: wp_1_posts, wp_2_posts etc. Why does it matter that much? Just try to get the counts of posts in each blog in one query—​it’s impossible with such a tables setup. Getting such info involves querying &lt;strong&gt;each table separately&lt;/strong&gt;. This means that with each new blog within the system, the cost of running such sequence of queries adds up dramatically. This is also known as a N+1 problem—​bad WordPress! bad!&lt;/p&gt;
&lt;h3 id=&#34;the-approach-around-it&#34;&gt;The approach around it&lt;/h3&gt;
&lt;p&gt;The counts of posts for each blog was needed to be computed very quickly in my case. The system consisted of hundreds of different mini-blogs and the stats were to be shown in the admin dashboard. Obviously making admins wait for the page to load for a long time wasn’t an option.&lt;/p&gt;
&lt;p&gt;I went the route of creating an additional database table, holding the info about the number of posts for each blog. This table was then being updated upon each post creation, update and deletion. It was also updated upon the blog removal.&lt;/p&gt;
&lt;p&gt;WordPress has a helper function for ensuring the table is created in the database. You feed it the DDL containing the definition of the table and it makes sure it is present in the database.&lt;/p&gt;
&lt;p&gt;I created a function that was being fired on each plugin class instantiation, while making that class a singleton. Here’s the code that makes the plugin class a singleton:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BlogsPruner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureDatabaseSetup&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureBlogStatsGetUpdated&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ... other initialization here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;singleton&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(!isset(self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$_class&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__CLASS__&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$_class&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__clone&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    trigger_error(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Cannot clone the pruner plugin&amp;#39;&lt;/span&gt;, E_USER_ERROR);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ... rest of the class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BlogsPruner::&lt;span style=&#34;color:#369&#34;&gt;singleton&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next step was to implement the function ensuring there’s a stats table in the database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;ensureDatabaseSetup&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$tableName&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blogStatsTableName&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$charsetCollate&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_charset_collate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;CREATE TABLE &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$tableName&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            blog_id bigint(20),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            count_posts int(2),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            UNIQUE KEY blog_id (blog_id)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    ) &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$charsetCollate&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt;( ABSPATH . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp-admin/includes/upgrade.php&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dbDelta( &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureBlogStatsUpdated&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code uses a helper function to correctly construct the name for the table:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;blogStatsTableName&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;base_prefix&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog_stats&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This made sure the table was using the correct prefix, just like all the other tables in the database.&lt;/p&gt;
&lt;p&gt;Now, I needed to ensure the stats were updated upon each post change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;ensureBlogStatsGetUpdated&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;save_post&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;onPostUpdated&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;delete_post&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;onPostDeleted&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;onPostUpdated&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$post&lt;/span&gt; = get_post(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(wp_is_post_revision(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;) || &lt;span style=&#34;color:#369&#34;&gt;$post&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;post_status&lt;/span&gt; == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;auto-draft&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;onPostDeleted&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getBlogUserCreatedPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Here we&amp;#39;re specifically not including the post that is auto-created
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// upon the blog creation:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getBlogUserCreatedPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SELECT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            COUNT(DISTINCT `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`id`) AS count_user_posts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          FROM `wp_blogs`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          INNER JOIN `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                  ON `wp_blogs`.`blog_id` = &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$blogId&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          WHERE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_type` = &amp;#39;post&amp;#39; AND
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_status` = &amp;#39;publish&amp;#39; AND
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            TIMESTAMPDIFF(SECOND, `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_date`, `wp_blogs`.`last_updated`) &amp;gt; 60&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_row&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; intval(&lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;count_user_posts&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;updateBlogPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;count_posts&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$where&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog_id&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blogStatsTableName&lt;/span&gt;(), &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$where&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The actual production plugin implemented many more features than this sample code demonstrates. It was listing the blogs that could be considered stale, automatically pruning them after specified in the admin screen time and allowing admins to configure it via the WordPress interface. The full set of features is beyond the scope of this post.&lt;/p&gt;
&lt;p&gt;The overall result made getting the statistics about very large set of blogs very fast. The cost of querying for the number of posts of each blog was moved to incremental, small updates upon each post being created, modified or removed. For the end user, this cost was imperceptable.&lt;/p&gt;
&lt;h3 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;WordPress is loved by many users. If you’re not just a user but also working with the code, there’s a number of traps you may fall into though. If I were to employ techniques that get advised as “WordPress usual/default/preferred”—​I’d end up with a very unhappy client who’s be owning a very broken WordPress system. Fortunately, the set of WordPress tables isn’t casted in stone and you can freely extend it—​as long as you’re cautious and know what you’re doing. Provided that these two prerequisites are met—​WordPress is just a database backed platform—​like any other.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>MySQL to PostgreSQL Migration Tips</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/11/mysql-to-postgresql-migration-tips/"/>
      <id>https://www.endpointdev.com/blog/2014/11/mysql-to-postgresql-migration-tips/</id>
      <published>2014-11-26T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently was involved in a project to migrate a client’s existing application from MySQL to PostgreSQL, and I wanted to record some of my experiences in doing so in the hopes they would be useful for others.&lt;/p&gt;
&lt;p&gt;Note that these issues should not be considered exhaustive, but were taken from my notes of issues encountered and/or things that we had to take into consideration in this migration process.&lt;/p&gt;
&lt;h3 id=&#34;convert-the-schema&#34;&gt;Convert the schema&lt;/h3&gt;
&lt;p&gt;The first step is to convert the equivalent schema in your PostgreSQL system, generated from the original MySQL.&lt;/p&gt;
&lt;p&gt;We used &lt;code&gt;mysqldump --compatible=postgresql --no-data&lt;/code&gt; to get a dump which matched PostgreSQL’s quoting rules. This file still required some manual editing to cleanup some of the issues, such as removing MySQL’s “Engine” specification after a CREATE TABLE statement, but this resulted in a script in which we were able to create a skeleton PostgreSQL database with the correct database objects, names, types, etc.&lt;/p&gt;
&lt;p&gt;Some of the considerations here include the database collations/charset. MySQL supports multiple collations/charset per database; in this case we ended up storing everything in UTF-8, which matched the encoding of the PostgreSQL database, so there were no additional changes needed here; otherwise, it would have been necessary to note the original encoding of the individual tables and later convert that to UTF-8 in the next step.&lt;/p&gt;
&lt;p&gt;We needed to make the following modifications for datatypes:&lt;/p&gt;
&lt;table style=&#34;border: 1px;&#34;&gt;&lt;tbody&gt;&lt;tr&gt;     &lt;th&gt;MySQL Datatype&lt;/th&gt;     &lt;th&gt;PostgreSQL Datatype&lt;/th&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;tinyint&lt;/td&gt;     &lt;td&gt;int&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;int(&lt;i&gt;NN&lt;/i&gt;)&lt;/td&gt;     &lt;td&gt;int&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;blob&lt;/td&gt;     &lt;td&gt;bytea*&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;datetime&lt;/td&gt;     &lt;td&gt;timestamp with timezone&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;int unsigned&lt;/td&gt;     &lt;td&gt;int**&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;enum(&#39;1&#39;)&lt;/td&gt;     &lt;td&gt;bool&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;longtext&lt;/td&gt;     &lt;td&gt;text&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;td&gt;varbinary(&lt;i&gt;NN&lt;/i&gt;)&lt;/td&gt;     &lt;td&gt;bytea&lt;/td&gt;   &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;* Note: we ended up converting these specific fields to text, just given the data that was stored in these fields in actuality, which just goes to show you should review your data.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;** Note: because PostgreSQL does not have unsigned numeric types, if this feature is an important part of your data model you can/should add a CHECK constraint to the column in question to check that the value is non-negative.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A few other syntactic changes; MySQL’s UNIQUE KEY in the CREATE TABLE statement needs to just be UNIQUE.&lt;/p&gt;
&lt;p&gt;Some of the MySQL indexes were defined as FULLTEXT indexes as well, which was a keyword PostgreSQL did not recognize. We made note of these, then created just normal indexes for the time being, intending to review to what extent these actually needed full text search capabilities.&lt;/p&gt;
&lt;p&gt;Some of the AUTO_INCREMENT fields did not get the DEFAULT value set correctly to a sequence, because those types just ended up as integers without being declared a serial field, so we used the following query to correct this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;-- cleanup missing autoincrement fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WITH&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;datasource&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;k.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;k.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;column_name&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;atttypid::regtype,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;adsrc&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;information_schema.key_column_usage&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;k&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;pg_attribute&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;attrelid&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;k.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;::&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;regclass&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;attname&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;k.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;column_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LEFT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;pg_attrdef&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;adrelid&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;k.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;::&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;regclass&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;adnum&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;k.ordinal_position&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;IN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;::&lt;span style=&#34;color:#038&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;information_schema.key_column_usage&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;constraint_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LIKE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%_pkey&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;GROUP&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;HAVING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;count&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;adsrc&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;IS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;atttypid&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;integer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;::regtype&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;frags&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;quote_ident(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;column_name&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_seq&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_seqname,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;quote_ident(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;table_name&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_table,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;quote_ident(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;column_name&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_col&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;datasource&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;queries&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CREATE SEQUENCE &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_seqname&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ALTER TABLE &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_table&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; ALTER COLUMN &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_col&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SET&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DEFAULT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;nextval(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$$ || q_seqname || $$&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;setval(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$$ || q_seqname || $$&amp;#39;&lt;/span&gt;,(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;max&lt;/span&gt;(&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_col&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;) FROM &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;q_table&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;query&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;frags&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;COALESCE(string_agg(query,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;E&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;),&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;No autoincrement fixes needed&amp;#39;&lt;/span&gt;;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;queries&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;queries&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;\&lt;/span&gt;gset&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BEGIN&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;:queries&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;COMMIT&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Basically the idea is that we look for all table with a defined integer primary key (hand-waving it it by using the _pkey suffix in the constraint name), but without a current default value, then generate the equivalent SQL to create a sequence and set that table’s default value to the nextval() for the sequence in question. We also generate SQL to scan that table and set that sequence value to the next appropriate value for the column in question. (Since this is for a migration and we know we’ll be the only user accessing these tables we can ignore MVCC.)&lt;/p&gt;
&lt;p&gt;Another interesting thing about this script is that we utilize psql’s ability to store results in a variable, using the \gset command, then we subsequently execute this SQL by interpolating that corresponding variable in the same script.&lt;/p&gt;
&lt;h3 id=&#34;convert-the-data&#34;&gt;Convert the data&lt;/h3&gt;
&lt;p&gt;The next step was to prepare the data load from a MySQL data-only dump. Using a similar dump recipe as for the initial import, we used: &lt;code&gt;mysqldump --compatible=postgresql --no-create-info --extended-insert &amp;gt; data.sql&lt;/code&gt; to save the data in a dump file so we could iteratively tweak our approach to cleaning up the MySQL data.&lt;/p&gt;
&lt;p&gt;Using our dump file, we attempted a fresh load into the new PostgreSQL database. This failed initially due to multiple issues, including ones of invalid character encoding and stricter datatype interpretations in PostgreSQL.&lt;/p&gt;
&lt;p&gt;What we ended up doing was to create a filter script to handle all of the “fixup” issues needed here. This involved decoding the data and reencoding to ensure we were using proper UTF8, performing some context-sensitive datatype conversions, etc.&lt;/p&gt;
&lt;h3 id=&#34;additional-schema-modifications&#34;&gt;Additional schema modifications&lt;/h3&gt;
&lt;p&gt;As we were already using a filter script to process the data dump, we decided to take the opportunity to fixup some warts in the current table definitions. This included some fields which were varchar, but should have actually been numeric or integer; as this was a non-trivial schema (100 tables) we were able to use PostgreSQL’s system views to identify a list of columns which should should be numeric and were currently not.&lt;/p&gt;
&lt;p&gt;Since this was an ecommerce application, we identified columns that were likely candidates for data type reassignment based on field names *count, *qty, *price, *num.&lt;/p&gt;
&lt;p&gt;Once we identified the fields in question, I wrote a script to generate the appropriate ALTER TABLE statements to first drop the default, change the column type, then set the new default. This was done via a mapping between table/column name and desired output type.&lt;/p&gt;
&lt;h3 id=&#34;convert-the-application&#34;&gt;Convert the application&lt;/h3&gt;
&lt;p&gt;The final (and needless to say most involved step) was to convert the actual application itself to work with PostgreSQL. Despite the fact that these databases both speak SQL, we had to come up for solutions for the following issues:&lt;/p&gt;
&lt;h3 id=&#34;quotation-styles&#34;&gt;Quotation styles&lt;/h3&gt;
&lt;p&gt;MySQL is more lax with its quoting styles, so some of this migration involved hunting down differences in quoting styles. The codebase contained lots of double-quoted string literals, which PostgreSQL interprets as identifiers, as well as the difference in quoting of column names (backticks for MySQL, double-quotes for PostgreSQL). These had to be identified wherever they appeared and fixed to use a consistent quoting style.&lt;/p&gt;
&lt;h3 id=&#34;specific-unsupported-syntax-differences&#34;&gt;Specific unsupported syntax differences:&lt;/h3&gt;
&lt;h4 id=&#34;insert-on-duplicate-key&#34;&gt;INSERT ON DUPLICATE KEY&lt;/h4&gt;
&lt;p&gt;MySQL supports the INSERT ON DUPLICATE KEY syntax. Modifying these queries involved creating a special UPSERT-style function to support the different options in use in the code base. We isolated and categorized the uses of INSERT ON DUPLICATE KEY UPDATE into several categories: those which did a straight record replace and those which did some sort of modification. I wrote a utility script (detailed later in this article) which served to replicate the logic needed to handle this as the application would expect.&lt;/p&gt;
&lt;p&gt;Upcoming versions of PostgreSQL are likely to incorporate an INSERT &amp;hellip; ON CONFLICT UPDATE/IGNORE syntax, which would produce a more direct method of handling migration of these sorts of queries.&lt;/p&gt;
&lt;h4 id=&#34;insert-ignore&#34;&gt;INSERT IGNORE&lt;/h4&gt;
&lt;p&gt;MySQL’s INSERT &amp;hellip; IGNORE syntax allows you to insert a row and effectively ignore a primary key violation, assuming that the rest of the row is valid. You can handle this case via creating a similar UPSERT function as in the previous point. Again, this case will be easily resolved if PostgreSQL adopts the INSERT &amp;hellip; ON CONFLICT IGNORE syntax.&lt;/p&gt;
&lt;h4 id=&#34;replace-into&#34;&gt;REPLACE INTO&lt;/h4&gt;
&lt;p&gt;MySQL’s REPLACE INTO syntax effectively does a DELETE followed by an INSERT; it basically ensures that a specific version of a row exists for the given primary key value. We handle this case by just modifying these queries to do an unconditional DELETE for the Primary Key in question followed by the corresponding INSERT. We ensure these are done within a single transaction so the result is atomic.&lt;/p&gt;
&lt;h4 id=&#34;interval-syntax&#34;&gt;INTERVAL syntax&lt;/h4&gt;
&lt;p&gt;Date interval syntax can be slightly different in MySQL; intervals may be unquoted in MySQL, but &lt;strong&gt;must&lt;/strong&gt; be quoted in PostgreSQL. This project necessitated hunting down several instances to add quoting of specific literal INTERVAL instances.&lt;/p&gt;
&lt;h3 id=&#34;function-considerations&#34;&gt;Function considerations&lt;/h3&gt;
&lt;h4 id=&#34;last_insert_id&#34;&gt;last_insert_id()&lt;/h4&gt;
&lt;p&gt;Many times when you insert a records into a MySQL table, later references to this are found using the last_insert_id() SQL function. These sorts of queries need to be modified to utilize the equivalent functionality using PostgreSQL sequences, such as the currval() function.&lt;/p&gt;
&lt;h4 id=&#34;group_concat&#34;&gt;GROUP_CONCAT()&lt;/h4&gt;
&lt;p&gt;MySQL has the GROUP_CONCAT function, which serves as a string “join” of sorts. We emulate this behavior in PostgreSQL by using the string_agg aggregate function with the delimiter of choice.&lt;/p&gt;
&lt;h4 id=&#34;concat_ws--expected-to-be-but-not-an-issue-postgres-has-this-function&#34;&gt;CONCAT_WS() — expected to be but not an issue; Postgres has this function&lt;/h4&gt;
&lt;p&gt;PostgreSQL has included a CONCAT_WS() function since PostgreSQL 9.1, so this was not an issue with the specific migration, but could still be an issue if you are migrating to an older version of PostgreSQL.&lt;/p&gt;
&lt;h4 id=&#34;str_to_date&#34;&gt;str_to_date()&lt;/h4&gt;
&lt;p&gt;This function does not exist directly in PostgreSQL, but can be simulated using to_date(). Note however that the format string argument differs between MySQL and PostgreSQL’s versions.&lt;/p&gt;
&lt;h4 id=&#34;date_format&#34;&gt;date_format()&lt;/h4&gt;
&lt;p&gt;MySQL has a date_format() function which transforms a date type to a string with a given format option. PostgreSQL has similar functionality using the to_char() function; the main difference here lies in the format string specifier.&lt;/p&gt;
&lt;h4 id=&#34;datediff&#34;&gt;DateDiff()&lt;/h4&gt;
&lt;p&gt;DateDiff() does not exist in PostgreSQL, this is handled by transforming the function call to the equivalent date manipulation operators using the subtraction (-) operator.&lt;/p&gt;
&lt;h4 id=&#34;rand-to-random&#34;&gt;rand() to random()&lt;/h4&gt;
&lt;p&gt;This is more-or-less a simple function rename, as the equivalent functionality for returning a random float between 0.0 &amp;lt;= x &amp;lt;= 1.0 exists in PostgreSQL and MySQL, it’s just what the function name itself is. The other difference is that MySQL supports a scale argument so the random number for rand(&lt;em&gt;N&lt;/em&gt;) will be returned between 0.0 &amp;lt;= x &amp;lt;= N, whereas you’d have to scale the result in PostgreSQL yourself, via random() * N.&lt;/p&gt;
&lt;h4 id=&#34;if-to-case-when-else&#34;&gt;IF() to CASE WHEN ELSE&lt;/h4&gt;
&lt;p&gt;MySQL has an IF() function which returns the second argument in the case the first argument evaluates to true otherwise returns the third argument. This can be trivially converted from IF(expression1, arg2, arg3) to the equilvalent PostgreSQL syntax: CASE WHEN expression1 THEN arg2 ELSE arg3.&lt;/p&gt;
&lt;h4 id=&#34;ifnull-to-coalesce&#34;&gt;IFNULL() to COALESCE()&lt;/h4&gt;
&lt;p&gt;MySQL has a function IFNULL() which returns the first argument if it is not NULL, otherwise it returns the second argument. This can effectively be replaced by the PostgreSQL COALESCE() function, which serves the same purpose.&lt;/p&gt;
&lt;h4 id=&#34;split_part&#34;&gt;split_part()&lt;/h4&gt;
&lt;p&gt;MySQL has a built-in function called split_part() which allows you to access a specific index of an array delimited by a string. PostgreSQL also has this function, however the split_part() function in MySQL allows the index to be negative, in which case this returns the part from the right-hand side.&lt;/p&gt;
&lt;p&gt;in MySQL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split_part(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;a banana boat&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;boat&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;in PostgreSQL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;split_part(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;a banana boat&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;//&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ERROR:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;must&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;be&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;greater&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;than&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;zero&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I fixed this issue by creating a custom PL/pgSQL function to handle this case. (In my specific case, all of the negative indexes were -1; i.e., the last element in the array, so I created a function to return only the substring occurring after the last instance of the delimiter.)&lt;/p&gt;
&lt;h3 id=&#34;performance-considerations&#34;&gt;Performance considerations&lt;/h3&gt;
&lt;h4 id=&#34;you-may-need-to-revisit-count-queries&#34;&gt;You may need to revisit COUNT(*) queries&lt;/h4&gt;
&lt;p&gt;MySQL MyISAM tables have a very fast COUNT(&lt;em&gt;) calculation, owing to queries taking a table lock (which means MySQL can cache the COUNT(&lt;/em&gt;) result itself, since there can be no concurrent updates), while PostgreSQL utilizes MVCC in order to calculate table counts which necessitates a full table scan to see which rows are visible to the specific calling snapshot, so this assumption may need to be revisited in order to calculate an equivalent performant query.&lt;/p&gt;
&lt;h4 id=&#34;group-by-vs-distinct-on&#34;&gt;GROUP BY vs DISTINCT ON&lt;/h4&gt;
&lt;p&gt;MySQL is much more (&lt;em&gt;ahem&lt;/em&gt;) flexible when it comes to GROUP BY/aggregate queries, allowing some columns to be excluded in a GROUP BY or an aggregate function. Making the equivalent query in PostgreSQL involves transforming the query from SELECT &amp;hellip; GROUP BY (cols) to a SELECT DISTINCT ON (cols) &amp;hellip; and providing an explicit sort order for the rows.&lt;/p&gt;
&lt;h3 id=&#34;more-notes&#34;&gt;More notes&lt;/h3&gt;
&lt;p&gt;Don’t be afraid to script things; in fact, I would go so far as to suggest that &lt;strong&gt;everything&lt;/strong&gt; you do should be scripted. This process was complicated and there were lots of moving parts to ensure moved in tandem. There were changes being made on the site itself concurrently, so we were doing testing against a dump of the original database at a specific point-in-time. Having everything scripted ensured that this process was repeatable and testable, and that we could get to a specific point in the process without having to remember anything I’d done off-the-cuff.&lt;/p&gt;
&lt;p&gt;In addition to scripting the actual SQL/migrations, I found it helpful to script the solutions to various classifications of problems. I wrote some scripts which I used to create some of the various scaffolding/boilerplate for the tables involved. This included a script which would create an UPSERT function for a specific table given the table name, which was used when replacing the INSERT ON DUPLICATE KEY UPDATE functions. This generated script could then be tailored to handle more complex logic beyond a simple UPDATE. (One instance here is an INSERT ON DUPLICATE KEY UPDATE which increased the count of a specific field in the table instead of replacing the value.)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# -*-cperl-*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Data::Dumper&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt; &lt;span style=&#34;color:#080&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Usage: $0 &amp;lt;table&amp;gt;\n&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;@ARGV&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBI&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;connect&lt;/span&gt;(...);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@raw_cols&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;@&lt;/span&gt;{ &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;column_info(&lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;public&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;)-&amp;gt;fetchall_arrayref({}) };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;%raw_cols&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;map&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;-&amp;gt;{COLUMN_NAME} =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt; } &lt;span style=&#34;color:#369&#34;&gt;@raw_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Can&amp;#39;t find table $table\n&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@raw_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@missing_cols&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;grep&lt;/span&gt; { ! &lt;span style=&#34;color:#038&#34;&gt;defined&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$raw_cols&lt;/span&gt;{&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;} } &lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Referenced non-existing columns: @missing_cols\n&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@missing_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;%is_pk&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;map&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;-&amp;gt;{COLUMN_NAME} } &lt;span style=&#34;color:#369&#34;&gt;@raw_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@pk_cols&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;primary_key(&lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;public&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;@is_pk&lt;/span&gt;{&lt;span style=&#34;color:#369&#34;&gt;@pk_cols&lt;/span&gt;} = (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)x&lt;span style=&#34;color:#369&#34;&gt;@pk_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@data_cols&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;grep&lt;/span&gt; { ! &lt;span style=&#34;color:#369&#34;&gt;$is_pk&lt;/span&gt;{&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;} } &lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Missing PK cols from column list!\n&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@pk_cols&lt;/span&gt; == &lt;span style=&#34;color:#038&#34;&gt;grep&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$is_pk&lt;/span&gt;{&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;} } &lt;span style=&#34;color:#369&#34;&gt;@cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;No data columns!\n&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;unless&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@data_cols&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;CREATE OR REPLACE FUNCTION
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    upsert_$table (@{[
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    join &amp;#39;, &amp;#39; =&amp;gt; map {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        &amp;#34;_$_ $raw_cols{ $_ }-&amp;gt;{pg_type}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    } @cols
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;]})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;RETURNS void
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;LANGUAGE plpgsql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;AS \$EOSQL\$
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;BEGIN
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    LOOP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        UPDATE $table SET @{[
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    join &amp;#39;, &amp;#39; =&amp;gt; map { &amp;#34;$_ = _$_&amp;#34; } @data_cols
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;]} WHERE @{[
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    join &amp;#39; AND &amp;#39; =&amp;gt; map { &amp;#34;$_ = _$_&amp;#34; } @pk_cols
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;]};
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        IF FOUND THEN
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            RETURN;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        END IF;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        BEGIN
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            INSERT INTO $table (@{[join &amp;#39;,&amp;#39; =&amp;gt; @cols]}) VALUES (@{[join &amp;#39;,&amp;#39; =&amp;gt; map { &amp;#34;_$_&amp;#34; } @cols]});
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            RETURN;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        EXCEPTION WHEN unique_violation THEN
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            -- Do nothing, and loop to try the UPDATE again.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        END;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    END LOOP;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;\$EOSQL\$
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This script created an upsert function from a given table to update all columns by default, also allowing you to create one with a different number of columns upserted.&lt;/p&gt;
&lt;p&gt;I also wrote scripts which could handle/validate some of the column datatype changes. Since there were large numbers of columns which were changed, often multiple in the same table, I was able to have this script create a single ALTER TABLE statement with multiple ALTER COLUMN TYPE USING clauses, plus be able to specify the actual method that these column changes were to take place. These included several different approaches, depending on the target data type, but generally were to solve cases where there were fairly legitimate data that was not picked up by PostgreSQL’s input parsers. These included how to interpret blank fields as integers (in some cases we wanted it to be 0, in others we wanted it to be NULL), weird numeric formatting (leaving off numbers before or after the decimal point), etc.&lt;/p&gt;
&lt;p&gt;We had to fix up in several locations missing defaults for AUTO_INCREMENT columns. The tables were created with the proper datatype, however we had to find tables which matched a specific naming convention and create/associate a sequence/serial column, set the proper default here, etc. (This was detailed above.)&lt;/p&gt;
&lt;p&gt;There was a fair amount of iteration and customization in this process, as there was a fair amount of data which was not of the expected format. The process was iterative, and generally involved attempting to alter the table from within a transaction and finding the next datum which the conversion to the expected type did not work. This would result in a modification of the USING clause of the ALTER TABLE ALTER COLUMN TYPE to accommodate some of the specific issues.&lt;/p&gt;
&lt;p&gt;In several cases, there were only a couple records which had bad/bunko data, so I included explicit UPDATE statements to update those data values via primary key. While this felt a bit “impure”, it was a quick and preferred solution to the issue of a few specific records which did not fit general rules.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Version 5 of Bucardo database replication system</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/06/bucardo-5-multimaster-postgres-released/"/>
      <id>https://www.endpointdev.com/blog/2014/06/bucardo-5-multimaster-postgres-released/</id>
      <published>2014-06-23T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/06/bucardo-5-multimaster-postgres-released/image-0.jpeg&#34; style=&#34;clear: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/06/bucardo-5-multimaster-postgres-released/image-0.jpeg&#34; width=&#34;350&#34;/&gt;&lt;/a&gt;
&lt;br/&gt;&lt;small&gt;&lt;a href=&#34;https://flic.kr/p/6GYFk5&#34;&gt;Goat &amp; Kid&lt;/a&gt; by Flickr user &lt;a href=&#34;https://www.flickr.com/photos/bala_/&#34;&gt;Bala Sivakumar&lt;/a&gt;&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;Bucardo 5, the next generation of the async multimaster replication system, has been released. This major release removes the previous two source database limitation, allowing you to have as many sources (aka masters) and as many targets (aka slaves) as you wish. Bucardo can also replicate to other targets, including MySQL, MariaDB, Oracle, SQLite, MongoDB, and Redis. Bucardo has been completely rewritten and is more powerful and efficient
than the previous version, known as Bucardo 4. You can always find the latest version &lt;a href=&#34;https://bucardo.org/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This article will show a quick demonstration of Bucardo. Future posts will explore its capabilities further:
for now, we will show how easy it is to get basic multimaster replication up and running.&lt;/p&gt;
&lt;p&gt;For this demo, I used a quick and disposable server from &lt;a href=&#34;https://aws.amazon.com/&#34;&gt;Amazon Web Services&lt;/a&gt; (AWS, specifically a basic t1.micro server running Amazon Linux). If you want to follow along, it’s free and simple to create your own instance. Once it is created and you have SSH’ed in as the ec2-user account, we can start to install PostgreSQL and Bucardo.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Always a good idea:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo yum update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# This also installs other postgresql packages:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo yum install postgresql-plperl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Create a new Postgres cluster:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ initdb btest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We cannot start Postgres up yet, as this distro uses both /var/run/postgresql and
/tmp for its socket directory. Once we adjust the permissions of the first directory,
we can start Postgres, and then create our first test database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chmod 777 /var/run/postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ pg_ctl -D btest -l logfile start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ createdb shake1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we need something to replicate! For a sample dataset, I like to use the open source Shakespeare project. It’s a small, free, simple schema that is easy to load. There’s a nice little &lt;a href=&#34;https://github.com/catherinedevlin/opensourceshakespeare&#34;&gt;project on github&lt;/a&gt; the contains a ready-made Postgres schema, so we will load that in to our new database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo yum install git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git clone -q https://github.com/catherinedevlin/opensourceshakespeare.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql shake1 -q -f opensourceshakespeare/shakespeare.sql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# You can safely ignore the &amp;#39;role does not exist&amp;#39; errors&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We want to create duplicates of this database, to act as the other sources. In other words, other servers that have the identical data and can be written to. Simple enough:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ createdb shake2 -T shake1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ createdb shake3 -T shake1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Bucardo has some dependencies that need installing. You may have a different
list depending on your distro: this is what Amazon Linux needed when I wrote this.
(If you are lucky, your distro may have Bucardo already available, in which case many of the steps below can be
replaced e.g. with “yum install bucardo”—​just make sure it is using version 5 or better! (e.g. with yum info bucardo))&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo yum install  perl-ExtUtils-MakeMaker  perl-DBD-Pg \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; perl-Encode-Locale  perl-Sys-Syslog perl-boolean \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; perl-Time-HiRes  perl-Test-Simple  perl-Pod-Parser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo yum install cpan
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo y | cpan DBIx::Safe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Perl module DBIx::Safe was not available on this system’s yum, hence we
needed to install it via &lt;a href=&#34;https://www.cpan.org/&#34;&gt;CPAN&lt;/a&gt;. Once all of that is
done, we are ready to install Bucardo. We’ll grab the official tarball, verify it,
untar it, and run make install:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ wget -nv http://bucardo.org/Bucardo.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ wget -nv http://bucardo.org/Bucardo.tar.gz.asc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gpg -q --keyserver pgp.mit.edu --recv-key 14964AC8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gpg --verify Bucardo.tar.gz.asc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tar xfz Bucardo.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ln -s Bucardo-5.0.0 bucardo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd bucardo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ perl Makefile.PL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ make
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo make install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s make some small adjustments via the bucardorc file (which sets some global information). Then we can run the
“bucardo install”, which creates the main bucardo database, containing the information the Bucardo daemon will need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mkdir pid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo -e &amp;#34;piddir=pid\nlogdest=.&amp;#34; &amp;gt; .bucardorc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo install --batch --quiet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Creating superuser &amp;#39;bucardo&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that Bucardo is installed and ready to go, let’s setup the replication. In this case, we
are going to have three of our databases replicate to each other. We can do all this in
only two commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo add dbs s1,s2,s3 dbname=shake1,shake2,shake3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Added databases &amp;#34;s1&amp;#34;,&amp;#34;s2&amp;#34;,&amp;#34;s3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo add sync bard dbs=s1:source,s2:source,s3:source tables=all
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Added sync &amp;#34;bard&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Created a new relgroup named &amp;#34;bard&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Created a new dbgroup named &amp;#34;bard&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.chapter&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.character&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.character_work&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.paragraph&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.wordform&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Added table &amp;#34;public.work&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With the first command, we told Bucardo how to connect to three databases, and gave
them names for Bucardo to refer to as (s1,s2,s3). You can also specify the
port and host, but in this case the default values of 5432 and no host (Unix sockets) were sufficient.&lt;/p&gt;
&lt;p&gt;The second command creates a named replication system, called a &lt;strong&gt;sync&lt;/strong&gt;, and assigns
it the name “bard”. It needs to know where and how to replicate, so we tell it to
use the three databases s1,s2, and s3. Each of these is to act as a source, so we
append that information as well. Finally, we need to know what to replicate. In this
case, we simply want all tables (or to be more precise, all tables with a primary
key or a unique index). Notice that Bucardo always puts databases and tables into
named groups—​in this case, it was done automatically, and the dbgroup and relgroup
are simply named after the sync.&lt;/p&gt;
&lt;p&gt;Let’s verify that replication is working, by checking that a changed row replicates
to all systems involved in the sync:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql shake1 -c \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &amp;#34;update character set speechcount=123 where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ for i in {1,2,3}; do psql shake$i -tc &amp;#34;select \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; current_database(), speechcount from character \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;; done | grep s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake1       |     123
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake2       |     123
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake3       |     123&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also take a peek in the Bucardo log file, &amp;ldquo;log.bucardo&amp;rdquo; to see the replication happening:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tail -2 log.bucardo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Delta count for s1.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Totals: deletes=2 inserts=2 conflicts=0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are two deletes and inserts because the changed row was first deleted, and then inserted
(via COPY, technically) to the other two databases. Next, let’s see how Bucardo handles a conflict.
We will have the same row get changed on all the servers, which should lead to a conflict:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ for i in {1,2,3}; do psql shake$i -tc \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &amp;#34;update character set speechcount=$i$i$i \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;; done
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looking in the logs shows that we did indeed have a conflict, and that it was resolved. The default conflict resolution declares the last database to be updated the winner. All three databases now have the same winning row:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tail log.bucardo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Delta count for s1.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Delta count for s2.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Delta count for s3.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Conflicts for public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Conflicts have been resolved
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(25181) KID (bard) Totals: deletes=2 inserts=2 conflicts=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ for i in {1,2,3}; do psql shake$i -tc \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &amp;#34;select current_database(), speechcount \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; from character where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;; done | grep s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake1       |     333
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake2       |     333
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; shake3       |     333&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes when I was developing this demo, Bucardo was so fast that conflicts did not happen. In
other words, because the updates were sequential, there is a window in which Bucardo can replicate
a change before the next update occurs. The “pause a sync” feature can be very handy for this,
as well as other times in which you need a sync to temporarily stop running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo pause bard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Syncs paused: bard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql shake1 -c &amp;#34;update character set speechcount=1234 where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql shake2 -c &amp;#34;update character set speechcount=4321 where charname=&amp;#39;Hamlet&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UPDATE 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bucardo resume bard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Syncs resumed: bard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tail log.bucardo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(27344) KID (bard) Delta count for s1.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(27344) KID (bard) Delta count for s2.public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(27344) KID (bard) Conflicts for public.&amp;#34;character&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(27344) KID (bard) Conflicts have been resolved
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(27344) KID (bard) Totals: deletes=2 inserts=2 conflicts=1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a lot more to Bucardo 5 than what is shown here. Future posts will cover
other things it can do, from replicating to non-Postgres systems such as Oracle,
MySQL, or MongoDB, to using custom conflict resolution mechanisms, to transforming
data on the fly while replicating. If you have any questions, use the comments below,
or drop a line to the Bucardo mailing list at &lt;a href=&#34;mailto:bucardo-general@bucardo.org&#34;&gt;bucardo-general@bucardo.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This major release would not have been possible without the help of many people
over the years who have contributed code, raised bugs, tested Bucardo out,
and asked (and/or answered!) important questions. Please see the &lt;a href=&#34;https://github.com/bucardo/bucardo/blob/master/Changes&#34;&gt;Changes&lt;/a&gt; file
for a partial list. Thanks to all of you, and special thanks to Jon Jensen, who started
this whole project, many moons ago.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Why Can’t I Edit this Database Table? Don’t Forget the Client!</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/06/why-cant-i-edit-this-database-table/"/>
      <id>https://www.endpointdev.com/blog/2014/06/why-cant-i-edit-this-database-table/</id>
      <published>2014-06-11T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;A client of mine recently informed me of an issue he’d been having for years, where he was unable to edit a specific table in his database. He uses Access to connect to a MySQL database via ODBC, and his database has a few dozen tables, all of which are editable except this one. He reports that, when trying to edit just this one table, putting the cursor into any of the fields and attempting to change any of the data is blocked. As he put it, “It’s like the keyboard won’t respond.”&lt;/p&gt;
&lt;p&gt;We confirmed through conversation that the issue was not a MySQL permissions problem—​not that I would have expected MySQL permissions to result in such client behavior. We also confirmed that, when using a different application to connect to MySQL with Perl’s DBI, the table was editable just as the rest of the database. At this point, I didn’t have any good suspects (as neither Access nor ODBC are my strong suit) and agreed to bring up the issue with the rest of the End Point engineering team.&lt;/p&gt;
&lt;p&gt;After sending out a description of the problem, it wasn’t long before &lt;a href=&#34;/team/josh-williams/&#34;&gt;Josh Williams&lt;/a&gt; responded. He had seen this sort of behavior with Access before, where the client will lock out the table if the table does not have a unique key defined. Not surprisingly, it turned out this particular table’s implied primary key was in fact a non-unique index. I applied a primary key to the field, dropped the original index, and received confirmation from the client that the table was now editable like the rest of the database.&lt;/p&gt;
&lt;p&gt;Setting up your database is a combination of server &lt;em&gt;and&lt;/em&gt; client behaviors. While most of the focus goes into configuring the server, if you encounter unusual circumstances, don’t forget the possibility that a given client may also have requirements impacting actions normally confined to the realm of the server.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Sanity, thy name is not MySQL</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/04/sanity-thy-name-is-not-mysql/"/>
      <id>https://www.endpointdev.com/blog/2014/04/sanity-thy-name-is-not-mysql/</id>
      <published>2014-04-10T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;Probably old news, but I hit this MySQL oddity today after a long day dealing with unrelated crazy stuff and it just made me go cross-eyed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;integer&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;val&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;enum(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;));&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;val&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What row do you get? I’ll wait while you second- and third-guess yourself.&lt;/p&gt;
&lt;p&gt;It turns out that the “enum” datatype in MySQL just translates to a set of unique integer values. In our case, that means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;rsquo;&amp;rsquo; == 1&lt;/li&gt;
&lt;li&gt;&amp;lsquo;1&amp;rsquo; == 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So you get the row with (1,&amp;rsquo;&amp;rsquo;). Now, if &lt;em&gt;that&lt;/em&gt; doesn’t confuse readers of your code, I don’t know what will.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>MySQL, ASCII Null, and Data Migration</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/02/mysql-ascii-null-and-data-migration/"/>
      <id>https://www.endpointdev.com/blog/2014/02/mysql-ascii-null-and-data-migration/</id>
      <published>2014-02-26T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;Data migrations always have a wide range of challenges. I recently took on a request to determine the difficulty of converting an ecommerce shop’s MySQL 5.0 database to PostgreSQL 9.3, with the first (presumably “easier”) step being just getting the schema converted and data imported before tackling the more challenging aspect of doing a full assessment of the site’s query base to re-write the large number of custom queries that leverage MySQL-specific language elements into their PostgreSQL counterparts.&lt;/p&gt;
&lt;p&gt;During the course of this first part, which had contained a number of difficulties I had anticipated, I hit one that I definitely had not anticipated:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  value too long for type character varying(20)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Surely, the error message is absolutely clear, but how could this possibly be? The obvious answer—​that the varchar definitions were different lengths between MySQL and PostgreSQL—​was sadly quite wrong (which you knew, or I wouldn’t have written this).&lt;/p&gt;
&lt;p&gt;After isolating out the row in question, the first clear distinction of the data in question was the presence of the ASCII null character in it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#39;07989409006\007989409&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;OK, sure, that’s weird (and I have a hunch why it’s there) but I’m still puzzled how the “too long” data could be in MySQL in the first place. Then, things become both weirder and more clear when I go to take a look at the record as it exists in the source database of the dump. The table in question has many columns, so naturally I look at the results with the left-justified output of \G. Without displaying the full record for a number of reasons, here’s what I saw for that field:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*************************** 1. row ***************************
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;xxxxx: 07989409006
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wow! OK, so in MySQL, even though the ASCII null is clearly maintained in the data (the row came from a mysqldump directly from that same database) somehow the data are truncated to the string before the ASCII null, and so it must be determining string length from the same process that is truncating. To “prove” this to myself, I added to the where clause on the above query “AND xxxxx = &amp;lsquo;07989409006&amp;rsquo;”, and sure enough the record was not found.&lt;/p&gt;
&lt;p&gt;I discussed all of my findings with a colleague, who was equally bewildered and recommended I post something describing the issue. I agreed, and to make sure I was talking about an existing problem (this was MySQL 5.0 after all) I went to reproduce the issue on a more modern release (v 5.5 to be exact).&lt;/p&gt;
&lt;p&gt;I created a test case, with a new table having a single field into which I would insert a too-long string that had an ASCII null at a point such that the string to that point was less than the field length. My testing &lt;em&gt;seemed&lt;/em&gt; to indicate the problem had been fixed sometime between 5.0 and 5.5:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select version();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| version()       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 5.5.35-33.0-log |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; create table foo (foo_var varchar(5));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 0 rows affected (0.60 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; insert into foo values (&amp;#39;123\0456&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 1 row affected, 1 warning (0.93 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select * from foo;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| foo_var |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 123 4   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now to run the exact same test on my MySQL 5.0 instance, prove the deviation in behavior, and we’ll be good to go:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select version();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| version()  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 5.0.95-log |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; create table foo (foo_var varchar(5));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 0 rows affected (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; insert into foo values (&amp;#39;123\0456&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 1 row affected, 1 warning (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select * from foo;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| foo_var |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 123 4   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, my contrived test also worked on the original MySQL server that had dumped out the data that led to this problem in the first place. What was going on here?&lt;/p&gt;
&lt;p&gt;By now, I’m pretty seriously flailing about in indignation. What was so special about the data from the dump, and its behavior in the original table, that I had failed to understand and reproduce? I begin devising tests that seemed too absurd to be believed, as though I had two apples that, if I repositioned them just right, would prove 1+1 is actually 3. Then, by a stroke of fortune, I hit on the missing clue. It was a bug, all right, but not anything like what I was thinking:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select version();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| version()  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 5.0.95-log |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select * from foo;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| foo_var |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 123 4   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select * from foo \G
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*************************** 1. row ***************************
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;foo_var: 123
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Eureka! The bug was an issue rendering the output using the \G query terminator, whether restricted just to ASCII null or perhaps to other backslash-escaped characters, I do not know. Finally, to confirm whether the issue was still an issue or fixed over the intervening versions, I ran my new test on 5.5:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select version();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| version()       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 5.5.35-33.0-log |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ql&amp;gt; select * from foo;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| foo_var |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 123 4   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select * from foo \G
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*************************** 1. row ***************************
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;foo_var: 123 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I had a clear explanation for the behavior of the data with ASCII nulls, but it threw me back to starting over on the original length problem. However, without the red herring of the “too long” data being truncated to explain the size problem, I looked more closely at the specific data causing the problem and realized that counting \ and 0 as literal characters, instead of the metadata represetnation of ASCII null, the width of the data ended up exceeding the field length. Testing this hypothesis was simple enough:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; select length(&amp;#39;07989409006\007989409&amp;#39;) AS len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| len |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  20 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#####
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres=# select length(&amp;#39;07989409006\007989409&amp;#39;) AS len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; len
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  21
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 row)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This answer leaves some confusion on a couple of fronts. Why doesn’t PostgreSQL properly treat the \0 representation of ASCII null? The answer to this is, itself, a mixed answer. PostgreSQL will treat a string as a literal unless explicitly told to do otherwise. In order to tell the database a string has escape sequences in it, you have to open the string with the “E” (or “e”) identifier. However, doing that, we still don’t match the behavior we see in MySQL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres=# select length(E&amp;#39;07989409006\007989409&amp;#39;) AS len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; len
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  18
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 row)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The escape sequence consumed two additional characters because PostgreSQL interpreted the escape as octal \007:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;\o, \oo, \ooo (o = 0 - 7) -&amp;gt; octal byte value&lt;a href=&#34;http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE&#34;&gt;1&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The other potentially confusing point, then, is &lt;em&gt;why&lt;/em&gt; didn’t MySQL interpret the escape sequence as octal \007? The simple answer there is, it just doesn’t do that; MySQL makes a special case of recognizing the ASCII null character as \0:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MySQL recognizes the escape sequences shown in Table 9.1, &amp;ldquo;Special Character Escape Sequences&amp;rdquo;. For all other escape sequences, backslash is ignored.&lt;a href=&#34;http://dev.mysql.com/doc/refman/5.6/en/string-literals.html#character-escape-sequences&#34;&gt;2&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;If you look at Table 9.1, you’ll see the list of escape sequences is rather short and does not include general handling for octal or hex numeric representations.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Copy Data Between MySQL Databases with Sequel Pro</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/01/copy-data-between-mysql-databases-with/"/>
      <id>https://www.endpointdev.com/blog/2014/01/copy-data-between-mysql-databases-with/</id>
      <published>2014-01-10T00:00:00+00:00</published>
      <author>
        <name>Greg Davidson</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;sequel-pro&#34;&gt;Sequel Pro&lt;/h3&gt;
&lt;img alt=&#34;Sequel pro&#34; border=&#34;0&#34; height=&#34;350&#34; src=&#34;/blog/2014/01/copy-data-between-mysql-databases-with/image-0.png&#34; style=&#34;display:block&#34; title=&#34;sequel-pro.png&#34; width=&#34;450&#34;/&gt;
&lt;p&gt;I often use &lt;a href=&#34;https://www.sequelpro.com/&#34;&gt;Sequel Pro&lt;/a&gt; when I’m getting up to speed on the data model for a project or when I just want to debug in a more visual way than with the mysql command-line client. It’s a free OS X application that lets you inspect and manage MySQL databases. I also find it very useful for making small changes to the data while I develop and test web apps.&lt;/p&gt;
&lt;h3 id=&#34;quickly-copy-data-between-databases&#34;&gt;Quickly Copy Data Between Databases&lt;/h3&gt;
&lt;p&gt;I recently needed a way to copy a few dozen records from one &lt;a href=&#34;http://www.devcamps.org/&#34;&gt;camp&lt;/a&gt; to another. I tried using the &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.7/en/select-into.html&#34;&gt;&amp;ldquo;SELECT&amp;hellip;INTO OUTFILE&amp;rdquo;&lt;/a&gt; method but ran into a permissions issue with that approach. Using &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.7/en/mysqldump.html&#34;&gt;mysqldump&lt;/a&gt; was another option but that seemed like overkill in this case—​I only needed to copy a few records from a single table. At this point I found a really neat and helpful feature in Sequel Pro: Copy as SQL INSERT&lt;/p&gt;
&lt;img alt=&#34;Copy as sql insert&#34; border=&#34;0&#34; height=&#34;407&#34; src=&#34;/blog/2014/01/copy-data-between-mysql-databases-with/image-1.png&#34; title=&#34;copy-as-sql-insert.png&#34; width=&#34;562&#34;/&gt;
&lt;p&gt;I simply selected the records I wanted to copy and used the “Copy as SQL INSERT” feature. The SQL insert statement I needed was now copied to the system clipboard and easily copied over to the other camp and imported via the mysql command-line client.&lt;/p&gt;
&lt;h3 id=&#34;bundles&#34;&gt;Bundles&lt;/h3&gt;
&lt;p&gt;The Sequel Pro website describes &lt;a href=&#34;https://www.sequelpro.com/bundles&#34;&gt;Bundles&lt;/a&gt; which extend the functionality in various ways—​including copying data as JSON. Very handy stuff. Many thanks to the &lt;a href=&#34;https://web.archive.org/web/20140208071143/http://northofthree.com/&#34;&gt;developers&lt;/a&gt; of this fine software. If you’re on OS X, be sure to give it a try.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Increasing MySQL 5.5 max_connections on RHEL 5</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/12/increasing-mysql-55-maxconnections-on/"/>
      <id>https://www.endpointdev.com/blog/2013/12/increasing-mysql-55-maxconnections-on/</id>
      <published>2013-12-24T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Busy database-backed websites often hit scalability limits in the database first. In tuning MySQL, one of the first things to look at is the max_connections parameter, which is often too low. (Of course another thing to look at is appropriate fragment caching in your app server, HTTP object caching in your web server, and a CDN in front of it all.)&lt;/p&gt;
&lt;p&gt;When using MySQL 5.5 from Oracle’s RPMs through cPanel (MySQL55-server-5.5.32-1.cp1136) on RHEL 5.10 x86_64, there is an interesting problem if you try to increase the max_connections setting beyond 214 in /etc/my.cnf. It will silently be ignored, and the limit remains 214:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; show variables like &amp;#39;max_connections&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Variable_name   | Value |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| max_connections | 214   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The problem is that the maximum number of open files allowed is too small, by default 1024, to increase max_connections beyond 214.&lt;/p&gt;
&lt;p&gt;There are plenty of online guides that explain how to handle this, including increasing the kernel fs.file-max setting, which may be necessary by editing /etc/sysctl.conf, in this example to double the default:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fs.file-max = 2459688&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then run sysctl -p to make the change take immediate effect. (It’ll remain after reboot too.)&lt;/p&gt;
&lt;p&gt;There are also many guides that say you need to change /etc/security/limits.conf along these lines:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql           soft    nofile         4096
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql           hard    nofile         4096&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, the /etc/security/limits.conf change does not actually work when mysqld is started via the init script in /etc/init.d/mysql or via service mysql restart.&lt;/p&gt;
&lt;p&gt;With standard Red Hat mysql-server (5.1) package that provides /etc/init.d/mysqld (not /etc/init.d/mysql as the Oracle and Percona versions do), you could create a file /etc/sysconfig/mysqld containing ulimit -n 4096 and that setting will take effect for each restart of the MySQL daemon.&lt;/p&gt;
&lt;p&gt;But the ulimit -n setting hacked into the init script or put into /etc/sysconfig/mysqld isn’t really needed after all, because you can simply set open_files_limit in /etc/my.cnf:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[mysqld]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;open_files_limit = 8192
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;max_connections = 1000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# etc.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;hellip; and mysqld_safe will increase the ulimit on its own before invoking the actual mysqld daemon.&lt;/p&gt;
&lt;p&gt;After service mysql restart you can verify the new open file limit in the running process, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# cat /var/lib/mysql/*.pid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;30697
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# ps auxww | grep 30697
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql    30697 97.8  9.8 6031872 1212224 pts/1 Sl   13:09   3:01 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/lib/mysql/some.hostname.err --open-files-limit=8192 --pid-file=/var/lib/mysql/some.hostname.pid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# cat /proc/30697/limits
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Limit                     Soft Limit           Hard Limit           Units
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max cpu time              unlimited            unlimited            seconds
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max file size             unlimited            unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max data size             unlimited            unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max stack size            10485760             unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max core file size        0                    unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max resident set          unlimited            unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max processes             96086                96086                processes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max open files            8192                 8192                 files
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max locked memory         32768                32768                bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max address space         unlimited            unlimited            bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max file locks            unlimited            unlimited            locks
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max pending signals       96086                96086                signals
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max msgqueue size         819200               819200               bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max nice priority         0                    0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Max realtime priority     0                    0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the running MySQL server will reveal the desired max_connections setting stuck this time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; show variables like &amp;#39;max_connections&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Variable_name   | Value |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| max_connections | 1000  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 row in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The relevant code in /usr/bin/mysqld_safe is here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if test -w / -o &amp;#34;$USER&amp;#34; = &amp;#34;root&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;then
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # ... [snip] ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  if test -n &amp;#34;$open_files&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  then
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ulimit -n $open_files
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fi
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if test -n &amp;#34;$open_files&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;then
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  append_arg_to_args &amp;#34;--open-files-limit=$open_files&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I have found that some newer versions of either MySQL55-server or cPanel or some intersection of the two has made manually specifying a higher open_files_limit in /etc/my.cnf no longer necessary, although it does not do any harm.&lt;/p&gt;
&lt;p&gt;But in conclusion, if you find yourself hitting the mysterious max_connections = 214 limit, just add the appropriately-sized open_files_limit to the [mysqld] section of /etc/my.cnf and restart the server with service mysql restart, and your problem should be solved!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to Enable MySQL Event Scheduler</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/how-to-enable-mysql-event-scheduler/"/>
      <id>https://www.endpointdev.com/blog/2013/11/how-to-enable-mysql-event-scheduler/</id>
      <published>2013-11-26T00:00:00+00:00</published>
      <author>
        <name>Emanuele “Lele” Calò</name>
      </author>
      <content type="html">
        &lt;p&gt;You may think that you already know what’s the opposite of “DISABLED”, but with MySQL Event Scheduler you’ll be wrong.&lt;/p&gt;
&lt;p&gt;In fact MySQL Event Scheduler may have three different states[1][2]:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DISABLED -  The Event Scheduler thread does not run [1]. In addition, the Event Scheduler state cannot be changed at runtime.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OFF (default) - The Event Scheduler thread does not run [1]. When the Event Scheduler is OFF it can be started by setting the value of event_scheduler to ON.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ON - The Event Scheduler is started; the event scheduler thread runs and executes all scheduled events.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So if you’re going to find it in the &lt;strong&gt;DISABLED&lt;/strong&gt; state and instinctively set it to &lt;strong&gt;ENABLED&lt;/strong&gt; you’ll end up with a non-starting MySQL daemon.&lt;/p&gt;
&lt;p&gt;Be warned and stay safe out there!&lt;/p&gt;
&lt;p&gt;[2]: When the Event Scheduler is not running does not appear in the output of SHOW PROCESSLIST&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>SFTP virtual users with ProFTPD and Rails: Part 2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/07/sftp-virtual-users-with-proftpd-and/"/>
      <id>https://www.endpointdev.com/blog/2013/07/sftp-virtual-users-with-proftpd-and/</id>
      <published>2013-07-10T00:00:00+00:00</published>
      <author>
        <name>Brian Gadoury</name>
      </author>
      <content type="html">
        &lt;p&gt;In &lt;a href=&#34;/blog/2012/12/sftp-virtual-users-with-proftpd-and/&#34;&gt;Part 1 of “SFTP virtual users with ProFTPD and Rails”&lt;/a&gt;, I introduced &lt;a href=&#34;http://www.proftpd.org/&#34;&gt;ProFTPD&lt;/a&gt;’s virtual users and presented my annotated proftpd.conf that I used to integrate virtual users with a Rails application. Here in Part 2, I’ll show how we generate virtual user credentials, how we display them to the user, as well as our SftpUser ActiveRecord model that does the heavy lifting.&lt;/p&gt;
&lt;p&gt;Let’s start at the top with the SFTP credentials UI. Our app’s main workflow actually has users doing most of their uploads through a sweet &lt;a href=&#34;https://www.plupload.com/&#34;&gt;Plupload widget&lt;/a&gt;. So, by default, the SFTP functionality is hidden behind a simple button sitting to the right of the Plupload widget:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/07/sftp-virtual-users-with-proftpd-and/image-0.png&#34;/&gt;&lt;/div&gt;
&lt;p&gt;The user can click that button to open the SFTP UI, or the Plupload widget will open it automatically if the user tries to upload a file through the widget that is too big. Either way, it uses a jQuery UI function to slide the SFTP UI open. Before it makes the jQuery call, an Ajax request is made to request a new SFTP virtual user username and password. When that request returns, we populate those two textboxes. At that point, that virtual user exists as a new row in the sftp_users database table. At this point in the workflow, the user will be able to login using those credentials, and upload their files.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/07/sftp-virtual-users-with-proftpd-and/image-1.png&#34;/&gt;&lt;/div&gt;
&lt;p&gt;Let’s go look at our shiny new virtual user in the sftp_users table:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-[ RECORD 1 ]-+-----------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id            | 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user_id       | 3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;username      | user13A84C76
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;passwd        | {sha1}5kT0WDb/5H6C8M92dTeiKQO0Kg0=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uid           |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sftp_group_id |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;homedir       | /home/sftp/uploads/3/user13A84C76
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;shell         |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;created_at    | 2013-07-08 17:24:24.780753&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The id and user_id fields are ignored by ProFTPD (they’re just standard primary/foreign key integers.) The username can be anything, so we just use “user” plus a random-enough hex string. The way we configured ProFTPD in Part 1 means our virtual users have absolutely nothing to do with normal system users, so the uid, sftp_group_id and shell fields can be empty.&lt;/p&gt;
&lt;p&gt;The homedir has three components: “/home/sftp/uploads” must exist and be writable by the effective UID that is running your Rails app. The directory named “3” is the value from the user_id field. (Adding that additional level lets us associate these virtual users with actual web app users should we need to debug, etc.) Lastly, each &lt;em&gt;virtual&lt;/em&gt; user gets its own directory underneath that. In this example, ProFTPD will create the “3” and “3/user13A84C76” directories when that virtual user logs in. The format of the value in the passwd field is highly dependent upon your proftpd.conf settings for SQLAuthTypes. The format shown here matches the proftpd.conf shown in Part 1 of this article, if you’re a copying and pasting type. The created_at field is ignored by ProFTPD, but it’s handy info to have.&lt;/p&gt;
&lt;p&gt;Let’s look at the relevant methods implemented by our SftpUser ActiveRecord model.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;hash_password&lt;/span&gt;(password)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{sha1}&amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Base64&lt;/span&gt;.strict_encode64(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Digest&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SHA1&lt;/span&gt;.digest(generate_password))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;generate_username&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt; + &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SecureRandom&lt;/span&gt;.hex(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;).upcase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;generate_password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;SecureRandom&lt;/span&gt;.hex(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;).downcase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Admittedly, there’s no deep wizardry going on there. The critical piece is that the password gets hashed in the format that ProFTPD expects. Our hash_password method handles that for us here. (You can do whatever you want for the username and the raw password.) As a caveat, upgrading from Ruby 1.8.7 to 1.9.3 required replacing the Base64.encode64 call with Base64.strict_encode64.&lt;/p&gt;
&lt;p&gt;Ok, that’s the end of Part 2. We now have a new virtual user with credentials that can be used to login via SFTP. We’ve displayed those credentials to the user using jQuery UI and Ajax. Each virtual user gets its own chrooted home directory that is created only if and when they log in. The next steps in the workflow will be discussed in Part 3: How the user signals they are done uploading via SFTP, using Resque to make the Rails app process the files uploaded by the user, preventing upload overlap and re-entrant problems, and cleaning up after a virtual user’s files are successfully processed offline by the Rails app.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Foreign Data Wrappers</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/05/foreign-data-wrappers/"/>
      <id>https://www.endpointdev.com/blog/2013/05/foreign-data-wrappers/</id>
      <published>2013-05-13T00:00:00+00:00</published>
      <author>
        <name>Josh Tolley</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2013/05/foreign-data-wrappers/image-0-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/05/foreign-data-wrappers/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Original images from Flickr user &lt;a href=&#34;https://www.flickr.com/photos/jenniferwilliams/&#34;&gt;jenniferwilliams&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of our clients, for various historical reasons, runs both MySQL and PostgreSQL to support their website. Information for user login lives in one database, but their customer activity lives in the other. The eventual plan is to consolidate these databases, but thus far, other concerns have been more pressing. So when they needed a report combining user account information and customer activity, the involvement of two separate databases became a significant complicating factor.&lt;/p&gt;
&lt;p&gt;In similar situations in the past, using earlier versions of PostgreSQL, we’ve written scripts to pull data from MySQL and dump it into PostgreSQL. This works well enough, but we’ve updated PostgreSQL fairly recently, and can use the SQL/MED features added in version 9.1. &lt;a href=&#34;https://wiki.postgresql.org/wiki/Foreign_data_wrappers&#34;&gt;SQL/MED&lt;/a&gt; (“MED” stands for “Management of External Data”) is a decade-old standard designed to allow databases to make external data sources, such as text files, web services, and even other databases look like normal database tables, and access them with the usual SQL commands. PostgreSQL has supported some of the SQL/MED standard since version 9.1, with a feature called Foreign Data Wrappers, and among other things, it means we can now access MySQL through PostgreSQL seamlessly.&lt;/p&gt;
&lt;p&gt;The first step is to install the right software, called mysql_fdw. It comes to us via Dave Page, PostgreSQL core team member and contributor to many projects. It’s worth noting Dave’s warning that he considers this experimental code. For our purposes it works fine, but as will be seen in this post, we didn’t push it too hard. We opted to &lt;a href=&#34;https://github.com/EnterpriseDB/mysql_fdw&#34;&gt;download the source&lt;/a&gt; and build it, but installing using pgxn works as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ env USE_PGXS=1 pgxnclient install mysql_fdw
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO: best version: mysql_fdw 1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO: saving /tmp/tmpjrznTj/mysql_fdw-1.0.1.zip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO: unpacking: /tmp/tmpjrznTj/mysql_fdw-1.0.1.zip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO: building extension
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -fpic -I/usr/include/mysql -I. -I. -I/home/josh/devel/pg91/include/postgresql/server -I/home/josh/devel/pg91/include/postgresql/internal -D_GNU_SOURCE -I/usr/include/libxml2   -c -o mysql_fdw.o mysql_fdw.c
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql_fdw.c: In function ‘mysqlPlanForeignScan’:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql_fdw.c:466:8: warning: ‘rows’ may be used uninitialized in this function [-Wmaybe-uninitialized]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -fpic -shared -o mysql_fdw.so mysql_fdw.o -L/home/josh/devel/pg91/lib -L/usr/lib  -Wl,--as-needed -Wl,-rpath,&amp;#39;/home/josh/devel/pg91/lib&amp;#39;,--enable-new-dtags  -L/usr/lib/x86_64-linux-gnu -lmysqlclient -lpthread -lz -lm -lrt -ldl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO: installing extension
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt; ... snip ... &amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here I’ll refer to the documentation provided in &lt;a href=&#34;https://github.com/EnterpriseDB/mysql_fdw/blob/master/README.md&#34;&gt;mysql_fdw’s README&lt;/a&gt;. The first step in using a foreign data wrapper, once the software is installed, is to create the foreign server, and the user mapping. The foreign server tells PostgreSQL how to connect to MySQL, and the user mapping covers what credentials to use. This is an interesting detail; it means the foreign data wrapper system can authenticate with external data sources in different ways depending on the PostgreSQL user involved. You’ll note the pattern in creating these objects: each simply takes a series of options that can mean whatever the FDW needs them to mean. This allows the flexibility to support all sorts of different data sources with one interface.&lt;/p&gt;
&lt;p&gt;The final step in setting things up is to create a foreign table. In MySQL’s case, this is sort of like a view, in that it creates a PostgreSQL table from the results of a MySQL query. For our purposes, we needed access to several thousand structurally identical MySQL tables (I mentioned the goal is to move off of this one day, right?), so I automated the creation of each table with a simple bash script, which I piped into psql:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;for i in `cat mysql_tables`; do
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &amp;#34;CREATE FOREIGN TABLE mysql_schema.$i ( ... table definition ...)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SERVER mysql_server OPTIONS (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            database &amp;#39;mysqldb&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query &amp;#39;SELECT ... some fields ... FROM $i&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In a step not shown above, this script also consolidates the data from each table into one, native PostgreSQL table, to simplify later reporting. In our case, pulling the data once and reporting on the results is perfectly acceptable; in other words, data a few seconds old wasn’t a concern. We also didn’t need to write back to MySQL, which presumably could complicate things somewhat. We did, however, run into the same data validation problems PostgreSQL users habitually complain about when working with MySQL. Here’s an example, in my own test database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; create table bad_dates (mydate date);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 0 rows affected (0.07 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; insert into bad_dates values (&amp;#39;2013-02-30&amp;#39;), (&amp;#39;0000-00-00&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query OK, 2 rows affected (0.02 sec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Records: 2  Duplicates: 0  Warnings: 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that MySQL silently transformed ‘2013-02-30’ into ‘0000-00-00’. Sigh. Then, in psql we do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;josh=# create extension mysql_fdw;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE EXTENSION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;josh=# create server mysql_svr foreign data wrapper mysql_fdw options (address &amp;#39;127.0.0.1&amp;#39;, port &amp;#39;3306&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE SERVER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;josh=# create user mapping for public server mysql_svr options (username &amp;#39;josh&amp;#39;, password &amp;#39;&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE USER MAPPING
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;josh=# create foreign table bad_dates (mydate date) server mysql_svr options (query &amp;#39;select * from test.bad_dates&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE FOREIGN TABLE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;josh=# select * from bad_dates ;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  date/time field value out of range: &amp;#34;0000-00-00&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’ve told PostgreSQL we’ll be feeding it valid dates, but MySQL’s idea of a valid date differs from PostgreSQL’s, and the latter complains when the dates don’t meet its stricter requirements. Several different workarounds exist, including admitting that ‘0000-00-00’ really is wrong and cleaning up MySQL, but in this case, we modified the query underlying the foreign table to fix the dates on the fly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CASE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;disabled&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHEN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;0000-00-00&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;THEN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ELSE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;disabled&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;END&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;-- various other fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;some_table&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Fortunately this is the only bit of MySQL / PostgreSQL impedance mismatch that has tripped us up thus far; we’d have to deal with any others we found individually, just as we did this one.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>SFTP virtual users with ProFTPD and Rails: Part 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/12/sftp-virtual-users-with-proftpd-and/"/>
      <id>https://www.endpointdev.com/blog/2012/12/sftp-virtual-users-with-proftpd-and/</id>
      <published>2012-12-20T00:00:00+00:00</published>
      <author>
        <name>Brian Gadoury</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently worked on a Rails 3.2 project that used the sweet &lt;a href=&#34;http://www.plupload.com/&#34;&gt;PLupload&lt;/a&gt; JavaScript/Flash upload tool to upload files to the web app. To make it easier for users to upload large and/or remote files to the app, we also wanted to let them upload via SFTP. The catch was, our users didn&amp;rsquo;t have SFTP accounts on our server and we didn&amp;rsquo;t want to get into the business of creating and managing SFTP accounts. Enter: &lt;a href=&#34;http://www.proftpd.org/&#34;&gt;ProFTPD&lt;/a&gt; and virtual users.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.proftpd.org/docs/directives/linked/config_ref_SQLAuthenticate.html&#34;&gt;ProFTPD&amp;rsquo;s virtual users&lt;/a&gt; concept allows you to point ProFTPD at a SQL database for your user and group authentication. This means SFTP logins don&amp;rsquo;t need actual system logins (although you can mix and match if you want). Naturally, this is perfect for dynamically creating and destroying SFTP accounts. Give your web app the ability to create disposable SFTP credentials and automatically clean up after the user is done with them, and you have a self-maintaining system.&lt;/p&gt;
&lt;p&gt;Starting from the inside-out, you need to configure ProFTPD to enable virtual users. Here are the relevant parts from our proftpd.conf:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;##
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Begin proftpd.conf excerpt. For explanation of individual config directives, see the 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# great ProFTPD docs at http://www.proftpd.org/docs/directives/configuration_full.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;##
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DefaultServer off
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Umask 002
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AllowOverwrite on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Don&amp;#39;t reference /etc/ftpusers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UseFtpUsers off
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;ifmodule mod_sftp.c=&amp;#34;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Enable SFTP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SFTPEngine on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Enable SQL based authentication
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLAuthenticate on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# From http://www.proftpd.org/docs/howto/CreateHome.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Note that the CreateHome params are kind of touchy and easy to break.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CreateHome on 770 dirmode 770 uid ~ gid ~
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# chroot them to their home directory
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DefaultRoot ~
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Defines the expected format of the passwd database field contents. Hint: An
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# encrypted password will look something like: {sha1}IRYEEXBUxvtZSx3j8n7hJmYR7vg=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLAuthTypes OpenSSL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# That &amp;#39;*&amp;#39; makes that module authoritative and prevents proftpd from
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# falling through to system logins, etc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AuthOrder mod_sql.c*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# sftp_users and sftp_groups are the database tables that must be defined with
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# the proceeding column names. You can have other columns in these tables and
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# ProFTPD will leave them alone. The sftp_groups table can be empty, but it must exist.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLUserInfo sftp_users username passwd uid sftp_group_id homedir shell
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLGroupInfo sftp_groups name id members
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SFTPHostKey /etc/ssh/ssh_host_rsa_key
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SFTPHostKey /etc/ssh/ssh_host_dsa_key
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SFTPCompression delayed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SFTPAuthMethods password
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RequireValidShell no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# SQLLogFile is very verbose, but helpful for debugging while you&amp;#39;re getting this working
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLLogFile /var/log/proftpd_sql.sql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Customize these for production
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLConnectInfo database@localhost:5432 dbuser dbpassword
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# The UID and GID values here are set to match the user that runs our web app because our
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# web app needs to read and delete files uploaded via SFTP. Naturally, that is outside
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# the requirements of a basic virtual user setup. But in our case, our web app user needs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# to be able to cd into a virtual user&amp;#39;s homedir, and run a `ls` in there. Also, note that
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# setting these two IDs here (instead of in our sftp_users table) *only* makes sense if
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# you are using the DefaultRoot directive to chroot virtual users.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLDefaultUID  510
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLDefaultGID  500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/ifmodule&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The CreateHome piece was the trickiest to get working just right for our use-case. But there are two reasons for that; we needed our web app to be able to read/delete the uploaded files, and we wanted to make ProFTPD create those home directories itself. (And it only creates that home directory once a user successfully logs in via SFTP. That means you can be more liberal in your UI with generating credentials that may never get used without having to worry about a ton of empty home directories lying about.)&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it for the introductory &amp;ldquo;Part 1&amp;rdquo; of this article. In Part 2, I&amp;rsquo;ll show how we generate credentials, the workflow behind displaying those credentials, and our SftpUser ActiveRecord model that handles it all. In Part 3, I&amp;rsquo;ll finish up by running through exactly how our web app accesses these files, and how it cleans up after it&amp;rsquo;s done.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Case Sensitive MySQL Searches</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/case-sensitive-mysql-searches/"/>
      <id>https://www.endpointdev.com/blog/2012/10/case-sensitive-mysql-searches/</id>
      <published>2012-10-18T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;p&gt;MySQL’s support for case sensitive search is explained somewhat opaquely in the aptly titled &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.6/en/case-sensitivity.html&#34;&gt;Case Sensitivity in String Searches&lt;/a&gt; documentation. In short, it explains that by default, MySQL won’t treat strings as case sensitive when executing a statement such as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;first_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;contacts&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;first_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;REGEXP&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^[a-z]&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This simple search to look for contacts whose first name starts with a lower case letter, will return &lt;em&gt;all&lt;/em&gt; contacts because in the default character set used by MySQL (&lt;a href=&#34;https://en.wikipedia.org/wiki/ISO/IEC_8859-1&#34;&gt;latin1&lt;/a&gt;), upper and lower case letters share the same “sort value”.&lt;/p&gt;
&lt;p&gt;UPDATE: After many helpful comments from readers, it would seem the term I should have used was collation, not sort value. The documentation for both &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.6/en/charset-general.html&#34;&gt;MySQL&lt;/a&gt; and &lt;a href=&#34;https://www.postgresql.org/docs/9.2/static/collation.html&#34;&gt;PostgreSQL&lt;/a&gt; have lengthy discussions on the topic.&lt;/p&gt;
&lt;h3 id=&#34;enough-with-the-backstory-how-do-i-perform-case-sensitive-searches&#34;&gt;Enough with the backstory, how do I perform case sensitive searches!&lt;/h3&gt;
&lt;p&gt;The docs say to convert the string representation to a binary one. This allows “comparisons [to] use the numeric values of the bytes in the operands”. Let’s see it in action:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;first_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;contacts&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;BINARY&lt;/span&gt;(first_name)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;REGEXP&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^[a-z]&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are other strategies available, such as changing the character set being used for comparisons with the &lt;a href=&#34;https://dev.mysql.com/doc/refman/5.0/en/charset-collate.html&#34;&gt;COLLATE&lt;/a&gt; function. This would likely work better for cases where you had many columns to compare.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;first_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;contacts&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;first_name&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;REGEXP&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^[a-z]&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;COLLATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;latin1_bin;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can even go so far as to have MySQL switch character sets and collations. But you do have to do this for each database, each table, and each column you need to convert. Not terribly fun.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Simple bash shell script for running batch MySQL jobs</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/simple-bash-shell-script-for-running/"/>
      <id>https://www.endpointdev.com/blog/2012/10/simple-bash-shell-script-for-running/</id>
      <published>2012-10-16T00:00:00+00:00</published>
      <author>
        <name>Barrett Griffith</name>
      </author>
      <content type="html">
        &lt;p&gt;The other day I needed to run a simple mysql job to backup and delete some database records on a live server. Being a live server, it is important to make sure you aren&amp;rsquo;t asking the database to take on jobs that could potentially lock it up. Better to run a batch job. Running a batch is simple. You can call it right from the mysql console with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[path_to]/[the_batch_script].&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sql&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But what if there are millions of records that need deleting? &lt;strong&gt;Bash shell script to the rescue.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here is the idea of the SQL job that needed to get run a few times:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;START&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TRANSACTION&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Find what you want to delete and put a LIMIT on your batch size */&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TEMPORARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;records_to_delete_temp&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.....&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;limit&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Creating backup table to archive spam orders */&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;IF&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NOT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;EXISTS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_backup`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LIKE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records`;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_backup`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_to_delete_temp`);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Delete Dependents - If your records have foreign key dependencies, delete them first */&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DELETE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`dependent_1`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;record_id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_to_delete_temp`);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DELETE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`dependent_2`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;record_id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_to_delete_temp`);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Delete the records */&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DELETE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;select&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records_to_delete_temp`);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Return count of remaining records - the where clause should be the same as the original select for the records to delete */&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;COUNT&lt;/span&gt;(*)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;`records`&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;where&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;....&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;COMMIT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;COUNT&lt;/span&gt;(*)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will return the remaining record count to the shell script.&lt;/p&gt;
&lt;p&gt;And the shell script&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;ret&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;mysql -u [user] -p[password] --skip-column-names [database_name]  &amp;lt; [the_batch_script].sql&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; [ &lt;span style=&#34;color:#369&#34;&gt;$ret&lt;/span&gt; -gt &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rows left: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$ret&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;ret&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;mysql -u [user] -p[password] --skip-column-names [database_name]  &amp;lt; [the_batch_script].sql&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--skip-column-names&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the little nugget that gives you clean output from the mysql script. Without &amp;ndash;skip-column-names you will have to get more creative with parsing the returned table.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-p[password]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just a friendly reminder, no space after the -p option.&lt;/p&gt;
&lt;p&gt;Should you really be running batches to remove millions of records from a live server with a shell script?&lt;/p&gt;
&lt;p&gt;Just because you can doesn&amp;rsquo;t mean you should. Before pulling the trigger, back up, consider what could go wrong, have a plan in place to address the possible failure.&lt;/p&gt;
&lt;p&gt;Find something fun for your hands to do while bash takes care of running the jobs. At the least, cross your fingers!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Points of Interest</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/05/points-of-interest/"/>
      <id>https://www.endpointdev.com/blog/2012/05/points-of-interest/</id>
      <published>2012-05-15T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s been a fairly straight forward week at work, but I have stumbled a few interesting finds along the way this week.&lt;/p&gt;
&lt;h3 id=&#34;vim-adventures&#34;&gt;&lt;a href=&#34;https://vim-adventures.com/&#34;&gt;Vim Adventures&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally! A game based approach to learning Vim keyboard commands. I was hoping someone would do this. It’s just getting started (only two levels) and sadly, it looks like it’ll be charging money to unlock higher levels. However, some things are worth paying for. I’ve found just playing the first two levels a few times have helped retrain my brain to not take my fingers off the home row. It’s still quite buggy and seems to only work in Chrome. I found several times I needed to close all my Chrome windows after playing. Also, incognito mode seems to help with the bugs, as it disables all extensions you may have installed.&lt;/p&gt;
&lt;h3 id=&#34;mysql-query-comments-in-rails&#34;&gt;&lt;a href=&#34;https://signalvnoise.com/posts/3130-mini-tech-note-mysql-query-comments-in-rails&#34;&gt;MySQL query comments in Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ever wanted to know where that slow query was being called from? Well, if you’re using MySQL with your Rails 2.3.x or 3.x.x app, you can get debug information about what controller’s action made the call. Check out 37Signals new &lt;a href=&#34;https://github.com/37signals/marginalia&#34;&gt;marginalia&lt;/a&gt; gem.&lt;/p&gt;
&lt;h3 id=&#34;how-to-use-ec2-as-a-web-proxy&#34;&gt;&lt;a href=&#34;https://kev.inburke.com/kevin/how-to-use-ec2-as-a-web-proxy/&#34;&gt;How to use EC2 as a web proxy&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Kevin Burke provides a very detailed HOWTO article for working around restrictions you may experience in the course of an Internet based life. Pretty amazing what Amazon’s &lt;a href=&#34;https://aws.amazon.com/free&#34;&gt;free usage tier&lt;/a&gt; puts out there; of course it’s only free for 12 months.&lt;/p&gt;
&lt;h3 id=&#34;include-pids-in-your-logs&#34;&gt;&lt;a href=&#34;https://web.archive.org/web/20121005121328/http://help.papertrailapp.com/discussions/suggestions/18-include-pids-in-rails-productionlog&#34;&gt;Include PIDs in your Logs&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For many Rails developers we get comfortable looking at development log files. Sometimes when I have to investigate a customer issue on a production server using logs, I wished I had the level of detail the development logger had. While that’s a wish, I’m finding it mandatory to include PID numbers in my production logs. In production systems with multiple requests being handled simultaneously, Rails logs start to become unusable. It’s not clear which log lines are from which requests. Adding a PID in front of the time stamp can help untangle the mess. Here are some example approaches to this for &lt;a href=&#34;https://andre.arko.net/2011/08/18/pid-numbers-for-rails-3-logs/&#34;&gt;Rails 3.x.x&lt;/a&gt; and &lt;a href=&#34;http://nhw.pl/wp/2009/09/15/logger-simple-yet-powerful-rails-debugging-tool&#34;&gt;Rails 2.3.x&lt;/a&gt;. Also, if you’re really a log-lover and manage a lot of servers, check out &lt;a href=&#34;https://papertrailapp.com/&#34;&gt;Papertrail&lt;/a&gt;, it looks very impressive for $7/mo.&lt;/p&gt;
&lt;h3 id=&#34;spectrum-shortages---why-its-happening-and-what-can-be-done&#34;&gt;&lt;a href=&#34;http://s4gru.com/entry/160-spectrum-shortages-why-its-happening-and-what-can-be-done/&#34;&gt;Spectrum Shortages - Why it’s happening and what can be done&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Telecom is not an area I have much familiarity with, but I found this article to be an interesting read. For example did you know that that largest owner of spectrum licenses are “under-capitalized or unwilling to build out networks” to use the spectrum? So while AT&amp;amp;T and Verizon struggle to meet the iPhone 4S’s data demands (twice as much as iPhone 4!), “there are some companies that have spectrum, but they’re struggling financially. Or they aren’t quite sure what to do with the spectrum. And others that have the money and business model, but need the spectrum.” It seems the way out of the mess is 4G, offering to improve the efficiency of spectrum use by 700 percent.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>MySQL replication monitoring on Ubuntu 10.04 with Nagios and NRPE</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/01/mysql-replication-monitoring-on-ubuntu/"/>
      <id>https://www.endpointdev.com/blog/2012/01/mysql-replication-monitoring-on-ubuntu/</id>
      <published>2012-01-21T00:00:00+00:00</published>
      <author>
        <name>Brian Buchalter</name>
      </author>
      <content type="html">
        &lt;p&gt;If you&amp;rsquo;re using MySQL replication, then you&amp;rsquo;re probably counting on it for some fairly important need.  Monitoring via something like Nagios is generally considered a best practice.  This article assumes you&amp;rsquo;ve already got your Nagios server setup and your intention is to add a Ubuntu 10.04 NRPE client.  This article also assumes the Ubuntu 10.04 NRPE client is your MySQL replication master, not the slave.  The OS of the slave does not matter.&lt;/p&gt;
&lt;h3 id=&#34;getting-the-nagios-nrpe-client-setup-on-ubuntu-1004&#34;&gt;Getting the Nagios NRPE client setup on Ubuntu 10.04&lt;/h3&gt;
&lt;p&gt;At first it wasn&amp;rsquo;t clear what packages would be appropriate packages to install.  I was initially misled by the naming of the nrpe package, but I found the correct packages to be:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install nagios-nrpe-server nagios-plugins&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The NRPE configuration is stored in /etc/nagios/nrpe.cfg, while the plugins are installed in /usr/lib/nagios/plugins/ (or lib64).  The installation of this package will also create a user nagios which does not have login permissions.  After the packages are installed the first step is to make sure that /etc/nagios/nrpe.cfg has some basic configuration.&lt;/p&gt;
&lt;p&gt;Make sure you note the server port (defaults to 5666) and open it on any firewalls you have running.  (I got hung up because I forgot I have both a software and hardware firewall running!)  Also make sure the server_address directive is commented out; you wouldn&amp;rsquo;t want to only listen locally in this situation.  I recommend limiting incoming hosts by using your firewall of choice.&lt;/p&gt;
&lt;h3 id=&#34;choosing-what-nrpe-commands-you-want-to-support&#34;&gt;Choosing what NRPE commands you want to support&lt;/h3&gt;
&lt;p&gt;Further down in the configuration, you&amp;rsquo;ll see lines like command[check_users]=/usr/lib/nagios/plugins/check_users -w 5 -c 10.  These are the commands you plan to offer the Nagios server to monitor.  Review the contents of /usr/lib/nagios/plugins/ to see what&amp;rsquo;s available and feel free to add what you feel is appropriate.  Well designed plugins should give you a usage if you execute them from the command line.  Otherwise, you may need to open your favorite editor and dig in!&lt;/p&gt;
&lt;p&gt;After verifying you&amp;rsquo;ve got your NRPE configuration completed and made sure to open the appropriate ports on your firewall(s), let&amp;rsquo;s restart the NRPE service:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;service nagios-nrpe-server restart&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This would also be an appropriate time to confirm that the nagios-nrpe-server service is configured to start on boot.  I prefer the chkconfig package to help with this task, so if you don&amp;rsquo;t already have it installed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install chkconfig
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chkconfig | grep nrpe
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# You should see...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nagios-nrpe-server     on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# If you don&amp;#39;t...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chkconfig nagios-nrpe-server on&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;pre-flight-check---running-check_nrpe&#34;&gt;Pre flight check - running check_nrpe&lt;/h3&gt;
&lt;p&gt;Before going any further, log into your Nagios server and run check_nrpe and make sure you can execute at least one of the commands you chose to support in nrpe.cfg.  This way, if there are any issues, it is obvious now, while we&amp;rsquo;ve not started modifying your Nagios server configuration.  The location of your check_nrpe binary may vary, but the syntax is the same:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check_nrpe -H host_of_new_nrpe_client -c command_name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If your command output something useful and expected, your on the right track.  A common error you might see: Connection refused by host.  Here&amp;rsquo;s a quick checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Did you start the nagios-nrpe-server service?&lt;/li&gt;
&lt;li&gt;Run netstat -lunt on the NRPE client to make sure the service is listening on the right address and ports.&lt;/li&gt;
&lt;li&gt;Did you open the appropriate ports on all your firewall(s)?&lt;/li&gt;
&lt;li&gt;Is there NAT translation which needs configuration?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;adding-the-check_mysql_replication-plugin&#34;&gt;Adding the check_mysql_replication plugin&lt;/h3&gt;
&lt;p&gt;There is a lot of noise out there on Google for Nagios plugins which offer MySQL replication monitoring.  I wrote the following one using ideas pulled from several existing plugins.  It is designed to run on the MySQL master server, check the master&amp;rsquo;s log position and then compare it to the slave&amp;rsquo;s log position.  If there is a difference in position, the alert is considered Critical.  Additionally, it checks the slave&amp;rsquo;s reported status, and if it is not &amp;ldquo;Waiting for master to send event&amp;rdquo;, the alert is also considered critical.  You can find the source for the plugin at my Github account under the project &lt;a href=&#34;https://github.com/bbuchalter/check_mysql_replication/blob/master/check_mysql_replication.sh&#34;&gt;check_mysql_replication&lt;/a&gt;.  Pull that source down into your plugins directory (/usr/lib/nagios/plugins/ (or lib64)) and make sure the permissions match the other plugins.&lt;/p&gt;
&lt;p&gt;With the plugin now in place, add a command to your nrpe.cfg.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;command[check_mysql_replication]=sudo /usr/lib/nagios/plugins/check_mysql_replication.sh -H &amp;lt;slave_host_address&amp;gt;&amp;lt;/slave_host_address&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point you may be saying, WAIT!  How will the user running this command (nagios) have login credentials to the MySQL server?  Thankfully we can create a home directory for that nagios user, and add a .my.cnf configuration with the appropriate credentials.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;usermod -d /home/nagios nagios #set home directory
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir /home/nagios
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod 755 /home/nagios
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chown nagios:nagios /home/nagios
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# create /home/nagios/.my.cnf with your preferred editor with the following:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[client]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user=example_replication_username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;password=replication_password
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod 600 /home/nagios/.my.cnf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chown nagios:nagios /home/nagios/.my.cnf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This would again be an appropriate place to run a pre flight check and run the check_nrpe from your Nagios server to make sure this configuration works as expected.  But first we need to add this command to the sudoer&amp;rsquo;s file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nagios ALL= NOPASSWD: /usr/lib/nagios/plugins/check_mysql_replication.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;wrapping-up&#34;&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;At this point, you should run another check_nrpe command from your server and see the replication monitoring report.  If not, go back and check these steps carefully.  There are lots of gotchas and permissions and file ownership are easily overlooked.  With this in place, just add the NRPE client using the existing templates you have for your Nagios servers and make sure the monitoring is reporting as expected.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>MySQL Integer Size Attributes</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/04/mysql-integer-size-attributes/"/>
      <id>https://www.endpointdev.com/blog/2011/04/mysql-integer-size-attributes/</id>
      <published>2011-04-28T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;MySQL has those curious size attributes you can apply to integer data types. For example, when creating a table, you might see:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tinyint(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_si&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;smallint&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_int&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;int&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_bi&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;bigint&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;Query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OK,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rows&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;affected&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;05&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;-----------+-------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Field&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;Type&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;Null&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;Key&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;Default&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Extra&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;-----------+-------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tinyint(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;YES&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_si&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;smallint&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;YES&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_int&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;int&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;YES&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_bi&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;bigint&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;   &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;YES&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;-----------+-------------+------+-----+---------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rows&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;03&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I had always assumed those size attributes were limiters, MySQL’s way of providing some sort of constraint on the integers allowed in the field. While doing some recent work for a MySQL client, I attempted to enforce the range of a tinyint according to that assumption. In reality, I only wanted a sign field, and would have liked to have applied a “CHECK field IN (-1,1)”, but without check constraints I figured at least keeping obviously incorrect data out would be better than nothing.&lt;/p&gt;
&lt;p&gt;I wanted to see what MySQL’s behavior would be on data entry that failed the limiters. I was hoping for an error, but expecting truncation. What I discovered was neither.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(field_ti)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;Query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OK,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;affected&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(field_ti)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;Query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OK,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;affected&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rows&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(field_ti)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;Query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OK,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;affected&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rows&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(field_ti)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;VALUES&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;Query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OK,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;affected&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;foo;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;field_ti&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;+&lt;span style=&#34;color:#888&#34;&gt;----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rows&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sec)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;mysql&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Two possible conclusions followed immediately: either the limiter feature was horribly broken, or those apparent sizes didn’t represent a limiter feature. A full review of MySQL’s &lt;a href=&#34;https://dev.mysql.com/doc/refman/8.0/en/numeric-types.html&#34;&gt;Numeric Types&lt;/a&gt; documentation provided the answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MySQL supports an extension for optionally specifying the display width of integer data types in parentheses following the base keyword for the type. For example, INT(4) specifies an INT with a display width of four digits. This optional display width may be used by applications to display integer values having a width less than the width specified for the column by left-padding them with spaces. (That is, this width is present in the metadata returned with result sets. Whether it is used or not is up to the application.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The display width does not constrain the range of values that can be stored in the column.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And, so, the lesson is repeated: Beware assumptions.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>MySQL Ruby Gem CentOS RHEL 5 Installation Error Troubleshooting</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/02/mysql-ruby-gem-centos-rhel-5/"/>
      <id>https://www.endpointdev.com/blog/2010/02/mysql-ruby-gem-centos-rhel-5/</id>
      <published>2010-02-09T00:00:00+00:00</published>
      <author>
        <name>Adam Vollrath</name>
      </author>
      <content type="html">
        &lt;p&gt;Building and installing the Ruby mysql gem on freshly-installed Red Hat based systems sometimes produces the frustratingly ambiguous error below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# gem install mysql&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/usr/bin/ruby extconf.rb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;checking &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; mysql_ssl_set()... no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;checking &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; rb_str_set_len()... no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;checking &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; rb_thread_start_timer()... no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;checking &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; mysql.h... no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;checking &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; mysql/mysql.h... no
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*** extconf.rb failed ***
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Could not create Makefile due to some reason, probably lack of
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;necessary libraries and/or headers.  Check the mkmf.log file &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; more
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;details.  You may need configuration options.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Searching the web for info on this error yields two basic solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://serverfault.com/questions/54532/installing-mysql-ruby-gem-on-64-bit-centos/60296#60296&#34;&gt;Install the mysql-devel package&lt;/a&gt; (this provides the mysql.h file in /usr/include/mysql/).&lt;/li&gt;
&lt;li&gt;Run gem install mysql &amp;ndash; &amp;ndash;with-mysql-config=/usr/bin/mysql_config or some &lt;a href=&#34;http://www.wzzrd.com/2008/02/building-mysql-gem-centos5-hell-usually.html&#34;&gt;other additional options&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are correct but not sufficient. Because this gem compiles a library to interface with MySQL’s C API, the gcc and make packages are also required to create the build environment:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# yum install mysql-devel gcc make&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# gem install mysql -- --with-mysql-config=/usr/bin/mysql_config&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, if you’re using your distro’s ruby (not a custom build like &lt;a href=&#34;/blog/2009/06/ruby-enterprise-edition-rpm-packages/&#34;&gt;Ruby Enterprise Edition&lt;/a&gt;), you can install &lt;a href=&#34;https://fedoraproject.org/wiki/EPEL&#34;&gt;EPEL&lt;/a&gt;’s ruby-mysql package along with their rubygem-rails and other packages.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Gathering server information with boxinfo</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/01/gathering-server-information-with/"/>
      <id>https://www.endpointdev.com/blog/2010/01/gathering-server-information-with/</id>
      <published>2010-01-15T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ve just publicly released another Postgres-related script, this one called “boxinfo”. Basically, it gathers information about a box (server), hence the catchy and original name. It outputs the information it finds into an HTML page, or into a MediaWiki formatted page.&lt;/p&gt;
&lt;p&gt;The goal of boxinfo is to have a simple, single script that quickly gathers important information about a server into a web page, so that you can get a quick overview of what is installed on the server and how things are configured. It’s also useful as a reference page when you are trying to remember which server was it that had Bucardo version 4.5.0 installed and was running pgbouncer.&lt;/p&gt;
&lt;p&gt;As we use MediaWiki internally here at End Point (running with a Postgres backend, naturally), the original (and default) format is HTML with some MediaWiki specific items inside of it.&lt;/p&gt;
&lt;p&gt;Because it is meant to run on a wide a range of boxes as possible, it’s written in Perl. While we’ve run into a few boxes over the years that did not have Perl installed, the number that had any other language you choose (except perhaps sh) is much greater. It requires no other Perl modules, and simply makes a lot of system calls.&lt;/p&gt;
&lt;p&gt;Various information about the box is gathered. System wide things such as mount points, disk space, schedulers, packaging systems are gathered first, along with versions of many common Unix utilities. We also gather information on some programs where more than just the version number is important, such as puppet, heartbeat, and lifekeeper. Of course, we also go into a great amount of detail about all the installed Postgres clusters on the box as well.&lt;/p&gt;
&lt;p&gt;The program tries its best to locate every active Postgres cluster on the box, and then gathers information about it, such as where pg_xlog is linked to, any contrib modules installed, any interesting configuration variables from postgresql.conf, the size of each database, and lots of detailed information about any Slony or Bucardo configurations it finds.&lt;/p&gt;
&lt;p&gt;The main page for it is on the Bucardo wiki at &lt;a href=&#34;https://bucardo.org/wiki/Boxinfo&#34;&gt;https://bucardo.org/wiki/Boxinfo&lt;/a&gt;. That page details the various command line options and should be considered the canonical documentation for the script. The latest version of boxinfo can be downloaded from that page as well. For any enhancement requests or problems to report, please submit an issue at &lt;a href=&#34;https://github.com/bucardo/bucardo/issues&#34;&gt;https://github.com/bucardo/bucardo/issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What exactly does the output look like? We’ve got an example on the wiki showing the sample output from a run against my laptop. Some of the items were removed, but it should give you an idea of what the script can do, particularly with regards to the Postgres information: &lt;a href=&#34;https://bucardo.org/Boxinfo/Example/&#34;&gt;https://bucardo.org/Boxinfo/Example/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The script is still a little rough, so we welcome any patches, bug reports, requests, or comments. The development version can be obtained by running: &lt;strong&gt;git clone git://bucardo.org/boxinfo.git&lt;/strong&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>State of the Postgres project</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/01/state-of-postgres-project/"/>
      <id>https://www.endpointdev.com/blog/2010/01/state-of-postgres-project/</id>
      <published>2010-01-04T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s been interesting watching &lt;a href=&#34;https://web.archive.org/web/20100304140554/http://blogs.the451group.com/opensource/2009/10/26/everything-you-always-wanted-to-know-about-mysql-but-were-afraid-to-ask/&#34;&gt;the&lt;/a&gt; &lt;a href=&#34;https://web.archive.org/web/20100821005629/http://blogs.the451group.com/opensource/2009/11/12/everything-you-always-wanted-to-know-about-mysql-but-were-afraid-to-ask-part-two/&#34;&gt;MySQL&lt;/a&gt; &lt;a href=&#34;https://web.archive.org/web/20100109005715/http://blogs.the451group.com/opensource/2010/01/04/everything-you-always-wanted-to-know-about-mysql-but-were-afraid-to-ask-part-three/&#34;&gt;drama&lt;/a&gt; unfold, but I have to take issue when people start trying to drag Postgres into it again by spreading FUD (Fear, Uncertainty, and Doubt). Rather than simply rebut the FUD, I thought this was a good opportunity to examine the strength of the Postgres project.&lt;/p&gt;
&lt;p&gt;Monty recently espoused the following in a &lt;a href=&#34;https://web.archive.org/web/20100329100324/http://ostatic.com/blog/oracle-mysql-and-the-gpl-dont-take-montys-word-for-it&#34;&gt;blog comment&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“&amp;hellip;This case is about ensuring that Oracle doesn’t gain money and market share by killing an Open Source competitor. Today MySQL, tomorrow PostgreSQL.&lt;/p&gt;
&lt;p&gt;Yes, PostgreSQL can also be killed; To prove the case, think what would happen if someone managed to ensure that the top 20 core PostgreSQL developers could not develop PostgreSQL anymore or if each of these developers would fork their own PostgreSQL project.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Later on in his blog &lt;a href=&#34;http://monty-says.blogspot.com/2009/12/help-keep-internet-free.html&#34;&gt;he raises the same theme again&lt;/a&gt; with a slight bit more detail:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Note that not even PostgreSQL is safe from this threat! For example, Oracle could buy some companies developing PostgreSQL and target the core developers. Without the core developers working actively on PostgreSQL, the PostgreSQL project will be weakened tremendously and it could even die as a result.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Is this a valid concern? It’s easy enough to overlook it considering the Sturm und Drang in Monty’s recent posts, but I think this is something worth seriously looking into. Specifically, is the Postgres project capable of withstanding a direct threat from a large company with deep pockets (e.g. Oracle)?&lt;/p&gt;
&lt;p&gt;To get to the answer, let’s run some numbers first. Monty mentions the “top 20” Postgres developers. If we look at the &lt;a href=&#34;https://www.postgresql.org/community/contributors/&#34;&gt;community contributors&lt;/a&gt; page, we see that there are in fact 25 major developers listed, as well as 7 core members, so 20 would indeed be a significant chunk of that page. To dig deeper, I looked at the cvs logs for the year of 2009 for the Postgres project, and ran some scripts against them. The 9185 commits were spread across 16 different people, and about 16 other people were mentioned in the commit notes as having contributed in some way (e.g. a patch from a non-committer). So again, it looks like Monty’s number of 20 is a pretty good approximation.&lt;/p&gt;
&lt;p&gt;However (and you knew there was a however), the catch comes from being able to actually stop 20 of those people from working on Postgres. There are basically two ways to do this: Oracle could buy out a company, or they could hire (buy out) a person. The first problem is that the Postgres community is very widely distributed. If you look at the people on the community contributors page, you’ll see that the 32 people work for 24 different companies. Further, no one company holds sway: the median is one company, and the high water mark is a mere three developers. All of this is much better than it was years ago, in the total number and in the distribution.&lt;/p&gt;
&lt;p&gt;The next fly in the ointment is that buying out a company is not always easy to do, despite the size of your pockets. Many companies on that list are privately held and will not sell. Even if you did buy out the company, there is no way to prevent the people working there from then moving to a different company. Finally, buying out some companies just isn’t possible, even if you are Oracle, because there are some big names on the list of people employing major Postgres developers: Google, Red Hat, Skype, and SRA. Then of course there is NTT, which is a &lt;strong&gt;really, really&lt;/strong&gt; big company (larger than Oracle). NTT’s Postgres developers are not always as visible as some of the English-speaking ones, but NTT employs a lot of people to work on Postgres (which is extremely popular in Japan).&lt;/p&gt;
&lt;p&gt;The second way is hiring people directly. However, people can not always be bought off. Sure, some of the developers might choose to leave if Oracle offered them $20 million dollars, but not all of them (Larry, I might go for $19 million, call me :). Even if they did leave, the depth of the Postgres community should not be underestimated. For every “major developer” on that page, there are many others who read the lists, know the code well, but just haven’t, for one reason or another, made it on to that list. At a rough guess, I’d say that there are a couple hundred people in the world who would be able to make commits to the Postgres source code. Would all of them be as fast or effective as some of the existing people? Perhaps not, but the point is that it would be nigh impossible to thin the pool fast enough to make a dent.&lt;/p&gt;
&lt;p&gt;The project’s &lt;a href=&#34;https://postgresql.markmail.org/&#34;&gt;email lists&lt;/a&gt; are as strong as ever, to such a point that I find it hard to keep up with the traffic, a problem I did not have a few years ago. The number of conferences and people attending each is growing rapidly, and there is a great demand for people with Postgres skills. The number of projects using Postgres, or offering it as an alternative database backend, is constantly growing. It’s no longer difficult to find a hosting provider that offers Postgres in addition to MySQL. Most important of all, the project continues to regularly release stable new versions. Version 8.5 will probably be released in 2010.&lt;/p&gt;
&lt;p&gt;In conclusion, the state of the Postgres project is in great shape, due to the depth and breadth of the community (and the depth and breadth of the developer subset). There is no danger of Postgres going the MySQL route; the PG developers are spread across a number of businesses, the code (and documentation!) is BSD, and no one firm holds sway in the project.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
