MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

LAMP php-gd Libraries

with one comment

Everything seemed complete after configuring my standalone MySQL instance to a LAMP installation, but last night I started playing with the image files. It turns out that I failed to install the php-gd library.

There’s very little feedback when you try to troubleshoot why you can’t read an image. In fact, the error message for reading the BLOB from MySQL was only available on the local Firefox browser:

The image "http://localhost/ConvertMySQLBlobToImage.php" cannot be displayed because it contains errors.

The fix requires root to install the php-gd library with the yum utility:

yum install php-gd

You’ll need to answer y to one question during the installation:

Loaded plugins: langpacks, refresh-packagekit
mysql-connectors-community                                  | 2.5 kB  00:00     
mysql-tools-community                                       | 2.5 kB  00:00     
mysql56-community                                           | 2.5 kB  00:00     
pgdg93                                                      | 3.6 kB  00:00     
updates/20/x86_64/metalink                                  |  16 kB  00:00     
Resolving Dependencies
--> Running transaction check
---> Package php-gd.x86_64 0:5.5.22-1.fc20 will be installed
--> Processing Dependency: libt1.so.5()(64bit) for package: php-gd-5.5.22-1.fc20.x86_64
--> Running transaction check
---> Package t1lib.x86_64 0:5.1.2-14.fc20 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package         Arch            Version                 Repository        Size
================================================================================
Installing:
 php-gd          x86_64          5.5.22-1.fc20           updates           89 k
Installing for dependencies:
 t1lib           x86_64          5.1.2-14.fc20           updates          164 k
 
Transaction Summary
================================================================================
Install  1 Package (+1 Dependent package)
 
Total download size: 252 k
Installed size: 629 k
Is this ok [y/d/N]: y
Downloading packages:
(1/2): php-gd-5.5.22-1.fc20.x86_64.rpm                      |  89 kB  00:00     
(2/2): t1lib-5.1.2-14.fc20.x86_64.rpm                       | 164 kB  00:01     
--------------------------------------------------------------------------------
Total                                              157 kB/s | 252 kB  00:01     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : t1lib-5.1.2-14.fc20.x86_64                                   1/2 
  Installing : php-gd-5.5.22-1.fc20.x86_64                                  2/2 
  Verifying  : php-gd-5.5.22-1.fc20.x86_64                                  1/2 
  Verifying  : t1lib-5.1.2-14.fc20.x86_64                                   2/2 
 
Installed:
  php-gd.x86_64 0:5.5.22-1.fc20                                                 
 
Dependency Installed:
  t1lib.x86_64 0:5.1.2-14.fc20                                                  
 
Complete!

After the installation, you can run the info.php program, which contains the following:

1
2
3
<?php
  phpinfo();
?>

You’ll find the following gd library display in the result from the info.php program:

Linux_php_gd_library

After retesting, we get both large text and blob files displayed in the web page:

LAMPBlobTextPage

As always, I hope this helps others. Especially, those who are working with your LAMP stack implementation of images.

Written by maclochlainn

March 31st, 2015 at 6:20 pm

MySQL bind-address

without comments

While I try to keep things simple, sometimes eliminating options and explanations comes back to haunt me. After posting how to open a Fedora firewall port for a LAMP stack, somebody got trapped by my instructions for installing MySQL on Fedora. They got stuck because they had the following setting in their /etc/my.cnf file:

bind-address=localhost.localdomain

I’d suggested using that bind-address value for a DHCP VMware Fedora installation in Step #7. I was trying to create an example for an isolated testing instance, which is why I set the bind-address to a localhost.localdomain value. They raised the following error when they tried to connect their base operating system’s version of MySQL Workstation to the Fedora VM:

Failed to Connect to MySQL at 192.168.2.168:3306 with user student

or, this dialog image:

MySQLBindAddr01

Before you do the next step, please ensure you’re using the right IP address. You can find that by running this command as an authorized sudoer:

ifconfig | grep inet.*netmask.*broadcast

In this case, the command returns:

        inet 192.168.2.168  netmask 255.255.255.0  broadcast 192.168.2.255

I’ve since added instructions to the older post to set the bind-address value in the my.cnf file as follows when they want to support external connections (naturally that means authorizing port 3306):

bind-address=0.0.0.0

After you reset the /etc/my.cnf file, you must stop and start, or restart the mysqld service. You can do that as the root user like this:

systemctl restart mysqld

Then, you can test a student user connection from MySQL Workbench like this:

MySQLBindAddr02

If the student user is authorized and the password is correct, you’ll see that the connection now works:

MySQLBindAddr03

As always, I hope this helps those working through similar issues.

Written by maclochlainn

March 29th, 2015 at 10:30 pm

Open Fedora Port 80

with 4 comments

After installing the LAMP stack on Fedora, you need to open port 80 in the Firewall to access the PHP programs on the Fedora instance from external servers. You can open a firewall port by launching the firewall-config application as the root user with the following syntax:

firewall-config

The firewall-config utility opens the following dialog:

FedoraFirewall1

Click on the Ports tab, and you’ll see the following:

FedoraFirewall2

Skip this step if you only want to set the runtime privilege to the port. Click on the Runtime tab and change it to Permanent if you want the port to be accessible when you reboot your OS.

