MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

Archive for the ‘Linux’ Category

Ruby+MySQL on Ubuntu

without comments

This post goes through installing and configuring Ruby and Ruby on Rails for MySQL. The first step requires updating the Ubuntu OS:

sudo apt-get update

Interestingly, I found that the man-db service had inadvertently stopped. It raised the following error:

E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.

You run this command to find the problem with the dpkg utility:

sudo dpkg --configure -a

It returned:

Setting up man-db (2.10.2-1) ...
Updating database of manual pages ...
man-db.service is a disabled or a static unit not running, not starting it.

The following command started the man-db service:

sudo systemctl start man-db.service

Next, you install the prerequisite packages with this command:

sudo apt-get install -y git-core zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev

Use the cd command to change to the student home directory. Clone the asdf as the multiple runtime version manager with this command:

git clone https://github.com/excid3/asdf.git ~/.asdf

The following is the output of the git clone command:

Cloning into '/home/student/.asdf'...
remote: Enumerating objects: 8756, done.
remote: Counting objects: 100% (829/829), done.
remote: Compressing objects: 100% (476/476), done.
remote: Total 8756 (delta 428), reused 657 (delta 334), pack-reused 7927
Receiving objects: 100% (8756/8756), 3.10 MiB | 4.29 MiB/s, done.
Resolving deltas: 100% (5148/5148), done.

Next, you fix your .bashrc file by adding the following components:

echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.bashrc
echo 'legacy_version_file = yes' >> ~/.asdfrc
echo 'export EDITOR="code --wait"' >> ~/.bashrc

Source the modifies shell, which you can do like this:

exec $SHELL

or, like:

. ${HOME}/.bashrc

Add the following asdf plug-ins:

asdf plugin add ruby
asdf plugin add nodejs

Install Ruby with the following command:

asdf install ruby 3.3.0

Install Ruby Global with this syntax:

asdf global ruby 3.3.0

Update the Ruby Gems with this command:

gem update --system

You can confirm your Ruby install with two commands. First, use the which utility to check the Ruby install:

which -a ruby

It should return:

/home/student/.asdf/shims/ruby

Then, check the Ruby version:

ruby -v

It should return:

ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]

Assuming you’ve installed and configured MySQL 8 on Ubuntu, you need this additional library to support the necessary Ruby Gem:

sudo apt-get install -y libmysqlclient-dev

Now, you can install the current MySQL Ruby Gem:

gem install mysql2

You can now write a mysql_connection.rb program to verify a connection to the MySQL 8 database, like:

# Include Ruby Gem libraries.
require 'rubygems'
require 'mysql2'
 
begin
  # Create new database connection.
  db = Mysql2::Client.new( :host     => 'localhost' \
                         , :username => 'student'   \
                         , :password => 'student'   \
                         , :database => 'studentdb')
 
  # Create a result set.
  stmt = db.query('SELECT version() AS version')
 
  # Read through the result set hash.
  stmt.each do | row |
    puts "#{row['version']}"
  end
  # Release the result set resources.
  stmt.free
 
rescue Mysql2::Error => e
  # Print the error.
  puts "ERROR #{e.errno} (#{e.sqlstate}): #{e.error}"
  puts "Can't connect to the MySQL database specified."
  # Signal an error.
  exit 1
 
ensure
  # Close the connection when it is open.
  db.close if db
end

Call the program with this syntax:

ruby mysql_connection.rb

It should return:

Connected to the MySQL database server.

You can verify the version with this mysql_version.rb program:

# Include Ruby Gem libraries.
require 'rubygems'
require 'mysql2'
 
begin
  # Create new database connection.
  db = Mysql2::Client.new( :host     => 'localhost' \
                         , :username => 'student'   \
                         , :password => 'student'   \
                         , :database => 'studentdb')
 
  # Create a result set.
  rs = db.query('SELECT version() AS version')
 
  # Read through the result set hash.
  rs.each do | row |
    puts "#{row['version']}"
  end
 
  # Release the result set resources.
  rs.free
 
rescue Mysql2::Error => e
  # Print the error.
  puts "ERROR #{e.errno} (#{e.sqlstate}): #{e.error}"
  puts "Can't connect to the MySQL database specified."
  # Signal an error.
  exit 1
 
ensure
  # Close the connection when it is open.
  db.close if db
end

On Ubuntu, it should return:

8.0.35-0ubuntu0.22.04.1

If you don’t know anything about the mysql2 Ruby Gem, you should read the documentation. It’s very concise and requires a basic understanding of Ruby programming. The two specific pages who may want to check for the next examples are:

The mysql_version.rb version uses the known string literal for columns or column aliases returned by the SQL statement, which becomes the stmt (or statement) in the program. The next program eliminates the need to enumerate with the text-based columns from the query by using the Statement#fields array values by use of a numeric index. The numeric index returns the field names from the Statement#fields class to use in as the name for values in the Result#fields value found in the row variable of the for loop.

# Include Ruby Gem libraries.
require 'rubygems'
require 'mysql2'
 
# Begin block.
begin
  # Create a new connection resource.
  db = Mysql2::Client.new( :host     => 'localhost' \
                         , :username => 'student'   \
                         , :password => 'student'   \
                         , :database => 'studentdb')
 
  # Create a result set.
  stmt = db.query("SELECT   DISTINCT i.item_title, ra.rating " +       \
                  "FROM     item i INNER JOIN rating_agency ra " +     \
                  "ON       i.item_rating_id = ra.rating_agency_id " + \
                  "WHERE    ra.rating_agency = 'MPAA'" +               \
                  "ORDER BY 1")
 
  # Read through the result set hash.
  stmt.each do | row |
    out = ""
    i = 0
    while i < stmt.fields.count()
      # Check when not last column and use the:
      #   - Hash returned by the result set for the value, and
      #   - String array value returned by the statement object
      #     as the name value of the hash by leveraging its 
      #     numeric index.
      if i < stmt.fields.count() - 1
        out += "#{row[stmt.fields[i]]}"
        out += ", "
      else
        out += "#{row[stmt.fields[i]]}"
      end
      i += 1
    end
    puts "#{out}"
  end
 
  # Release the result set resources.
  stmt.free
 
rescue Mysql2::Error => e
  # Print the error.
  puts "ERROR #{e.errno} (#{e.sqlstate}): #{e.error}"
  puts "Can't connect to MySQL database specified."
  # Signal an error.
  exit 1
 
ensure
  # Close the connection when it is open.
  db.close if db
end

It returns the select two columns from the query:

A Man for All Seasons, G
Around the World in 80 Days, G
Beau Geste, PG
Brave Heart, R
Camelot, G
Casino Royale, PG-13
...
Tomorrow Never Dies, PG-13
Tora! Tora! Tora!, G
Tron, PG

The following mysql_query_params.rb Ruby example accepts a single argument to leverage a wild card query in MySQL:

require 'rubygems'
require 'mysql2'
 
# Input external arguments.
arguments = ARGV
 
# Check for one input parameter and substitute an empty string
# when one isn't found.
if arguments.length == 1
  argument = arguments[0]
else
  argument = ""
end
 
# Begin block.
begin
  # Create a new connection resource.
  db = Mysql2::Client.new( :host     => 'localhost' \
                         , :username => 'student'   \
                         , :password => 'student'   \
                         , :database => 'studentdb')
 
  # Create a result set.
  stmt = db.prepare("SELECT   DISTINCT i.item_title, ra.rating " +       \
                    "FROM     item i INNER JOIN rating_agency ra " +     \
                    "ON       i.item_rating_id = ra.rating_agency_id " + \
                    "WHERE    ra.rating_agency = 'MPAA'" +               \
                    "AND      i.item_title LIKE CONCAT(?,'%')" +         \
                    "ORDER BY 1")
 
  # Bind the variable into the query.
  rs = stmt.execute(argument)
 
  # Read through the result set hash.
  rs.each do | row |
    out = ""
    i = 0
    while i < rs.fields.count()
      # Check when not last column and use the:
      #   - Hash returned by the result set for the value, and
      #   - String array value returned by the statement object
      #     as the name value of the hash by leveraging its 
      #     numeric index.
      if i < rs.fields.count() - 1
        out += "#{row[rs.fields[i]]}"
        out += ", "
      else
        out += "#{row[rs.fields[i]]}"
      end
      i += 1
    end
    puts "#{out}"
  end
 
  # Release the result set resources.
  rs.free
 
rescue Mysql2::Error => e
  # Print the error.
  puts "ERROR #{e.errno} (#{e.sqlstate}): #{e.error}"
  puts "Can't connect to MySQL database specified."
  # Signal an error.
  exit 1
 
ensure
  # Close the connection when it is open.
  db.close if db
end

If you call the mysql_query_params.rb program with this syntax:

ruby mysql_aquery_params.rb Harry

It’ll return the following from the studentdb database:

Harry Potter and the Chamber of Secrets, PG
Harry Potter and the Deathly Hallows, Part 1, PG-13
Harry Potter and the Deathly Hallows, Part 2, PG-13
Harry Potter and the Goblet of Fire, PG-13
Harry Potter and the Half Blood Prince, PG
Harry Potter and the Order of the Phoenix, PG-13
Harry Potter and the Prisoner of Azkaban, PG
Harry Potter and the Sorcerer's Stone, PG

After that, you should install Rails (check for current version beyond 1/2024). Install Ruby Global with this syntax:

gem install rails -v 7.1.3

Check the version installed:

rails -v

It should return:

Rails 7.1.3

Run this command to enable Rails for MySQL 8:

rails new myapp -d mysql

If you want to configure a username and password for MySQL, edit the config/database.yml file.

As always, I hope this helps somebody looking for step-by-step guide.

Written by maclochlainn

February 3rd, 2024 at 4:57 pm

VSCode & $PYTHONPATH

without comments

About 4 years ago, I demonstrated how to develop Python functions with a relative src directory in this old blog post. I thought it might be possible to do with VSCode. Doing a bit of research, it appeared all that was required was adding the PythonPath to VSCode’s Python settings in:

/home/student/.vscode/extensions/ms-python.python-2023.22.0/pythonFiles/.vscode/settings.json

It contained:

{"files.exclude":{"**/__pycache__/**":true,"**/**/*.pyc":true},"python.formatting.provider":"black"}

I added a configuration for the PYTHONPATH, as shown:

{"files.exclude":{"**/__pycache__/**":true,"**/**/*.pyc":true},"python.formatting.provider":"black","python.pythonPath": "/home/student/Lib"}

As you can tell from the embedded VSCode Terminal output below, the PYTHONPATH is not found. You can manually enter it and retest your code successfully. There is no way to use a relative PYTHONPATH like the one you can use from an shell environment file.

This is the hello_whom5.py code:

#!/usr/bin/python
 
# Import the basic sys library.
import sys
from input import parse_input
 
# Assign command-line argument list to variable.
whom = parse_input(sys.argv)
 
# Check if string isn't empty and use dynamic input.  
if len(whom) > 0:
 
  # Print dynamic hello salutation.
  print("Hello " + whom + "!\n")
 
else:
 
  # Print default saluation.
  print("Hello World!")

This is the input.py library module:

# Parse a list and return a whitespace delimited string.
def parse_input(input_list):
 
  # Assign command-line argument list to variable.
  cmd_list = input_list[1:]
 
  # Declare return variable.
  result = ""
 
  # Check whether or not their are parameters beyond the file name.
  if isinstance(input_list,list) and len(input_list) != 0:
 
    # Loop through the command-line argument list and print it. 
    for element in cmd_list:
      if len(result) == 0:
        result = element
      else:
        result = result + " " + element
 
    # Return result variable as string.
    return result

This is the Terminal output from VSCode:

student@student-virtual-machine:~$ /bin/python /home/student/Code/python/hello_whom5.py
Traceback (most recent call last):
  File "/home/student/Code/python/hello_whom5.py", line 5, in <module>
    from input import parse_input
ModuleNotFoundError: No module named 'input'
student@student-virtual-machine:~$ export set PYTHONPATH=/home/student/Lib
student@student-virtual-machine:~$ /bin/python /home/student/Code/python/hello_whom5.py
Hello World!
student@student-virtual-machine:~$ /bin/python /home/student/Code/python/hello_whom5.py Katniss Everdeen
Hello Katniss Everdeen!
 
student@student-virtual-machine:~$

The VSCode image for the test follows below:


As always, I hope this helps somebody working the same issue. However, if somebody has a better solution, please let me know.

Written by maclochlainn

January 14th, 2024 at 11:17 pm

Ubuntu, R, RScript & RStudio

without comments

Installed R, Rscript, and RStudio on my student Ubuntu instance. You use the following command to install R a

sudo apt install -y r-base-core

Then, you can check the version with the following command:

R --version

It should return:

R version 4.1.2 (2021-11-01) -- "Bird Hippie"
Copyright (C) 2021 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
 
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.

You also can run the interactive R environment by simply typing “R” at the command-line interface (CLI). It will display the following after entering the environment, quitting the environment, and discarding the workspace:

R version 4.1.2 (2021-11-01) -- "Bird Hippie"
Copyright (C) 2021 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
 
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
 
  Natural language support but running in an English locale
 
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
 
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.
 
> q()
Save workspace image? [y/n/c]: n

You can write and run a hello.r script file as follows in a Linux environment:

#!/usr/bin/Rscript
 
# Print a string.
print("Hello World!")

It prints what you would expect:

[1] "Hello World!"

The RStudio comes in two versions. One is Free and the other costs money. These are not hosted in the Ubuntu repository, and you must download them manually to apply them. You can go to RStudio web site or run the following command to download RStudio Free edition:

wget https://download1.rstudio.org/electron/jammy/amd64/rstudio-2023.12.0-369-amd64.deb

After downloading the package, you can’t quite install RStudio until you install two likely uninstalled dependencies, which are:

libclang-dev
libclang-14-dev
libclang1-14
libclang-common-14-dev
lib32gcc-s1
lib32stdc++6
libc6-i386
libobjc4
libobjc-11-dev  
libssl-dev