FedoraFirewallPermanent

Click on Add button to add a port exception, and you’ll see the following:

FedoraFirewall3

Enter Port 80 for the Apache server unless you used a different value for the Apache server’s listener port. If you’re not sure open the /etc/httpd/conf/httpd.conf file and check for the following line (default value shown):

Listen 80

Click the OK button to set the port exception. Then, you can connect to the Linux machine with the IP address, a DNS name, or a name you resolve in your local hosts file, like:

http://192.168.2.1/query.php

You can find the IP address of your Fedora image by inspecting the /etc/hosts file or capture a DHCP assigned address with the following command as the root user (or with sudo as a valid sudoer user):

ifconfig -a

It should return the following image, which is based on the data stored in MySQL’s studentdb database, as qualified in yesterday’s blog post:

ExternalWebPage

I hope this helps those setting up a LAMP instance to work with the MySQL database.

Written by maclochlainn

March 29th, 2015 at 12:35 am

Fedora Install LAMP

with 5 comments

My students wanted an extra credit assignment, so I thought a LAMP configuration and test would be appropriate. The only problem was I hadn’t added it to their course VMware instance. So, here are the instructions to install Apache2, PHP, and MySQLi for a complete LAMP stack when MySQL is already installed.

The post builds on my Fedora Install of MySQL and MySQL Workbench on Fedora posts from last year. It also presumes that you’ve installed a studentdb database but you need to know how to do that let me know (but it hasn’t changed much from the example at the bottom of this old MySQL 5.1 blog post).

You install Apache2 with the following command as the root user, or with the sudo command as a sudoer-list user:

yum install httpd

The following displays the results of starting the yum utility to install httpd, and you need to reply with a y to complete the installation:

Loaded plugins: langpacks, refresh-packagekit
mysql-connectors-community                                  | 2.5 kB  00:00     
mysql-tools-community                                       | 2.5 kB  00:00     
mysql56-community                                           | 2.5 kB  00:00     
pgdg93                                                      | 3.6 kB  00:00     
updates/20/x86_64/metalink                                  |  16 kB  00:00     
updates                                                     | 4.9 kB  00:00     
updates/20/x86_64/primary_db                                |  13 MB  00:04     
(1/2): updates/20/x86_64/updateinfo                         | 1.9 MB  00:02     
(2/2): updates/20/x86_64/pkgtags                            | 1.4 MB  00:01     
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.10-2.fc20 will be installed
--> Processing Dependency: httpd-tools = 2.4.10-2.fc20 for package: httpd-2.4.10-2.fc20.x86_64
--> Processing Dependency: system-logos-httpd for package: httpd-2.4.10-2.fc20.x86_64
--> Running transaction check
---> Package fedora-logos-httpd.noarch 0:21.0.1-1.fc20 will be installed
---> Package httpd-tools.x86_64 0:2.4.10-2.fc20 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package                  Arch         Version              Repository     Size
================================================================================
Installing:
 httpd                    x86_64       2.4.10-2.fc20        updates       1.2 M
Installing for dependencies:
 fedora-logos-httpd       noarch       21.0.1-1.fc20        fedora         28 k
 httpd-tools              x86_64       2.4.10-2.fc20        updates        79 k
 
Transaction Summary
================================================================================
Install  1 Package (+2 Dependent packages)
 
Total download size: 1.3 M
Installed size: 4.0 M
Is this ok [y/d/N]: y
Downloading packages:
(1/3): fedora-logos-httpd-21.0.1-1.fc20.noarch.rpm          |  28 kB  00:00     
(2/3): httpd-2.4.10-2.fc20.x86_64.rpm                       | 1.2 MB  00:01     
(3/3): httpd-tools-2.4.10-2.fc20.x86_64.rpm                 |  79 kB  00:00     
--------------------------------------------------------------------------------
Total                                              815 kB/s | 1.3 MB  00:01     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : httpd-tools-2.4.10-2.fc20.x86_64                             1/3 
  Installing : fedora-logos-httpd-21.0.1-1.fc20.noarch                      2/3 
  Installing : httpd-2.4.10-2.fc20.x86_64                                   3/3 
  Verifying  : httpd-2.4.10-2.fc20.x86_64                                   1/3 
  Verifying  : fedora-logos-httpd-21.0.1-1.fc20.noarch                      2/3 
  Verifying  : httpd-tools-2.4.10-2.fc20.x86_64                             3/3 
 
Installed:
  httpd.x86_64 0:2.4.10-2.fc20                                                  
 
Dependency Installed:
  fedora-logos-httpd.noarch 0:21.0.1-1.fc20  httpd-tools.x86_64 0:2.4.10-2.fc20 
 
Complete!

Next, you install php as the root user with the following command:

yum install php

The following displays when you install php, and you need to reply with a y to complete the installation:

Loaded plugins: langpacks, refresh-packagekit
Resolving Dependencies
--> Running transaction check
---> Package php.x86_64 0:5.5.22-1.fc20 will be installed
--> Processing Dependency: php-common(x86-64) = 5.5.22-1.fc20 for package: php-5.5.22-1.fc20.x86_64
--> Processing Dependency: php-cli(x86-64) = 5.5.22-1.fc20 for package: php-5.5.22-1.fc20.x86_64
--> Running transaction check
---> Package php-cli.x86_64 0:5.5.22-1.fc20 will be installed
---> Package php-common.x86_64 0:5.5.22-1.fc20 will be installed
--> Processing Dependency: php-pecl-jsonc(x86-64) for package: php-common-5.5.22-1.fc20.x86_64
--> Running transaction check
---> Package php-pecl-jsonc.x86_64 0:1.3.6-1.fc20 will be installed
--> Processing Dependency: /usr/bin/pecl for package: php-pecl-jsonc-1.3.6-1.fc20.x86_64
--> Processing Dependency: /usr/bin/pecl for package: php-pecl-jsonc-1.3.6-1.fc20.x86_64
--> Running transaction check
---> Package php-pear.noarch 1:1.9.5-6.fc20 will be installed
--> Processing Dependency: php-xml for package: 1:php-pear-1.9.5-6.fc20.noarch
--> Processing Dependency: php-posix for package: 1:php-pear-1.9.5-6.fc20.noarch
--> Running transaction check
---> Package php-process.x86_64 0:5.5.22-1.fc20 will be installed
---> Package php-xml.x86_64 0:5.5.22-1.fc20 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package               Arch          Version               Repository      Size
================================================================================
Installing:
 php                   x86_64        5.5.22-1.fc20         updates        2.6 M
Installing for dependencies:
 php-cli               x86_64        5.5.22-1.fc20         updates        3.9 M
 php-common            x86_64        5.5.22-1.fc20         updates        1.0 M
 php-pear              noarch        1:1.9.5-6.fc20        updates        343 k
 php-pecl-jsonc        x86_64        1.3.6-1.fc20          updates         34 k
 php-process           x86_64        5.5.22-1.fc20         updates         77 k
 php-xml               x86_64        5.5.22-1.fc20         updates        247 k
 
Transaction Summary
================================================================================
Install  1 Package (+6 Dependent packages)
 
Total download size: 8.2 M
Installed size: 32 M
Is this ok [y/d/N]: y
Downloading packages:
(1/7): php-5.5.22-1.fc20.x86_64.rpm                         | 2.6 MB  00:03     
(2/7): php-cli-5.5.22-1.fc20.x86_64.rpm                     | 3.9 MB  00:03     
(3/7): php-common-5.5.22-1.fc20.x86_64.rpm                  | 1.0 MB  00:00     
(4/7): php-pear-1.9.5-6.fc20.noarch.rpm                     | 343 kB  00:00     
(5/7): php-pecl-jsonc-1.3.6-1.fc20.x86_64.rpm               |  34 kB  00:00     
(6/7): php-process-5.5.22-1.fc20.x86_64.rpm                 |  77 kB  00:00     
(7/7): php-xml-5.5.22-1.fc20.x86_64.rpm                     | 247 kB  00:00     
--------------------------------------------------------------------------------
Total                                              1.1 MB/s | 8.2 MB  00:07     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : php-cli-5.5.22-1.fc20.x86_64                                 1/7 
  Installing : php-process-5.5.22-1.fc20.x86_64                             2/7 
  Installing : php-xml-5.5.22-1.fc20.x86_64                                 3/7 
  Installing : 1:php-pear-1.9.5-6.fc20.noarch                               4/7 
  Installing : php-common-5.5.22-1.fc20.x86_64                              5/7 
  Installing : php-pecl-jsonc-1.3.6-1.fc20.x86_64                           6/7 
  Installing : php-5.5.22-1.fc20.x86_64                                     7/7 
  Verifying  : php-5.5.22-1.fc20.x86_64                                     1/7 
  Verifying  : php-common-5.5.22-1.fc20.x86_64                              2/7 
  Verifying  : php-cli-5.5.22-1.fc20.x86_64                                 3/7 
  Verifying  : 1:php-pear-1.9.5-6.fc20.noarch                               4/7 
  Verifying  : php-process-5.5.22-1.fc20.x86_64                             5/7 
  Verifying  : php-xml-5.5.22-1.fc20.x86_64                                 6/7 
  Verifying  : php-pecl-jsonc-1.3.6-1.fc20.x86_64                           7/7 
 
Installed:
  php.x86_64 0:5.5.22-1.fc20                                                    
 
Dependency Installed:
  php-cli.x86_64 0:5.5.22-1.fc20        php-common.x86_64 0:5.5.22-1.fc20      
  php-pear.noarch 1:1.9.5-6.fc20        php-pecl-jsonc.x86_64 0:1.3.6-1.fc20   
  php-process.x86_64 0:5.5.22-1.fc20    php-xml.x86_64 0:5.5.22-1.fc20         
 
Complete!

After installing the software, you can set the Apache server to start automatically with the following command:

chkconfig httpd on

However, that command only starts the Apache server the next time you boot the server. You use the following command as the root user to start the Apache server:

apachectl start

You can verify the installation with the following command as the root user:

ps -ef | grep httpd | grep -v grep

It should return:

root      5433     1  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5434  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5435  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5436  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5437  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5438  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    5442  5433  0 17:03 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

and, then verify the listening port with the following command as the root user:

netstat -tulpn | grep :80

It should return the following when both the Apache server is listening on port 80 and the Oracle multi-protocol server is listening on port 8080:

tcp6       0      0 :::80                   :::*                    LISTEN      5433/httpd          
tcp6       0      0 :::8080                 :::*                    LISTEN      1505/tnslsnr

After verifying the connection, you can test it by creating the traditional info.php program file in the /var/www/http directory. The file should contain the following:

1
2
3
<?php
  phpinfo();
?>

You can test it by opening the Firefox browser and entering the following URL from the Fedora Linux image:

http://localhost/info.php

It should display the typical diagnostic page. This verifies the configuration of the Apache and PHP servers. The next step verifies whether you have the mysqli library to connect to the MySQL database.

You create a mysqli_check.php script, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<header>
<title>Static Query Object Sample</title>
<style type="text/css">
  /* HTML element styles. */
  table {background:white;border-style:solid;border-width:3px;border-color:black;border-collapse:collapse;}
  th {text-align:center;font-style:bold;background:lightgray;border:solid 1px gray;}
  td {border:solid 1px gray;}
 
  /* Class tag element styles. */
  .ID {min-width:50px;text-align:right;}
  .Label {min-width:200px;text-align:left;}
</style>
</header>
<body>
<?php
  if (!function_exists('mysqli_init') && !extension_loaded('mysqli')) {
    print 'mysqli not installed.'; }
  else {
    print 'mysqli installed.'; }
?>
</script>
</body>
</html>

You can test it with the following URL from the local browser:

http://localhost/mysqli_check.php

If it’s installed you can skip the next step, but if not you need to run yum in expert mode as follows (the check for php-mysql isn’t really necessary because it’s too old a version but good practice):

[root@localhost etc]# yum shell
Loaded plugins: langpacks, refresh-packagekit
> remove php-mysql
No Match for argument: php-mysql
> install php-mysqlnd
> run
--> Running transaction check
---> Package php-mysqlnd.x86_64 0:5.5.22-1.fc20 will be installed
--> Processing Dependency: php-pdo(x86-64) = 5.5.22-1.fc20 for package: php-mysqlnd-5.5.22-1.fc20.x86_64
--> Running transaction check
---> Package php-pdo.x86_64 0:5.5.22-1.fc20 will be installed
--> Finished Dependency Resolution
 
================================================================================
 Package             Arch           Version               Repository       Size
================================================================================
Installing:
 php-mysqlnd         x86_64         5.5.22-1.fc20         updates         293 k
Installing for dependencies:
 php-pdo             x86_64         5.5.22-1.fc20         updates         141 k
 
Transaction Summary
================================================================================
Install  1 Package (+1 Dependent package)
 
Total download size: 433 k
Installed size: 1.4 M
Is this ok [y/d/N]: y
Downloading packages:
(1/2): php-mysqlnd-5.5.22-1.fc20.x86_64.rpm                 | 293 kB  00:00     
(2/2): php-pdo-5.5.22-1.fc20.x86_64.rpm                     | 141 kB  00:00     
--------------------------------------------------------------------------------
Total                                              427 kB/s | 433 kB  00:01     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : php-pdo-5.5.22-1.fc20.x86_64                                 1/2 
  Installing : php-mysqlnd-5.5.22-1.fc20.x86_64                             2/2 
  Verifying  : php-pdo-5.5.22-1.fc20.x86_64                                 1/2 
  Verifying  : php-mysqlnd-5.5.22-1.fc20.x86_64                             2/2 
 
Installed:
  php-mysqlnd.x86_64 0:5.5.22-1.fc20                                            
 
Dependency Installed:
  php-pdo.x86_64 0:5.5.22-1.fc20                                                
 
Finished Transaction
> quit

You should note that this also installed PDO. One caveat, before you rerun the mysqli_check.php script from a browser, you need to restart the Apache server. You can do that as the root user with the following syntax:

apachectl restart

You can retest it with the following URL from the local browser:

http://localhost/mysqli_check.php

At this point you should have everything installed to test your connection the MySQL database. As mentioned, this example extends my instructions for installing MySQL on the Fedora instance.

The following query.php file tests your ability to connect to the MySQL database with the mysqli driver, and it uses the studentdb and video store example from my Oracle Database 11g and MySQL 5.6 Developer Handbook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<html>
<header>
<title>Static Query Object Sample</title>
<style type="text/css">
  /* HTML element styles. */
  table {background:white;border-style:solid;border-width:3px;border-color:black;border-collapse:collapse;}
  th {text-align:center;font-style:bold;background:lightgray;border:solid 1px gray;}
  td {border:solid 1px gray;}
 
  /* Class tag element styles. */
  .ID {min-width:50px;text-align:right;}
  .Label {min-width:200px;text-align:left;}
</style>
</header>
<body>
<?php
  // Assign credentials to connection.
  $mysqli = new mysqli("localhost", "student", "student", "studentdb");
 
  // Check for connection error and print message.
  if ($mysqli->connect_errno) {
    print $mysqli->connect_error."<br />";
    print "Connection not established ...<br />";
  }
  else {
 
    // Declare a static query.
    $query = "SELECT au.system_user_id, au.system_user_name FROM system_user au" ;
 
    // Loop through a result set until completed.  
    do {
 
      // Attempt query and exit with failure before processing.
      if (!$stmt = $mysqli->query($query)) {
 
        // Print failure to resolve query message.
        print $mysqli->error."<br />";
        print "Failed to resolve query ...<br />";
      }     
      else {
 
        // Print the opening HTML table tag.
        print '<table><tr><th class="ID">ID</th><th class="Label">User Role Name</th></tr>';
 
        // Fetch a row for processing.
        while( $row = $stmt->fetch_row() ) {
 
          // Print the opening HTML row tag.
          print "<tr>";
 
          // Loop through the row's columns.
          for ($i = 0;$i < $mysqli->field_count;$i++) {
            // Handle column one differently.
            if ($i == 0)
              print '<td class="ID">'.$row[$i]."</td>";
            else
              print '<td class="Label">'.$row[$i]."</td>";
          }
          // Print the closing HTML row tag.
          print "</tr>"; 
        }
      }
    } while( $mysqli->next_result());
 
  // Print the closing HTML table tag.
  print "</table>"; 
 
  // Release connection resource.
  $mysqli->close(); }
?>
</script>
</body>
</html>

This should display the following in the browser:

FedoraConfigMySQLPHP

You can see how to open port 80 for the Apache server in this blog post. If you want to work with blob data types, you’ll also need to use yum to install the php-gd library. You can read my LAMP php-gd library blog post to learn how to install the libraries. As always, I hope a step-by-step approach without assumptions helps those learning MySQL.

Written by maclochlainn

March 28th, 2015 at 7:41 pm

Lowercase Table Names

with 6 comments

A student posed the question about why table names are case sensitive. That’s because case sensitive table names are the default installation, as qualified in the MySQL documentation. You can verify that with the following query:

SELECT CASE
         WHEN @@lower_case_table_names = 1 THEN
           'Case insensitive tables'
         ELSE
           'Case sensitive tables.'
         END AS "Table Name Status";

The default value returned on Linux is:

+------------------------+
| Table Name Status      |
+------------------------+
| Case sensitive tables. |
+------------------------+
1 row in set (0.00 sec)

The default value for the lower_case_table_names value on the Windows OS is 1 not 0 because you can inadvertently create a lowercase and case sensitive table when you write an INSERT statement and use a lowercase table name. I’ve provided that detail in a reply comment to this blog post.

You can change the default by adding the following parameter in the my.cnf file on Linux or the my.ini file on Windows:

# Make all tables case insensitive.
lower_case_table_names=1

This lets you enter tables in upper or mixed case, and stores them in the data catalog as lowercase table names.

Written by maclochlainn

March 22nd, 2015 at 11:53 am

PostgreSQL Composites

without comments

PostgreSQL like Oracle supports record data types but unlike Oracle, PostgreSQL doesn’t support collections of record data types. Here’s an example of how to define a PostgreSQL composite data type, and how to use it as a column’s data type.

CREATE TYPE address_type AS
( street_address  VARCHAR
, city            VARCHAR
, state           VARCHAR
, zip_code        VARCHAR );

Then, you define an ADDRESS table, like:

CREATE TABLE address
( address_id      SERIAL
, address_struct  ADDRESS_TYPE );

You can now insert rows like:

-- Insert the first row.
INSERT INTO address
( address_struct )
VALUES
(('52 Hubble Street','Lexington','KY','40511-1225'));
 
-- Insert the second row.
INSERT INTO address
( address_struct )
VALUES
(('54 Hubble Street','Lexington','KY','40511-1225'));

Then, you can query them like this:

SELECT * FROM address;

It returns:

 address_id |                address_struct
------------+----------------------------------------------
          1 | ("52 Hubble Street",Lexington,KY,40511-1225)
          2 | ("54 Hubble Street",Lexington,KY,40511-1225)
(2 rows)

You must use parentheses around the ADDRESS_STRUCT column to query individual items, like:

SELECT   address_id
,       (address_struct).street_address
,       (address_struct).city
,       (address_struct).state
,       (address_struct).zip_code
FROM     address;

It returns output like a table:

 address_id |  street_address  |   city    | state |  zip_code
------------+------------------+-----------+-------+------------
          1 | 52 Hubble Street | Lexington | KY    | 40511-1225
          2 | 54 Hubble Street | Lexington | KY    | 40511-1225
(2 rows)

While you can define a table that holds an array of a composite type, there’s no syntax that appears to work with an array of a composite type. I hope this helps those interested in implementing record structures in PostgreSQL.

Written by maclochlainn

March 16th, 2015 at 2:02 am

PostgreSQL Auto IDs

with 2 comments