Therefore, the prestep is:

sudo apt install -y libssl-dev libclang-dev libclang-14-dev libobjc-11-dev libclang1-14 libclang-common-14-dev lib32gcc-s1 lib32stdc++6 libc6-i386 libobjc4

Then, you can install RStudio with this command from the directory where you downloaded it:

sudo dpkg -i rstudio-2023.12.0-369-amd64.deb

After a successful installation, you can launch RStudio with the following command:

rstudio

You will get the following console:

As always, I hope this helps those trying to do something that should be simple but isn’t quite simple.

Written by maclochlainn

January 10th, 2024 at 12:43 am

Posted in Linux,R,Ubuntu

Tagged with

Ubuntu, Perl & MySQL

without comments

Configuring Perl to work with MySQL is straight forward. While Perl is installed generally, you may need to install the libdbd-mysql-perl library.

You install it as a sudoer user with this syntax:

sudo apt install -y libdbd-mysql-perl

You can find the Perl version with the following version.pl program:

1
2
3
4
#!/usr/bin/perl -w
 
# Print the version.
print "Perl ".$]."\n";

The first line lets you call the program without prefacing the program name with perl. The first line invokes a subshell of perl by default. You just need to ensure the file has read and execute privileges to run by using the

chmod 755 version.pl

You call it with this:

./version.pl

It prints:

Perl 5.034000

The following static_query.pl Perl program uses the Perl DBI library to query and return a data set based on a static query.

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
#!/usr/bin/perl -w
 
# Use the DBI library.
use DBI;
use strict;
use warnings;
 
# Create a connection.
my $dbh = DBI->connect("DBI:mysql:database=studentdb;host=localhost:3306"
                      ,"student","student",{'RaiseError' => 1});
 
# Create SQL statement.
my $sql = "SELECT i.item_title
           ,      ra.rating
           ,      cl.common_lookup_meaning
           FROM   item i INNER JOIN common_lookup cl
           ON     i.item_type = cl.common_lookup_id INNER JOIN rating_agency ra
           ON     i.item_rating_id = ra.rating_agency_id
           WHERE  i.item_title LIKE 'Harry%'
           AND    cl.common_lookup_type = 'BLU-RAY'";
 
# Prepare SQL statement.
my $sth = $dbh->prepare($sql);
 
# Execute statement and read result set.
$sth->execute() or die $DBI::errstr;
 
# Read through returned rows, assign elements explicitly to match SELECT-list.
while (my @row = $sth->fetchrow_array()) {
  my $item_title = $row[0];
  my $rating = $row[1];
  my $lookup_meaning = $row[2];
  print "$item_title, $rating, $lookup_meaning\n";
}
 
# Close resources.
$sth->finish();

It returns the following rows from the sample database:

Harry Potter and the Sorcerer's Stone, PG, Blu-ray
Harry Potter and the Chamber of Secrets, PG, Blu-ray
Harry Potter and the Prisoner of Azkaban, PG, Blu-ray
Harry Potter and the Goblet of Fire, PG-13, Blu-ray

The following dynamic_query.pl Perl program uses the Perl DBI library to prepare a query, bind a local variable into the query, and return a data set based on a dynamic query.

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
#!/usr/bin/perl -w
 
# Use the DBI library.
use DBI;
use strict;
use warnings;
 
# Mimic a function parameter by using a local variable.
my $item_title_in = 'Star';
 
# Create a connection.
my $dbh = DBI->connect("DBI:mysql:database=studentdb;host=localhost:3306"
                      ,"student","student",{'RaiseError' => 1});
 
# Create SQL statement.
my $sql = "SELECT i.item_title
           ,      ra.rating
           ,      cl.common_lookup_meaning
           FROM   item i INNER JOIN common_lookup cl
           ON     i.item_type = cl.common_lookup_id INNER JOIN rating_agency ra
           ON     i.item_rating_id = ra.rating_agency_id
           WHERE  i.item_title LIKE CONCAT(?,'%')
           AND    cl.common_lookup_type = 'BLU-RAY'";
 
# Prepare SQL statement.
my $sth = $dbh->prepare($sql);
 
# Bind a variable to first parameter in the query string.
$sth->bind_param(1, $item_title_in);
 
# Execute statement and read result set.
$sth->execute() or die $DBI::errstr;
 
# Read through returned rows, assign elements explicitly to match SELECT-list.
while (my @row = $sth->fetchrow_array()) {
  my $item_title = $row[0];
  my $rating = $row[1];
  my $lookup_meaning = $row[2];
  print "$item_title, $rating, $lookup_meaning\n";
}
 
# Close resources.
$sth->finish();

It returns the following rows from the sample database:

Star Wars II, PG, Blu-ray

You can replace lines 34 through 40 with the following to read any number of columns into a comma-delimited row return:

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Read through returned rows, assign elements explicitly to match SELECT-list.
while (my @row = $sth->fetchrow_array()) {
 
  # Read through a dynamic column list for column separated display.
  my $result = '';
  foreach(@row) {
    if (length($result) == 0) {
      $result = $_; }
    else {
      $result .= ", " . $_; }
  } 
 
  # Print comma-separted values by row.
  print $result . "\n"
}

It returns the following rows from the sample database:

Star Wars II, PG, Blu-ray

As always, I hope this helps the reader solve a problem.

Written by maclochlainn

January 8th, 2024 at 10:37 pm

Native sqlplus editing

without comments

I have to remind myself from time to time that Ubuntu is a Desktop or Workstation and by default can go missing key server software, like ssh. This became evident when I wanted to check whether I could run sqlplus from my Mac OS terminal through my Ubuntu VM and internally embedded Oracle Database 23c Free docker instance.

If like me you forgot to add it, you can add the ssh service with the following commands to your Ubuntu VM:

sudo apt update
sudo apt install -y openssh-server
sudo systemctl start ssh.service

Then, you can test the installation with an ssh call to localhost, like:

ssh localhost

You should see the following, where you need to enter the sudoer’s password to continue. Your localhost target causes an authenticity check, like:

The authenticity of host 'localhost (127.0.0.1)' can't be established.
ED25519 key fingerprint is SHA256:js8knEf/lOE1rSss3u8lP4Ii634Y0CkUz+oJM5dt3w4.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Enter yes to continue:

Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

It will now add localhost to the list of known hosts provide standard messages, as shown below.

Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
student@localhost's password: 
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 6.2.0-39-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
 
Expanded Security Maintenance for Applications is not enabled.
 
9 updates can be applied immediately.
5 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
 
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
 
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Having verified the installation and functionality of ssh in the Ubuntu VM. Then, I launched a Terminal session on my MacBookPro base operating system. Using the Ubuntu instance ssh and a customized Bash function, I discovered its IP address.

The following is the get_ip() user-defined function in the Ubuntu instance’s student user’s customized .bashrc file:

# Return the local instance's IP address.
get_ip ()
{
  echo `hostname -I | cut -f1 -d' '`
}

In this instance, it returned:

192.168.195.155

With the IP address, I secured shelled into my Ubuntu sudoer student user like this:

ssh student@192.168.195.155

It’ll prompt you for the remote server’s student password, like:

student@192.168.195.155's password:

After entering the correct password, I got the standard reply of a valid connection:

Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 6.2.0-39-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
 
Expanded Security Maintenance for Applications is not enabled.
 
9 updates can be applied immediately.
5 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
 
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
 
Last login: Fri Jan  5 18:13:21 2024 from 127.0.0.1

Next, I connected to the Ubuntu Docker Oracle Database 23c Free instance with this syntax:

docker exec -it --user student oracle23c bash

At the prompt for the Docker instance of Oracle Database 23c Free, you can type sqlplus to work directly against the Oracle Database 23c Free instance with a pluggable c##student database user.

sqlplus c##student/student
 
SQL*Plus: Release 23.0.0.0.0 - Production on Sat Jan 6 01:38:06 2024
Version 23.3.0.23.09
 
Copyright (c) 1982, 2023, Oracle.  All rights reserved.
 
Last Successful login time: Sat Dec 23 2023 04:30:00 +00:00
 
Connected to:
Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09

Now, I can interactively edit my files with vi in the Docker Oracle Database 23c Free directory. The following demonstrates using the sandboxed student() function from my earlier Oracle 23c Free SQL*Plus blog post and connects as a sandboxed student user in the Docker Oracle 23c Free container. The image uses a different Mac OS and different Ubuntu VM from the earlier entries in this blog post from the earlier examples.

You can edit and test the files in the Docker Oracle 23c Free instance through the command-line interface (CLI). You can further automate the ssh connection by making the Ubuntu instance’s IP address a static address instead of a DCHP-assigned address; and then you can put it in the Mac OS’s /etc/hosts file which lets you resolve it by name (through file versus DNS resolution).

As always, I hope this helps those looking for a solution.

Written by maclochlainn

January 5th, 2024 at 6:26 pm

Ubuntu DaaS VM

without comments

Completed the build of my new Ubuntu Virtual Machine (VM) with Oracle 23c installed in a Docker instance, and MySQL and PostgreSQL installed locally. The VM image also provides a LAMP stack with Apache2, PHP 8.1 with MySQLi and PDO modules. Since the original post, I’ve added a number of items to support our program courses and the Data Science degrees.