PostgreSQL’s approach to automatic numbering is as simple as Oracle but different than MySQL, and Microsoft SQL Server. For example, you have a two-step process with Oracle, PostgreSQL, MySQL, and Microsoft SQL Server. First, you create an Oracle table with the GENERATED AS IDENTITY clause, a PostgreSQL table with the SERIAL data type, a MySQL table with the AUTO_INCREMENT clause, and a Microsoft SQL Server table with the IDENTITY(1,1) clause. Then, you need to write an INSERT statement for Oracle, MySQL, or Microsoft SQL Server like:

  1. Oracle’s INSERT statement excludes the auto-incrementing column from the list of columns or provides a NULL value in the VALUES-list. You can then assign the RETURNING INTO result from an INSERT statement to a session-level (bind) variable.
  2. MySQL’s INSERT statement excludes the auto-incrementing column from the list of columns or provides a NULL value in the VALUES-list. You can then assign the LAST_INSERT_ID() function value to a session-level variable, and populate a foreign key column.
  3. Microsoft SQL Server’s INSERT statement excludes the auto-incrementing column from the list of columns or provides a NULL value in the VALUES-list. You can then assign the SCOPE_IDENTITY() function’s value to a session-level variable, and populate a foreign key column.

PostgreSQL differs because it works differently between the SQL and PL/pgSQL contexts. Let’s look at how you link the insert of data into two tables in both contexts.

The following PostgreSQL syntax creates an ADDRESS table with an auto incrementing ADDRESS_ID column that uses a SERIAL data type, which acts like an auto numbering column:

/* Create a customer table. */
CREATE TABLE customer
( customer_id     SERIAL CONSTRAINT customer_pk PRIMARY KEY
, first_name      VARCHAR(20)
, last_name       VARCHAR(20));
 
/* Create an address table. */
CREATE TABLE address
( address_id      SERIAL CONSTRAINT address_pk PRIMARY KEY
, customer_id     INTEGER
, street_address  VARCHAR(40)
, city            VARCHAR(30)
, state           VARCHAR(8)
, zip_code        VARCHAR(10));

If you want to insert one row into the CUSTOMER table and a related row in the ADDRESS table. You have two possible approaches. One works in both the SQL and PL/pgSQL contexts. That mechanism requires you to use a scalar subquery to capture the foreign key value of the CUSTOMER_ID column in the ADDRESS table, like this:

/* Insert into customer table. */
INSERT INTO customer
( first_name, last_name ) 
VALUES
('F. Scott','Fitzgerald');
 
/* Insert into address table. */
INSERT INTO address
( customer_id
, street_address
, city
, state
, zip_code )
VALUES
((SELECT   customer_id
  FROM     customer
  WHERE    first_name = 'F. Scott'
  AND      last_name = 'Fitzgerald')
,'599 Summit Avenue'
,'St. Paul'
,'Minnesota'
,'55102');

The RETURNING INTO clause of PostgreSQL only works in a PL/pgSQL context, like this:

DO $$
DECLARE lv_customer_id INTEGER;
BEGIN
  /* Insert into customer table. */
  INSERT INTO customer
  ( first_name, last_name ) 
  VALUES
  ('Madeleine','Smith') 
  RETURNING customer_id INTO lv_customer_id;
 
  /* Insert into address table. */
  INSERT INTO address
  ( customer_id
  , street_address
  , city
  , state
  , zip_code )
  VALUES
  ( lv_customer_id
  ,'7 Blythswood Square'
  ,'Glasgow'
  ,'Scotland'
  ,'G2 4BG');
 
  /* Manage any exceptions. */
  EXCEPTION
    WHEN OTHERS THEN
      RAISE NOTICE '% %', SQLERRM, SQLSTATE;
END$$;

You query the auto generated values and data from the INSERT statement to the CUSTOMER table with a scalar subquery against the natural key (the FIRST_NAME and LAST_NAME columns) from the ADDRESS table. The following is an example of such a query:

SELECT   *
FROM     customer c INNER JOIN address a
ON       c.customer_id = a.customer_id;

It returns:

 customer_id | first_name | last_name  | address_id | customer_id |   street_address    |   city   |   state   | zip_code
-------------+------------+------------+------------+-------------+---------------------+----------+-----------+----------
           1 | F. Scott   | Fitzgerald |          1 |           1 | 599 Summit Avenue   | St. Paul | Minnesota | 55102
           2 | Madeleine  | Smith      |          2 |           2 | 7 Blythswood Square | Glasgow  | Scotland  | G2 4BG
(2 rows)

My take is that the RETURNING column_value INTO local_value clause is a better approach than using Oracle’s .NEXTVAL and .CURRVAL values. I also think the RETURNING INTO clause is a better approach than using MySQL’s LAST_INSERT_ID() or Microsoft SQL Server’s SCOPE_IDENTITY().

Initially, I felt it was odd that the PostgreSQL disallows the RETURNING INTO clause in a SQL context, because it allows the syntax in a PL/pgSQL context. After some reflection the choice makes more sense because most developers work within a procedural context when they use transactions across two or more tables. PL/pgSQL is PostgreSQL’s procedural context from managing transactions across two or more tables.

As always, I hope this helps.

Written by maclochlainn

March 15th, 2015 at 9:37 pm

i-Blazon Prime

without comments

I replaced my OtterBox Defender with an i-Blazon Prime case when upgrading to the iPhone 6. It appeared a good option, and for 4 months it was fine. Yesterday, the latch broke and my iPhone fell to the ground as a walked. Fortunately, my iPhone landed flat, the i-Blazon plastic case absorbed the impact, and my iPhone was undamaged.

Here’s a photograph of the damaged case:

i-BlasonAnnotated