There are lots of tricks and techniques in the blog associated with creating the build. I took the background photograph of Chapel Bridge early on Sunday morning August 30, 1987 in Lucerne Switzerland (with a Canon A1 and ASA 64 slide film that subsequently digitized well).

Next step: roll it out into an AWS image for the students to use for their courses.

Related blog posts:

As always, I hope the post and information helps others.

Written by maclochlainn

December 13th, 2023 at 1:15 am

Apache2 on Ubuntu

without comments

It’s always interesting when I build new instances. Ubuntu 22.0.4 was no different but I ran into an issue with installing Apache2 and eventually loading the mysqli module.

The Apache2 error was an issue with an unsupported module or hidden prerequisite. The MySQLi required an Apache reload after installation. Contrary to some erroneous posts the mysqli driver is supported on PHP 8.1.

Apache2 installation starts first and the mysqli module reload and verification script follows. On Ubuntu, you install Apache2 if you’re unaware of the hidden pre-requisite, otherwise install the pre-requisite first and avoid the error.

This is the command to install the apache2 module:

sudo apt-get install -y apache2

It generated the following error message:

apache2: Syntax error on line 146 of /etc/apache2/apache2.conf: Syntax error on line 1 of /etc/apache2/mods-enabled/wsgi.load: Cannot load /usr/lib/apache2/modules/mod_wsgi.so into server: /usr/lib/apache2/modules/mod_wsgi.so: cannot open shared object file: No such file or directory
Action 'start' failed.
The Apache error log may have more information.

Line 146 in the /etc/apache2/apache2.conf file contains the instruction to load modules. The error says it can’t find the mod_wsgi.so library, which was originally part of the deprecated Python 2.7 release.

IncludeOptional mods-enabled/*.load

The first step I pursued was finding the missing library, which appeared to be in the libapache2-mod-wsgi package. However, it became clear there is no installation candidate for that module, which supported Python 3.x. A little more research led me to find the appropriate library version for Python 3, which is found in the libapache2-mod-wsgi-py3 package.

I installed the libapache2-mod-wsgi-py3 package with the following syntax:

sudo apt-get install -y libapache2-mod-wsgi-py3

After applying it, I was able to start Apache2. Then, typing in localhost returns the Apache2 index.htm page, like:

After creating the following file in the default directory:

<?php
  phpinfo();
?>

Typing in localhost/infophp returns the Apache2 info.php page, like:

After the basics for PHP, the next step is the mysqli module for the MySQL database. This can be done in two steps on Ubuntu.

  1. Install the MySQLi software with the following syntax on Ubuntu:

    sudo apt-get install -y php8.1-mysql

    If you forget and use the old php-mysqli, it will redirect to the new PHP 8.1 MySQL module.

  2. You need to reload the Apache configuration with the following syntax:

    sudo systemctl reload apache2

Now, you can use the following PHP program to verify that the mysqli and pdo drivers are installed:

<html>
<header>
<title>Module Verification</title>
</header>
<body>
<?php
  if (!function_exists('mysqli_init') && !extension_loaded('mysqli')) {
    print 'mysqli not installed.'; }
  else {
    print 'mysqli installed.'; }
  if (!function_exists('pdo_init') && !extension_loaded('pdo')) {
    print '<p>pdo not installed.</p>'; }
  else {
    print '<p>pdo installed.</p>'; }
?>
</script>
</body>
</html>

If everything is correct, it should return the following in a browser when you query it from localhost/the-file-name and the file is in the /var/www/html directory:

mysqli installed.
 
pdo installed.

This means you can now write PHP applications, like the following example for my students:

I also have some demonstration programs that upload PNG files. As usual, I forgot about that while building the Ubuntu installation with MySQL 8, PHP 8.1 and Apache2. Fortunately, I solved it back in the day when moving from PHP 5.7 to 7.1 and here are the equivalent steps for Ubuntu:

I installed the libapache2-mod-wsgi-py3 package with the following syntax:

sudo apt-get install -y php-gd

Then, I restarted the Apache2 server to incorporate the php-gd library in my PHP module with this syntax:

sudo systemctl restart apache2.service

Retesting the PHP form to upload and render a PNG image file with this code (note that the only thing you can display is the html header and converted image, as shown on lines 64 and 65):

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
<?php
  /*  ConvertBlobToImage.php
   *  by Michael McLaughlin
   *
   *  This script queries an image from a BLOB column and
   *  converts it to a PNG image.
   *
   *  ALERT:
   *
   *  The header must be inside the PHP script tag because nothing
   *  can be rendered before the header() function call that signals
   *  this is a PNG file.
   */
 
    // Database credentials must be set manually because an include_once() function
    // call puts something ahead of the header, which causes a failure when rendering
    // an image.
 
    // Include the credential library.
    include_once("MySQLCredentials.inc");
 
  // Return successful attempt to connect to the database.
  if (!$c = @mysqli_connect(HOSTNAME,USERNAME,PASSWORD,DATABASE)) {
 
    // Print user message.
    print "Sorry! The connection to the database failed. Please try again later.";
 
    // Assign the OCI error and format double and single quotes.
    print mysqli_error();
 
    // Kill the resource.
    die();
  }
  else {
 
    // Declare input variables.
    $id = (isset($_GET['id'])) ? (int) $_GET['id'] : 1023;
 
    // Initialize a statement in the scope of the connection.
    $stmt = mysqli_stmt_init($c);
 
    // Declare a SQL SELECT statement returning a MediumBLOB.
    $sql = "SELECT item_blob FROM item WHERE item_id = ?";
 
    // Prepare statement and link it to a connection.
    if (mysqli_stmt_prepare($stmt,$sql)) {
      mysqli_stmt_bind_param($stmt,"i",$id);
 
      // Execute the PL/SQL statement.
      if (mysqli_stmt_execute($stmt)) {
 
        // Bind result to local variable.
	      mysqli_stmt_bind_result($stmt, $image);
 
        // Read result.
        mysqli_stmt_fetch($stmt);
      }
    } 
 
    // Disconnect from database.
    mysqli_close($c);
 
    // Print the header first.
    header('Content-type: image/png');
    imagepng(imagecreatefromstring($image));
  }
?>

The call to the ConvertMySQLBlobToImage.php is handled in an image tag, as shown:

<img src="ConvertMySQLBlobToImage.php?id='.$id.'">

Rendering a web page, like:

As always, I hope this explains something worthwhile.

Written by maclochlainn

December 10th, 2023 at 11:25 pm

OracleDB Python Tutorial 1

without comments

This shows you how to get Python working with the Oracle Database 23c in Docker or Podman on Ubuntu. You can find useful connection strings for this in Oracle Database Free Get Started.

  1. First step requires you to install the pip3/span> utility on Ubuntu.

    sudo apt install -y python3-pip

  2. Second step requires that you pip3 install the oracledb library:

    sudo pip3 install oracledb --upgrade

  3. Third step requires you write a Python program to test your connection to Oracle Database 23c Free, like:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    #!/usr/bin/python
     
    # Import the Oracle library.
    import oracledb
     
    try:
      # Create a connection to local Docker or Podman installation.
      db = oracledb.connect(user='c##student', password='student', dsn='localhost:51521/FREE')
     
      # Print a connection message.
      print("Connected to the Oracle", db.version, "database.")
     
    except oracledb.DatabaseError as e:
      error, = e.args
      print(sys.stderr, "Oracle-Error-Code:", error.code)
      print(sys.stderr, "Oracle-Error-Message:", error.message)
     
    finally:
      # Close connection. 
      db.close()

    The 51521 port is the recommended port when setting up Docker or Podman services, however, it can be set to any port above 1024.

    It should print:

    Connected to the Oracle 23.3.0.23.9 database.
  4. Fourth step requires you write a Python program to test querying data from an Oracle Database 23c Free instance. I created the following avenger table and seeded it with six Avengers.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    /* Conditionally drop the table. */
    DROP TABLE IF EXISTS avenger;
     
    /* Create the table. */
    CREATE TABLE avenger
    ( avenger_id      NUMBER
    , first_name      VARCHAR2(20)
    , last_name       VARCHAR2(20)
    , character_name  VARCHAR2(20));
     
    /* Seed the table with data. */
    INSERT INTO avenger VALUES (1,'Anthony','Stark','Iron Man');
    INSERT INTO avenger VALUES (2,'Thor','Odinson','God of Thunder');
    INSERT INTO avenger VALUES (3,'Steven','Rogers','Captain America');
    INSERT INTO avenger VALUES (4,'Bruce','Banner','Hulk');
    INSERT INTO avenger VALUES (5,'Clinton','Barton','Hawkeye');
    INSERT INTO avenger VALUES (6,'Natasha','Romanoff','Black Widow');

    Then, I extended the program logic to include a cursor and for loop to read the values from the avenger table:

    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
    
    #!/usr/bin/python
     
    # Import the Oracle library.
    import oracledb
     
    try:
      # Create a connection to local Docker or Podman installation.
      db = oracledb.connect(user='c##student', password='student', dsn='localhost:51521/FREE')
     
      # Create a cursor.
      cursor = db.cursor()
     
      # Execute a query.
      cursor.execute("SELECT   character_name " +
                     ",        first_name " +
                     ",        last_name " +
                     "FROM     avenger " +
                     "ORDER BY character_name")
     
      # Read the contents of the cursor.
      for row in cursor:
        print(row[0] + ':',row[2] + ',',row[1])
     
    except oracledb.DatabaseError as e:
      error, = e.args
      print(sys.stderr, "Oracle-Error-Code:", error.code)
      print(sys.stderr, "Oracle-Error-Message:", error.message)
     
    finally:
      # Close cursor and connection.
      cursor.close() 
      db.close()

    The 51521 port is the recommended port when setting up Docker or Podman services, however, it can be set to any port above 1024.

    It should print:

    Black Widow: Romanoff, Natasha
    Captain America: Rogers, Steven
    God of Thunder: Odinson, Thor
    Hawkeye: Barton, Clinton
    Hulk: Banner, Bruce
    Iron Man: Stark, Anthony
  5. Fifth step requires you write a Python program to test querying data filtered by a local variable from an Oracle Database 23c Free instance. This example looks only for the Hulk among the six Avengers.

    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
    
    #!/usr/bin/python
     
    # Import the Oracle library.
    import oracledb
     
    try:
      # Create a connection to local Docker or Podman installation.
      db = oracledb.connect(user='c##student', password='student', dsn='localhost:51521/FREE')
     
      # Create a cursor.
      cursor = db.cursor()
     
      # Execute a query.
      stmt = "SELECT   character_name "            \
             ",        first_name "                \
             ",        last_name "                 \
             "FROM     avenger "                   \
             "WHERE    character_name = :avenger " \
             "ORDER BY character_name"
     
      # Execute with bind variable.
      cursor.execute(stmt, avenger = "Hulk")
     
      # Read the contents of the cursor.
      for row in cursor:
        print(row[0] + ':',row[2] + ',',row[1])
     
    except oracledb.DatabaseError as e:
      error, = e.args
      print(sys.stderr, "Oracle-Error-Code:", error.code)
      print(sys.stderr, "Oracle-Error-Message:", error.message)
     
    finally:
      # Close cursor and connection. 
      cursor.close() 
      db.close()

    It should print:

    Hulk: Banner, Bruce