While I wanted to order only the belt portion of the case, that’s not possible like it is with the OtterBox Defender. So, I ordered a new i-Blazon. My plan is to monitor the belt case. If it cracks again, I’ll protect the telephone and I’ll know to move back to the OtterBox Defender.

Written by maclochlainn

February 25th, 2015 at 12:54 am

Posted in iBlazon,iPhone,iPhone6

Tagged with

SQL Server XQuery

with 2 comments

I promised my students an example of writing xquery statements in Microsoft SQL Server. This post builds on two earlier posts. The first qualifies how to build a marvel table with source data, and the second qualifies how you can create an XML Schema Collection and insert relational data into an XML structure.

You can query a sequence with xquery as follows:

DECLARE @x xml;
SET @x = N'';
SELECT @x.query('(1,2,(10,11,12,13,14,15)),-6');

It returns:

1 2 10 11 12 13 14 15 -6

You can query a sequence with an xquery FLOWR statement. FLOWR stands for: FOR, LET, ORDER BY, WHERE, and RETURN. A sample xquery with a FLOWER statement is:

DECLARE @x xml;
SET @x = N'';
SELECT @x.query('for $i in ((1,2,(10,11,12,13,14,15)),-6)
                 order by $i
                 return $i');

It returns:

-6 1 2 10 11 12 13 14 15

You can query the entire node tree with the following xquery statement because it looks for the occurrence of any node with the /* search string:

DECLARE @x xml;
SET @x = N'<marvel>
             <avenger_name>Captain America</avenger_name>
           </marvel>';
SELECT @x.query('/*');

You can query the avenger_name elements from the marvel_xml table with the following syntax:

SELECT xml_table.query('/marvel/avenger_name')
FROM   marvel_xml;

It returns the following set of avenger_name elements:

<avenger_name>Hulk</avenger_name>
<avenger_name>Iron Man</avenger_name>
<avenger_name>Black Widow</avenger_name>
<avenger_name>Thor</avenger_name>
<avenger_name>Captain America</avenger_name>
<avenger_name>Hawkeye</avenger_name>
<avenger_name>Winter Soldier</avenger_name>
<avenger_name>Iron Patriot</avenger_name>

You can query the fourth avenger_name element from the marvel_xml table with the following xquery statement:

SELECT xml_table.query('/marvel[4]/avenger_name')
FROM   marvel_xml;

It returns the following avenger_name element:

<avenger_name>Thor</avenger_name>

You can use the value() function to verify an avenger_name element exists:

SELECT CASE
          WHEN xml_table.value('(/marvel[4]/avenger_name)','nvarchar') = 'T' THEN
            'Exists'
       END AS "Verified"
FROM   marvel_xml
WHERE  id = 3;

The query returns the Exists string when it finds a valid avenger_name element. You have a number of other tools to query results sets from the XML node tree.

I hope this helps my students and anybody interested in writing xquery-enable queries.

Written by maclochlainn

February 19th, 2015 at 1:54 am

Insert into XML Column

with one comment

Working through Chapter 7 of the Querying Microsoft SQL Server 2012 book for Microsoft’s Exam 70-461, I found the XML examples incomplete for my students. I decided to put together a post on how to create:

  • An XML Schema Collection type.
  • A table that uses an XML Schema Collection as a column’s data type.
  • An example on how you can transfer the contents of a table into the XML Schema Collection.

This post assumes you understand the basics about XML structures. If you’re unfamiliar with XML, please note that everything within it is case sensitive unlike SQL. You raise exceptions when the case of your XML fails to match the case of your XML Schema Collection definitions. I raised the following exception by using a Marvel element tag in title case when the XML Schema Collection uses a lowercase marvel element tag:

Msg 6913, Level 16, State 1, Line 2
XML Validation: Declaration not found for element 'Marvel'. Location: /*:Marvel[1]

The basic marvel table is defined in this earlier blog post. To ensure you don’t run into conflicts with previously existing objects, you can delete the marvel table with the following syntax:

1
2
IF OBJECT_ID('studentdb.marvel_xml','U') IS NOT NULL
  DROP TABLE marvel_xml;

There is no predefined function that lets you conditionally drop the XML Schema Collection from the data base. The alternative is to query the Microsoft SQL Server data catalog for the existence of a row before dropping the XML Schema Collection, like this:

1
2
3
4
IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'MarvelXMLTable')
  DROP XML SCHEMA COLLECTION MarvelXMLTable;
ELSE
  SELECT 'Not found.';

You can now create the MarvelXMLTable XML Schema Collection with the following syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE XML SCHEMA COLLECTION MarvelXMLTable AS
N'<?xml version="1.0" encoding="UTF-16"?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes">
    <xsd:import namespace="http://schemas.microsoft.com/sqlserver/2004/sqltypes"
                schemaLocation="http://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
    <xsd:element name="Marvel">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="marvel_id" type="sqltypes:int" />
          <xsd:element name="avenger_name" type="sqltypes:nvarchar" />
          <xsd:element name="first_name" type="sqltypes:nvarchar" />
          <xsd:element name="last_name" type="sqltypes:nvarchar" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>';

After creating the XML Schema Collection, you can confirm whether it works correctly with the following statements. It will fail when you use semicolons on the DECLARE or SET lines, so avoid them as shown below:

1
2
3
4
5
6
7
8
DECLARE @marvel AS XML(MarvelXMLTable)
SET @marvel = '<marvel>
                 <marvel_id>1</marvel_id>
                 <avenger_name>Falcon</avenger_name>
                 <first_name>Sam</first_name>
                 <last_name>Wilson</last_name>
               </marvel>'
SELECT @marvel;

After creating and verifying the integrity of the XML Schema Collection, you can create a marvel_xml table. The xml_table column of the marvel_xml table uses a strongly-typed XML type, as shown:

1
2
3
CREATE TABLE marvel_xml
( id        INT IDENTITY(1,1) CONSTRAINT marvel_xml_pk PRIMARY KEY
, xml_table XML(MarvelXMLTable));

You can write an INSERT statement with single element like this:

1
2
3
4
5
6
7
8
9
INSERT INTO marvel_xml 
( xml_table )
VALUES
('<Marvel>
    <marvel_id>9</marvel_id>
    <avenger_name>Falcon</avenger_name>
    <first_name>Sam</first_name>
    <last_name>Wilson</last_name>
  </Marvel>');

You can insert two elements with an INSERT statement like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INSERT INTO marvel_xml
( xml_table )
VALUES
('<Marvel>
    <marvel_id>9</marvel_id>
    <avenger_name>Falcon</avenger_name>
    <first_name>Sam</first_name>
    <last_name>Wilson</last_name>
  </Marvel>
  <Marvel>
    <marvel_id>10</marvel_id>
    <avenger_name>Scarlet Witch</avenger_name>
    <first_name>Wanda</first_name>
    <last_name>Witch</last_name>
  </Marvel>');

There’s a lot of typing to insert XML literal values. It a lot easier to transfer relational data from a table to an XML type. Unfortunately, Microsoft didn’t make it very easy. The FOR XML AUTO doesn’t work for two reasons. First, the FOR XML AUTO clause doesn’t render the correct XML structure. Second, the INSERT statement disallows nested SELECT queries that include the FOR XML AUTO clause.

Here’s the way you insert relational data into an XML type column:

1
2
3
4
5
6
7
8
9
10
11
12
13
DECLARE @xml_elements NVARCHAR(MAX);
SET @xml_elements = N'';
WITH x AS
(SELECT CONCAT(N'<marvel>'
              ,N'<marvel_id>',marvel_id,N'</marvel_id>'
              ,N'<avenger_name>',avenger_name,N'</avenger_name>'
              ,N'<first_name>',first_name,N'</first_name>'
              ,N'<last_name>',last_name,N'</last_name>'
              ,N'</marvel>') AS element
 FROM   marvel)
SELECT @xml_elements += element FROM x;
INSERT INTO marvel_xml (xml_table)
VALUES (@xml_elements);

Line 1 declares an xml_element session variable. Line 2 initializes the xml_element session variable as an empty string. The WITH clause on lines 3 through 10 creates a Common Table Expression (CTE) with the valid XML structure for all rows in the marvel table. The nested SELECT statement on line 11 returns data from the CTE and adds returned row to the session-level string variable. Finally, the INSERT statement on lines 12 and 13 inserts the XML table into the xml_table column.

You can then query the table with the following statement:

SELECT xml_table
FROM   marvel_xml;

Click on the return type in the SQL Server Management Studio (SSMS), and the XML structure will expand to show this:

<marvel>
  <marvel_id>1</marvel_id>
  <avenger_name>Hulk</avenger_name>
  <first_name>Bruce</first_name>
  <last_name>Banner</last_name>
</marvel>
<marvel>
  <marvel_id>2</marvel_id>
  <avenger_name>Iron Man</avenger_name>
  <first_name>Tony</first_name>
  <last_name>Stark</last_name>
</marvel>
<marvel>
  <marvel_id>3</marvel_id>
  <avenger_name>Black Widow</avenger_name>
  <first_name>Natasha</first_name>
  <last_name>Romanoff</last_name>
</marvel>
<marvel>
  <marvel_id>4</marvel_id>
  <avenger_name>Thor</avenger_name>
  <first_name>Thor</first_name>
  <last_name>Odinsson</last_name>
</marvel>
<marvel>
  <marvel_id>5</marvel_id>
  <avenger_name>Captain America</avenger_name>
  <first_name>Steve</first_name>
  <last_name>Rogers</last_name>
</marvel>
<marvel>
  <marvel_id>6</marvel_id>
  <avenger_name>Hawkeye</avenger_name>
  <first_name>Clint</first_name>
  <last_name>Barton</last_name>
</marvel>
<marvel>
  <marvel_id>7</marvel_id>
  <avenger_name>Winter Soldier</avenger_name>
  <first_name>Bucky</first_name>
  <last_name>Barnes</last_name>
</marvel>
<marvel>
  <marvel_id>8</marvel_id>
  <avenger_name>Iron Patriot</avenger_name>
  <first_name>James</first_name>
  <last_name>Rhodey</last_name>
</marvel>

As always, I hope this post benefits those who read it.

Written by maclochlainn

February 18th, 2015 at 2:22 am