As always, I hope this puts everything together for setting up Python with Oracle Database 23c Free.

Written by maclochlainn

December 10th, 2023 at 12:27 am

pgAdmin4 on Ubuntu

without comments

Installing pgAdmin4 is qualified by the pgAdmin4 (APT) web page, which relies on the curl utility. Ubuntu doesn’t install curl by default, so you need to install it before you can download and install pgAdmin4:

  1. Download and install curl utility

    sudo apt-get install -y curl

  2. Download and install the public key for the repository (if not done previously):

    sudo curl -fsS https://www.pgadmin.org/static/packages_pgadmin_org.pub | sudo gpg --dearmor -o /usr/share/keyrings/packages-pgadmin-org.gpg

    It may find an existing key and require you to confirm its replacement:

    File '/usr/share/keyrings/packages-pgadmin-org.gpg' exists. Overwrite? (y/N) y
  3. Create the repository configuration file:

    sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/packages-pgadmin-org.gpg] https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/$(lsb_release -cs) pgadmin4 main" > /etc/apt/sources.list.d/pgadmin4.list && apt update'

  4. Install the pgadmin4 Desktop:

    sudo apt install pgadmin4-desktop

  5. You can launch your pgadmin4 program file or by clicking the pgAdmin4 icon in the other applications menu:

  6. It takes a couple moments to launch the pgadmin4 desktop. The initial screen will look like:

  7. After pgadmin4 launches, you’re prompted for a master password. Enter the password and click the OK button to proceed or simply let it sit for a couple minutes to avoid entering a password.

  8. After entering the password or skipping its entry you arrive at the base dialog, as shown.

  9. Click the Add New Server link, which prompts you to register your database. Enter videodb in the Name field and click the Connection tab to the right of the General tab.

  10. In the Connection dialog, enter the following values:

    • Host name/address: localhost
    • Port: 5432
    • Maintenance database: postgres
    • Username: student
    • Password: student

    Enter a name for your database. In this example, videodb is the Server Name. Click the Save button to proceed.

  11. Having completed the configuration, you now see the home page.

This completes the instructions for installing, configuring, and using PostgreSQL on AlmaLinux. As always, I hope it helps those looking for instructions.

Written by maclochlainn

December 7th, 2023 at 10:36 pm

Posted in Linux,Ubuntu

Python3 on PostgreSQL

without comments

The necessary Python 3 driver for connections to the PostgreSQL database is python3-psycopg2, as qualified by this earlier post with full test examples for Red Hat distributions. You can install it on Ubuntu with the following command:

sudo apt-get install -y python3-psycopg2

As always, I hope this helps those looking for a solution. Also, remember the referenced post above provides Linux distribution neutral full solutions.

Written by maclochlainn

December 3rd, 2023 at 11:00 pm