MacLochlainns Weblog

Web Name: MacLochlainns Weblog

WebSite: http://blog.mclaughlinsoftware.com

ID:33892

Keywords:

MacLochlainns,Weblog,

Description:

These are my notes for creating a small Node.js application that queries a MySQL database. The post will show you how to:Configure your Node.js development directory.Build a small application to test a MySQL connection.Build a small application that connects to the MySQL database and queries data.This blog post assumes you ve performed a global install of Node.js on a Linux server. If you re unfamiliar with how to perform a global Node.js installation, I cover how to do it in this earlier blog post.Before you write the Node.js applicaiton, you need to setup a db developer directory. Then, create a node_modules symbolic link to the /usr/local/lib/node_modules directory in the db directory. You can use the following command from the db directory:ln -s /usr/local/lib/node_modules `pwd`/node_modulesln -s /usr/local/lib/node_modules `pwd`/node_modulesAfter creating the node_modules symbolic link, you need to run the following two npm commands:npm init --ynpm install --save mysqlnpm init --ynpm install --save mysqlThe first command sets up a generic package.json file, and the second adds the mysql package and supporting packages to the package.json file. These two steps configure the Node.js side of these examples.They both require that you create the student user with a native password, like so:CREATE USER 'student'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY 'student';GRANT ALL PRIVILEGES ON studentdb.* TO 'student'@'localhost';FLUSH PRIVILEGES;CREATE USER 'student'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY 'student';GRANT ALL PRIVILEGES ON studentdb.* TO 'student'@'localhost';FLUSH PRIVILEGES;The following example shows you how to check a connection to the MySQL database:const mysql = require 'mysql' const connection = mysql.createConnection host: 'localhost', user: 'student', password: 'student', database: 'studentdb' connection.connect err = if err throw err else console.log 'Connected to MySQL Server!\n' console.log 'User configured wiht mysql_native_password.\n' ; console.log 'Press Ctrl-C to terminate ...' const mysql = require('mysql') const connection = mysql.createConnection({ host: 'localhost', user: 'student', password: 'student', database: 'studentdb' connection.connect((err) = { if (err) throw err else console.log('Connected to MySQL Server!\n') console.log('User configured wiht mysql_native_password.\n'); console.log('Press Ctrl-C to terminate ...') })You extend the previous example by adding a query component and returning the query result value to the console s log (leveraging the w3school s Node.js and MySQL tutorial example):const mysql = require 'mysql' const connection = mysql.createConnection host: 'localhost', user: 'student', password: 'student', database: 'studentdb' connection.connect err = if err throw err else console.log 'Connected to MySQL Server!\n' connection.query 'SELECT DISTINCT item_title FROM item', function err, result if err throw err else console.log result console.log 'Press Ctrl-C to terminate ...' console.log('Connected to MySQL Server!\n') connection.query('SELECT DISTINCT item_title FROM item', function (err, result) { if (err) throw err else console.log(result) console.log('Press Ctrl-C to terminate ...') })It should display the following:Connected to MySQL Server! [ RowDataPacket { item_title: 'The Hunt for Red October' }, RowDataPacket { item_title: 'Star Wars I' }, RowDataPacket { item_title: 'Star Wars II' }, RowDataPacket { item_title: 'Star Wars III' }, RowDataPacket { item_title: 'The Chronicles of Narnia' }, RowDataPacket { item_title: 'RoboCop' }, RowDataPacket { item_title: 'Pirates of the Caribbean' }, RowDataPacket { item_title: 'MarioKart' }, RowDataPacket { item_title: 'Splinter Cell' }, RowDataPacket { item_title: 'Need for Speed' }, RowDataPacket { item_title: 'The DaVinci Code' }, RowDataPacket { item_title: 'Cars' }, RowDataPacket { item_title: 'Beau Geste' }, RowDataPacket { item_title: 'I Remember Mama' }, RowDataPacket { item_title: 'Tora! Tora! Tora!' }, RowDataPacket { item_title: 'A Man for All Seasons' }, RowDataPacket { item_title: 'Hook' }, RowDataPacket { item_title: 'Around the World in 80 Days' }, RowDataPacket { item_title: 'Harry Potter and the Sorcerer\'s Stone' }, RowDataPacket { item_title: 'Camelot' }, RowDataPacket { item_title: 'Casino Royale' }, RowDataPacket { item_title: 'Die Another Day' }, RowDataPacket { item_title: 'Golden Eye' }, RowDataPacket { item_title: 'Tomorrow Never Dies' }, RowDataPacket { item_title: 'The World Is Not Enough' }, RowDataPacket { item_title: 'Brave Heart' }, RowDataPacket { item_title: 'Christmas Carol' }, RowDataPacket { item_title: 'Scrooge' }, RowDataPacket { item_title: 'Clear and Present Danger' }, RowDataPacket { item_title: 'Harry Potter and the Chamber of Secrets' }, RowDataPacket { item_title: 'Harry Potter and the Prisoner of Azkaban' }, RowDataPacket { item_title: 'Harry Potter and the Goblet of Fire' }, RowDataPacket { item_title: 'Harry Potter and the Order of the Phoenix' }, RowDataPacket { item_title: 'Harry Potter and the Half Blood Prince' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 1' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 2' }, RowDataPacket { item_title: 'Tron' }, RowDataPacket { item_title: 'The Avengers' }, RowDataPacket { item_title: 'Thor: The Dark World' } ] Press Ctrl-C to terminate ...Connected to MySQL Server! [ RowDataPacket { item_title: 'The Hunt for Red October' }, RowDataPacket { item_title: 'Star Wars I' }, RowDataPacket { item_title: 'Star Wars II' }, RowDataPacket { item_title: 'Star Wars III' }, RowDataPacket { item_title: 'The Chronicles of Narnia' }, RowDataPacket { item_title: 'RoboCop' }, RowDataPacket { item_title: 'Pirates of the Caribbean' }, RowDataPacket { item_title: 'MarioKart' }, RowDataPacket { item_title: 'Splinter Cell' }, RowDataPacket { item_title: 'Need for Speed' }, RowDataPacket { item_title: 'The DaVinci Code' }, RowDataPacket { item_title: 'Cars' }, RowDataPacket { item_title: 'Beau Geste' }, RowDataPacket { item_title: 'I Remember Mama' }, RowDataPacket { item_title: 'Tora! Tora! Tora!' }, RowDataPacket { item_title: 'A Man for All Seasons' }, RowDataPacket { item_title: 'Hook' }, RowDataPacket { item_title: 'Around the World in 80 Days' }, RowDataPacket { item_title: 'Harry Potter and the Sorcerer\'s Stone' }, RowDataPacket { item_title: 'Camelot' }, RowDataPacket { item_title: 'Casino Royale' }, RowDataPacket { item_title: 'Die Another Day' }, RowDataPacket { item_title: 'Golden Eye' }, RowDataPacket { item_title: 'Tomorrow Never Dies' }, RowDataPacket { item_title: 'The World Is Not Enough' }, RowDataPacket { item_title: 'Brave Heart' }, RowDataPacket { item_title: 'Christmas Carol' }, RowDataPacket { item_title: 'Scrooge' }, RowDataPacket { item_title: 'Clear and Present Danger' }, RowDataPacket { item_title: 'Harry Potter and the Chamber of Secrets' }, RowDataPacket { item_title: 'Harry Potter and the Prisoner of Azkaban' }, RowDataPacket { item_title: 'Harry Potter and the Goblet of Fire' }, RowDataPacket { item_title: 'Harry Potter and the Order of the Phoenix' }, RowDataPacket { item_title: 'Harry Potter and the Half Blood Prince' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 1' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 2' }, RowDataPacket { item_title: 'Tron' }, RowDataPacket { item_title: 'The Avengers' }, RowDataPacket { item_title: 'Thor: The Dark World' } ] Press Ctrl-C to terminate ...As always, I hope this helps those looking to learn I liked the example used to explore basic routing in Chapter 2 of the Web Development with Node Express book. I embellished the Node.js router example and static pages just a bit. The routing example requires you create a public subdirectory where you deploy the code and put the about.html, home.html, and 404.html files in the public subdirectory. Then, you must put a logo.png file in a tmp subdirectory inside of the public directory. The book assumes you know how to build these pages, which seems reasonable but just in case, here are the files I used to test the book s example.The about.html page: html head title About Page /title /head body p A small sample Node.js routing example. /p p img width= 25 src= /img/logo.png / nbsp; sup i MEAN Stack Development /i /sup /p /body /html html head title About Page /title /head body p A small sample Node.js routing example. /p p img width= 25 src= /img/logo.png / nbsp; sup i MEAN Stack Development /i /sup /p /body /html The home.html page: html head title Home Page /title /head body p > html head title Home Page /title /head body The 404.html page: html head title 404 Error Message /title /head body p A 404 Error Message Page. /p p img width= 25 src= /img/logo.png / nbsp; sup i MEAN Stack Development /i /sup /p /body /html html head title 404 Error Message /title /head body p A 404 Error Message Page. /p p img width= 25 src= /img/logo.png / nbsp; sup i MEAN Stack Development /i /sup /p /body /html The routing example sets the path to lowercase, which is important when you type the URL to verify physical files. For example, you need to use the routing startrekoriginal1.png string value in the URL. It will fail if you use the mixed case actual file name of the StarTrekOriginal1.png file. That s because the routing maps the lowercase string to the physical file.While the /public directory is the home directory of the virtual map as a rule, you can t use it in the URL (as explained in next Chapter 3). Also, the router uses /img as a virtual directory which maps to the physical /tmp subdirectory. When you want to validate a physical image file you need to know these two rules. They explain why the following URL lets you verify a physical image file found in the /public/tmp directory.localhost:3000/img/StarTrekOriginal1.pnglocalhost:3000/img/StarTrekOriginal1.pngyou will trigger the 404 Error page. Here s the Node.js routing code:/* Construct a web server. */const http = require 'http' const fs = require 'fs' const port = process.env.PORT || 3000/* Function uses the fs package to read files. */function serveStaticFile res, path, contentType, responseCode = 200 fs.readFile __dirname + path, err, data = if err res.writeHead 500, 'Content-Type': 'text/plain' return res.end '500 - Internal Error' res.writeHead responseCode, 'Content-Type': contentType res.end data const server = http.createServer req, res = // Normalize URL by removing query string, optional // trailing slash, and making it lowercase. const path= req.url.replace /\/?(?:\?.*)?$/,'' .toLowerCase switch path case '': serveStaticFile res, '/public/home.html', 'text/html' break case '/about': serveStaticFile res, '/public/about.html', 'text/html' break case '/img/startrekoriginal1.png': serveStaticFile res, '/public/tmp/StarTrekOriginal1.png', 'image/png' break case '/img/logo.png': serveStaticFile res, '/public/tmp/logo.png', 'image/png' break default: serveStaticFile res, '/public/404.html', 'text/html', 404 breakserver.listen port, = console.log `server started on port $ port ; ` + 'press Ctrl-C to terminate...' /* Construct a web server. */const http = require('http')const fs = require('fs')const port = process.env.PORT || 3000/* Function uses the fs package to read files. */function serveStaticFile(res, path, contentType, responseCode = 200) { fs.readFile(__dirname + path, (err, data) = { if (err) { res.writeHead(500, { 'Content-Type': 'text/plain' }) return res.end('500 - Internal Error') res.writeHead(responseCode, { 'Content-Type': contentType }) res.end(data)/* Create the Node.js server. */const server = http.createServer((req, res) = { // Normalize URL by removing query string, optional // trailing slash, and making it lowercase. const path= req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase() switch(path) { case '': serveStaticFile(res, '/public/home.html', 'text/html' ) break case '/about': serveStaticFile(res, '/public/about.html', 'text/html' ) break case '/img/startrekoriginal1.png': serveStaticFile(res, '/public/tmp/StarTrekOriginal1.png', 'image/png' ) break case '/img/logo.png': serveStaticFile(res, '/public/tmp/logo.png', 'image/png' ) break default: serveStaticFile(res, '/public/404.html', 'text/html', 404 ) breakserver.listen(port, () = console.log(`server started on port ${port}; ` + 'press Ctrl-C to terminate...'))Assuming you name the Node.js routing example helloworld3.js, you would start the router with the following command:node helloworld3.jsnode helloworld3.jsIt should start the router. Enter the following URL:http://localhost:3000http://localhost:3000You should see a page rendered like the following:As always, I hope this helps those trying to use this technology. I want to add the MEAN (MongoDB, Express.js, Angular.js, and Node.js) stack to my backend server development course. This post documents the installation and configuration of components on Fedora 30.The first step requires installing the Node package. The Node package also contains the Node package manager (npm). You install the Node packages as the root user or as a sudoer user with the following command.yum install -y npmyum install -y npmIt should produce the following installation log:Last metadata expiration check: 1:10:42 ago on Wed 08 Jul 2020 06:57:52 PM MDT.Dependencies resolved.================================================================================================================================ Package Architecture Version Repository Size================================================================================================================================Installing: npm x86_64 1:6.13.4-1.10.19.0.1.fc30 updates 3.8 MInstalling dependencies: nodejs x86_64 1:10.19.0-1.fc30 updates 88 k nodejs-libs x86_64 1:10.19.0-1.fc30 updates 9.1 MInstalling weak dependencies: nodejs-full-i18n x86_64 1:10.19.0-1.fc30 updates 7.3 MTransaction Summary================================================================================================================================Install 4 PackagesTotal download size: 20 MInstalled size: 91 MDownloading Packages: 1/4 : nodejs-10.19.0-1.fc30.x86_64.rpm 173 kB/s | 88 kB 00:00 2/4 : nodejs-full-i18n-10.19.0-1.fc30.x86_64.rpm 2.8 MB/s | 7.3 MB 00:02 3/4 : nodejs-libs-10.19.0-1.fc30.x86_64.rpm 2.7 MB/s | 9.1 MB 00:03 4/4 : npm-6.13.4-1.10.19.0.1.fc30.x86_64.rpm 1.3 MB/s | 3.8 MB 00:02 --------------------------------------------------------------------------------------------------------------------------------Total 4.9 MB/s | 20 MB 00:04 Running transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Running transaction Running scriptlet: npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 1/1 Preparing : 1/1 Installing : nodejs-libs-1:10.19.0-1.fc30.x86_64 1/4 Installing : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Installing : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 3/4 Installing : nodejs-1:10.19.0-1.fc30.x86_64 4/4 Running scriptlet: nodejs-1:10.19.0-1.fc30.x86_64 4/4 Verifying : nodejs-1:10.19.0-1.fc30.x86_64 1/4 Verifying : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Verifying : nodejs-libs-1:10.19.0-1.fc30.x86_64 3/4 Verifying : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 4/4 Installed: nodejs-1:10.19.0-1.fc30.x86_64 nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 nodejs-libs-1:10.19.0-1.fc30.x86_64 npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 Complete!Last metadata expiration check: 1:10:42 ago on Wed 08 Jul 2020 06:57:52 PM MDT.Dependencies resolved.================================================================================================================================ Package Architecture Version Repository Size================================================================================================================================Installing: npm x86_64 1:6.13.4-1.10.19.0.1.fc30 updates 3.8 MInstalling dependencies: nodejs x86_64 1:10.19.0-1.fc30 updates 88 k nodejs-libs x86_64 1:10.19.0-1.fc30 updates 9.1 MInstalling weak dependencies: nodejs-full-i18n x86_64 1:10.19.0-1.fc30 updates 7.3 MTransaction Summary================================================================================================================================Install 4 PackagesTotal download size: 20 MInstalled size: 91 MDownloading Packages:(1/4): nodejs-10.19.0-1.fc30.x86_64.rpm 173 kB/s | 88 kB 00:00 (2/4): nodejs-full-i18n-10.19.0-1.fc30.x86_64.rpm 2.8 MB/s | 7.3 MB 00:02 (3/4): nodejs-libs-10.19.0-1.fc30.x86_64.rpm 2.7 MB/s | 9.1 MB 00:03 (4/4): npm-6.13.4-1.10.19.0.1.fc30.x86_64.rpm 1.3 MB/s | 3.8 MB 00:02 --------------------------------------------------------------------------------------------------------------------------------Total 4.9 MB/s | 20 MB 00:04 Running transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Running transaction Running scriptlet: npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 1/1 Preparing : 1/1 Installing : nodejs-libs-1:10.19.0-1.fc30.x86_64 1/4 Installing : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Installing : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 3/4 Installing : nodejs-1:10.19.0-1.fc30.x86_64 4/4 Running scriptlet: nodejs-1:10.19.0-1.fc30.x86_64 4/4 Verifying : nodejs-1:10.19.0-1.fc30.x86_64 1/4 Verifying : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Verifying : nodejs-libs-1:10.19.0-1.fc30.x86_64 3/4 Verifying : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 4/4 Installed: nodejs-1:10.19.0-1.fc30.x86_64 nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 nodejs-libs-1:10.19.0-1.fc30.x86_64 npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 Complete!After installing the Node package, you should use the Node package manager (npm) to install the Node Monitor nodemon. nodemon is a popular utility that automatically lets you restart Node programs when you make changes to the source code.While npm is installed as part of the Node package, you must use npm to install the Node Monitor. The following command installs the nodemon globally on your Fedora system. The -g flag lets you install it globally, which is important when you manage package.json files.npm install -g nodemonnpm install -g nodemonYou install nodemon globally but most of your web app or project files will be installed locally. Node is a different paradigm than building an Apache or IIS web application because Node provides a framework for you to build a web server.Here s a quick Hello World! example that I borrowed a JavaScript helloworld.js file from an excellent Web Development with Node Express: Leveraging the JavaScript Stack by Ethan Brown. For those who haven t worked with JavaScript in years, semicolons are optional now./* Construct a web server. */const http = require 'http' const port = process.env.PORT || 3000const server = http.createServer req, res = res.writeHead 200, 'Content-Type': 'text/plain' res.end 'Hello world!' server.listen port, = console.log `server started on port $ port ; ` + 'press Ctrl-C to terminate...' /* Construct a web server. */const http = require('http')const port = process.env.PORT || 3000const server = http.createServer((req, res) = { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello world!')server.listen(port, () = console.log(`server started on port ${port}); ` + 'press Ctrl-C to terminate...'))I put this in /var/www/html/node directory, which is owned by the superuser, root. You need to start the server before accessing it from a browser. You can start the program with the following syntax as a privileged user:node /var/www/html/node/helloworld.jsnode /var/www/html/node/helloworld.jsThen, you can use the localhost to access it with the following URL:http://localhost:3000http://localhost:3000It will display the following:Next, you need to use the Node Package Manager (npm) to install the Express.js packages. You do that with the following syntax:npm install -g express express-generatornpm install -g express express-generatorIt should produce a console out put like the following:npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)/usr/local/bin/express - /usr/local/lib/node_modules/express-generator/bin/express-cli.js+ express@4.17.1+ express-generator@4.16.1added 60 packages from 42 contributors in 4.798snpm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)/usr/local/bin/express - /usr/local/lib/node_modules/express-generator/bin/express-cli.js+ express@4.17.1+ express-generator@4.16.1added 60 packages from 42 contributors in 4.798sAfter you install all the packages, you can inspect them with the following command. The packages are found in the /usr/local/lib/node_modules/express directory. The listing is generated from the package.json file on Fedora and Ubuntu Linux.npm list -gnpm list -gIt should display something like this:/usr/local/lib├─┬ express@4.17.1│ ├─┬ accepts@1.3.7│ │ ├─┬ mime-types@2.1.27│ │ │ └── mime-db@1.44.0│ │ └── negotiator@0.6.2│ ├── array-flatten@1.1.1│ ├─┬ body-parser@1.19.0│ │ ├── bytes@3.1.0│ │ ├── content-type@1.0.4 deduped│ │ ├── debug@2.6.9 deduped│ │ ├── depd@1.1.2 deduped│ │ ├─┬ http-errors@1.7.2│ │ │ ├── depd@1.1.2 deduped│ │ │ ├── inherits@2.0.3│ │ │ ├── setprototypeof@1.1.1 deduped│ │ │ ├── statuses@1.5.0 deduped│ │ │ └── toidentifier@1.0.0│ │ ├─┬ iconv-lite@0.4.24│ │ │ └── safer-buffer@2.1.2│ │ ├── on-finished@2.3.0 deduped│ │ ├── qs@6.7.0 deduped│ │ ├─┬ raw-body@2.4.0│ │ │ ├── bytes@3.1.0 deduped│ │ │ ├── http-errors@1.7.2 deduped│ │ │ ├── iconv-lite@0.4.24 deduped│ │ │ └── unpipe@1.0.0 deduped│ │ └── type-is@1.6.18 deduped│ ├─┬ content-disposition@0.5.3│ │ └── safe-buffer@5.1.2 deduped│ ├── content-type@1.0.4│ ├── cookie@0.4.0│ ├── cookie-signature@1.0.6│ ├─┬ debug@2.6.9│ │ └── ms@2.0.0│ ├── depd@1.1.2│ ├── encodeurl@1.0.2│ ├── escape-html@1.0.3│ ├── etag@1.8.1│ ├─┬ finalhandler@1.1.2│ │ ├── debug@2.6.9 deduped│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── on-finished@2.3.0 deduped│ │ ├── parseurl@1.3.3 deduped│ │ ├── statuses@1.5.0 deduped│ │ └── unpipe@1.0.0│ ├── fresh@0.5.2│ ├── merge-descriptors@1.0.1│ ├── methods@1.1.2│ ├─┬ on-finished@2.3.0│ │ └── ee-first@1.1.1│ ├── parseurl@1.3.3│ ├── path-to-regexp@0.1.7│ ├─┬ proxy-addr@2.0.6│ │ ├── forwarded@0.1.2│ │ └── ipaddr.js@1.9.1│ ├── qs@6.7.0│ ├── range-parser@1.2.1│ ├── safe-buffer@5.1.2│ ├─┬ send@0.17.1│ │ ├── debug@2.6.9 deduped│ │ ├── depd@1.1.2 deduped│ │ ├── destroy@1.0.4│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── etag@1.8.1 deduped│ │ ├── fresh@0.5.2 deduped│ │ ├── http-errors@1.7.2 deduped│ │ ├── mime@1.6.0│ │ ├── ms@2.1.1│ │ ├── on-finished@2.3.0 deduped│ │ ├── range-parser@1.2.1 deduped│ │ └── statuses@1.5.0 deduped│ ├─┬ serve-static@1.14.1│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── parseurl@1.3.3 deduped│ │ └── send@0.17.1 deduped│ ├── setprototypeof@1.1.1│ ├── statuses@1.5.0│ ├─┬ type-is@1.6.18│ │ ├── media-typer@0.3.0│ │ └── mime-types@2.1.27 deduped│ ├── utils-merge@1.0.1│ └── vary@1.1.2└─┬ express-generator@4.16.1 ├── commander@2.15.1 ├── ejs@2.6.1 ├─┬ minimatch@3.0.4 │ └─┬ brace-expansion@1.1.11 │ ├── balanced-match@1.0.0 │ └── concat-map@0.0.1 ├─┬ mkdirp@0.5.1 │ └── minimist@0.0.8 └── sorted-object@2.0.1/usr/local/lib├─┬ express@4.17.1│ ├─┬ accepts@1.3.7│ │ ├─┬ mime-types@2.1.27│ │ │ └── mime-db@1.44.0│ │ └── negotiator@0.6.2│ ├── array-flatten@1.1.1│ ├─┬ body-parser@1.19.0│ │ ├── bytes@3.1.0│ │ ├── content-type@1.0.4 deduped│ │ ├── debug@2.6.9 deduped│ │ ├── depd@1.1.2 deduped│ │ ├─┬ http-errors@1.7.2│ │ │ ├── depd@1.1.2 deduped│ │ │ ├── inherits@2.0.3│ │ │ ├── setprototypeof@1.1.1 deduped│ │ │ ├── statuses@1.5.0 deduped│ │ │ └── toidentifier@1.0.0│ │ ├─┬ iconv-lite@0.4.24│ │ │ └── safer-buffer@2.1.2│ │ ├── on-finished@2.3.0 deduped│ │ ├── qs@6.7.0 deduped│ │ ├─┬ raw-body@2.4.0│ │ │ ├── bytes@3.1.0 deduped│ │ │ ├── http-errors@1.7.2 deduped│ │ │ ├── iconv-lite@0.4.24 deduped│ │ │ └── unpipe@1.0.0 deduped│ │ └── type-is@1.6.18 deduped│ ├─┬ content-disposition@0.5.3│ │ └── safe-buffer@5.1.2 deduped│ ├── content-type@1.0.4│ ├── cookie@0.4.0│ ├── cookie-signature@1.0.6│ ├─┬ debug@2.6.9│ │ └── ms@2.0.0│ ├── depd@1.1.2│ ├── encodeurl@1.0.2│ ├── escape-html@1.0.3│ ├── etag@1.8.1│ ├─┬ finalhandler@1.1.2│ │ ├── debug@2.6.9 deduped│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── on-finished@2.3.0 deduped│ │ ├── parseurl@1.3.3 deduped│ │ ├── statuses@1.5.0 deduped│ │ └── unpipe@1.0.0│ ├── fresh@0.5.2│ ├── merge-descriptors@1.0.1│ ├── methods@1.1.2│ ├─┬ on-finished@2.3.0│ │ └── ee-first@1.1.1│ ├── parseurl@1.3.3│ ├── path-to-regexp@0.1.7│ ├─┬ proxy-addr@2.0.6│ │ ├── forwarded@0.1.2│ │ └── ipaddr.js@1.9.1│ ├── qs@6.7.0│ ├── range-parser@1.2.1│ ├── safe-buffer@5.1.2│ ├─┬ send@0.17.1│ │ ├── debug@2.6.9 deduped│ │ ├── depd@1.1.2 deduped│ │ ├── destroy@1.0.4│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── etag@1.8.1 deduped│ │ ├── fresh@0.5.2 deduped│ │ ├── http-errors@1.7.2 deduped│ │ ├── mime@1.6.0│ │ ├── ms@2.1.1│ │ ├── on-finished@2.3.0 deduped│ │ ├── range-parser@1.2.1 deduped│ │ └── statuses@1.5.0 deduped│ ├─┬ serve-static@1.14.1│ │ ├── encodeurl@1.0.2 deduped│ │ ├── escape-html@1.0.3 deduped│ │ ├── parseurl@1.3.3 deduped│ │ └── send@0.17.1 deduped│ ├── setprototypeof@1.1.1│ ├── statuses@1.5.0│ ├─┬ type-is@1.6.18│ │ ├── media-typer@0.3.0│ │ └── mime-types@2.1.27 deduped│ ├── utils-merge@1.0.1│ └── vary@1.1.2└─┬ express-generator@4.16.1 ├── commander@2.15.1 ├── ejs@2.6.1 ├─┬ minimatch@3.0.4 │ └─┬ brace-expansion@1.1.11 │ ├── balanced-match@1.0.0 │ └── concat-map@0.0.1 ├─┬ mkdirp@0.5.1 │ └── minimist@0.0.8 └── sorted-object@2.0.1You can also create a secure node site (HTTPS) with the following additional steps. They include creating a self-signed secure public and private key. This creates the public key:openssl genrsa -out key.pemopenssl genrsa -out key.pemThe openssl command will generate a private key key.pem file. It generates something like the following text message to console:Generating RSA private key, 2048 bit long modulus (2 primes)...........+++++.............................................+++++e is 65537 (0x010001)Generating RSA private key, 2048 bit long modulus (2 primes)...........+++++.............................................+++++e is 65537 (0x010001)Next, you need to generate a self-signed certificate. You do this in two steps.Create a Distinguished Name (DN) file. The csr.pem file is the DN file. You need it to create a self-signed certificate:openssl req -new -key key.pem -out csr.pemopenssl req -new -key key.pem -out csr.pemIt will prompt you for values, like the following:You are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) [XX]:State or Province Name (full name) []:MiddleEarthLocality Name (eg, city) [Default City]:RivendellOrganization Name (eg, company) [Default Company Ltd]:Fellowship Organizational Unit Name (eg, section) []:SelfCommon Name (eg, your name or your server's hostname) []:LocalhostEmail Address []:bilbo@loth.orgPlease enter the following 'extra' attributesto be sent with your certificate requestA challenge password []:friendAn optional company name []:BilboYou are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) [XX]:State or Province Name (full name) []:MiddleEarthLocality Name (eg, city) [Default City]:RivendellOrganization Name (eg, company) [Default Company Ltd]:Fellowship Organizational Unit Name (eg, section) []:SelfCommon Name (eg, your name or your server's hostname) []:LocalhostEmail Address []:bilbo@loth.orgPlease enter the following 'extra' attributesto be sent with your certificate requestA challenge password []:friendAn optional company name []:BilboUse the DN file to create your secure certificate. The following openssl command creates the certificate file by using your private key key.pem and DN csr.pem files.openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pemopenssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pemIt should generate a Secure certificate cert.pem file and return something like the following to the console.Signature oksubject=C = XX, ST = MiddleEarth, L = Rivendell, O = Fellowship, OU = Self, CN = Localhost, emailAddress = bilbo@loth.orgGetting Private keySignature oksubject=C = XX, ST = MiddleEarth, L = Rivendell, O = Fellowship, OU = Self, CN = Localhost, emailAddress = bilbo@loth.orgGetting Private keyYou can put these private key (key.pem) and certificate (cert.pem) files in an ssl subdirectory of the directory where you put the JavaScript program. The following creates a secure server page with the following code./* Construct a secure web server. */ const https = require 'https' const fs = require 'fs' const port = process.env.PORT || 3000 const options = key: fs.readFileSync 'ssl/key.pem' , cert: fs.readFileSync 'ssl/cert.pem' const server = https.createServer options, res = res.writeHead 200, 'Content-Type': 'text/plain' res.end 'Hello world!' server.listen port, = console.log `server started on port $ port ; ` + 'press Ctrl-C to terminate...' /* Construct a secure web server. */ const https = require('https') const fs = require('fs') const port = process.env.PORT || 3000 const options = { key: fs.readFileSync('ssl/key.pem'), cert: fs.readFileSync('ssl/cert.pem') const server = https.createServer((options, res) = { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello world!') server.listen(port, () = console.log(`server started on port ${port}); ` + 'press Ctrl-C to terminate...'))If you try launch your browser using the localhost instead of a DNS or file resolved network name on the designated port, it will raise the following security error:This site can't be reachedhttps's server IP address could not be found.DNS_PROBE_FINISHED_NXDOMAINThis site can't be reachedhttps's server IP address could not be found.DNS_PROBE_FINISHED_NXDOMAINAn alternate approach to writing a secure server includes using Express.js library. The syntax changes somewhat and you include two new libraries, as shown below:/* Construct a secure web server. */ const https = require 'https' const express = require 'express' const fs = require 'fs' const app = express const port = process.env.PORT || 3001 const options = key: fs.readFileSync 'ssl/key.pem' , cert: fs.readFileSync 'ssl/cert.pem' https.createServer options, app .listen port, = console.log `Express started in $ app.get 'env' mode ` + `on port + $ port .` /* Construct a secure web server. */ const https = require('https') const express = require('express') const fs = require('fs') const app = express() const port = process.env.PORT || 3001 const options = { key: fs.readFileSync('ssl/key.pem'), cert: fs.readFileSync('ssl/cert.pem') https.createServer(options, app).listen(port, () = { console.log(`Express started in ${app.get('env')} mode ` + `on port + ${port}.`) })This will fail with the following error message if you re running it with a global installation unless you set the $NODE_PATH environment variable correctly. Without setting the variable you may get the following error message:internal/modules/cjs/loader.js:638 throw err; Error: Cannot find module 'express' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15) at Function.Module._load (internal/modules/cjs/loader.js:562:25) at Module.require (internal/modules/cjs/loader.js:692:17) at require (internal/modules/cjs/helpers.js:25:18) at Object. anonymous (/var/www/html/node/helloworldsecure.js:3:17) at Module._compile (internal/modules/cjs/loader.js:778:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) at Module.load (internal/modules/cjs/loader.js:653:32) at tryModuleLoad (internal/modules/cjs/loader.js:593:12) at Function.Module._load (internal/modules/cjs/loader.js:585:3)internal/modules/cjs/loader.js:638 throw err; Error: Cannot find module 'express' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15) at Function.Module._load (internal/modules/cjs/loader.js:562:25) at Module.require (internal/modules/cjs/loader.js:692:17) at require (internal/modules/cjs/helpers.js:25:18) at Object. anonymous (/var/www/html/node/helloworldsecure.js:3:17) at Module._compile (internal/modules/cjs/loader.js:778:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) at Module.load (internal/modules/cjs/loader.js:653:32) at tryModuleLoad (internal/modules/cjs/loader.js:593:12) at Function.Module._load (internal/modules/cjs/loader.js:585:3)Sometimes they ll advise you to do an individual user installation of Express.js to get past this error but that s not necessary. You just need to set the $NODE_PATH environment variable as follows:export NODE_PATH=/usr/local/lib/node_modulesexport NODE_PATH=/usr/local/lib/node_modulesThis will enable the JavaScript to work without error and without a specific user installation. Assuming you name either of these programs as helloworldsecure.js, you run them with the following command:node helloworldsecure.jsnode helloworldsecure.jsYou can terminate the program with a Ctrl+c or if use the kill -15 pid command if you started it as a background process. You can find the process ID (pid) with the jobs command.As always, I hope this is helpful to those starting out with this cool technology stack. While teaching a class on the Linux Command-Line (CLI), the book gave an example of generating a list of random US telephone numbers into a file. The book uses the RANDOM function to generate segments of the telephone number, and then the grep command to identify malformed telephone numbers.My students wanted me to explain why the numbers were malformed. I had to explain that the RANDOM function returns a random number between 1 and 99,999. The RANDOM function may return a 1 to 5 digit random number, which means you may get a 1-digit or 2-digit number when you request a 3-digit random number or a 1- to 3-digit number when you request a 4-digit random number.The author s example is:for i in 1..10 ; do echo (${RANDOM:0:3}) ${RANDOM:0:3}-${RANDOM:0:4} list.txtdonefor i in {1..10}; do echo (${RANDOM:0:3}) ${RANDOM:0:3}-${RANDOM:0:4} list.txtdoneThey asked if there was a way to write a shell script that guaranteed random but well-formed US telephone numbers. I said yes, however, you need to write a recursive bash shell function and assign the result to a global variable set in the shell script.They seemed doubtful, so I wrote it for them. Here s the script if you re interested in learning more about bash shell scripting. While I implemented it with an bash array, that s optional.#!/usr/bin/bash# ============================================================# Name: telephone.sh# Author: Michael McLaughlin# Date: 05-May-2020# ------------------------------------------------------------# Purpose: Demonstrate how to generate random telehpone# numbers. The RANDOM function returns a random# number between 1 and 99999; and while you can# easily shave off a extra digit guarnteeing a# value above 100 is impossible without logic.# ============================================================targetLength # Declare variable in function-level scope. randomString='' # Check the number of parameters to process. if ${#} = 2 ; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$ ${formatLength}-${length} # Check if the telephone number is too short. if ${short} 0 ; then randomString=`echo ${randomString}${RANDOM:0:${short}} ` # Check if the combination of random numbers equals the target length # and assign the value to the global variable, or repeat processing # by making a recursive function call. if `echo -n ${randomString} | wc -c` = ${formatLength} ; then result=${randomString} else targetLength ${randomString} ${formatLength}# Declare global variable to support targetLength().result=''# Declare an array of strings.declare -A telephone_parts# Generate one hundred random telephone numbers.for i in 1..100 ; do # Create random three digit area code. targetLength ${RANDOM:0:3} 3 telephone_parts 1 =${result} # Create random three digit prefix code. targetLength ${RANDOM:0:3} 3 telephone_parts 2 =${result} # Create random four digit number code. targetLength ${RANDOM:0:4} 4 telephone_parts 3 =${result} # Print the telephone numbers. echo [${i}] (${telephone_parts[1]}) ${telephone_parts[2]}-${telephone_parts[3]} done#!/usr/bin/bash# ============================================================# Name: telephone.sh# Author: Michael McLaughlin# Date: 05-May-2020# ------------------------------------------------------------# Purpose: Demonstrate how to generate random telehpone# numbers. The RANDOM function returns a random# number between 1 and 99999; and while you can# easily shave off a extra digit guarnteeing a# value above 100 is impossible without logic.# ============================================================targetLength() # Declare variable in function-level scope. randomString='' # Check the number of parameters to process. if [[ ${#} = 2 ]]; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$((${formatLength}-${length})) # Check if the telephone number is too short. if [[ ${short} 0 ]]; then randomString=`echo ${randomString}${RANDOM:0:${short}} ` # Check if the combination of random numbers equals the target length # and assign the value to the global variable, or repeat processing # by making a recursive function call. if [[ `echo -n ${randomString} | wc -c` = ${formatLength} ]]; then result=${randomString} else targetLength ${randomString} ${formatLength}# Declare global variable to support targetLength().result=''# Declare an array of strings.declare -A telephone_parts# Generate one hundred random telephone numbers.for i in {1..100}; do # Create random three digit area code. targetLength ${RANDOM:0:3} 3 telephone_parts[1]=${result} # Create random three digit prefix code. targetLength ${RANDOM:0:3} 3 telephone_parts[2]=${result} # Create random four digit number code. targetLength ${RANDOM:0:4} 4 telephone_parts[3]=${result} # Print the telephone numbers. echo [${i}] (${telephone_parts[1]}) ${telephone_parts[2]}-${telephone_parts[3]} doneFor reference, a recursive function call isn t required here. It could be done more effectively with the following while loop:targetLength # Declare variable in function-level scope. randomString='' short=1 # Check the number of parameters to process. if ${#} = 2 ; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Check if the telephone number is too short. while ${short} 0 ; do # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$ ${formatLength}-${length} # Assign new value to randomString. randomString=`echo ${randomString}${RANDOM:0:${short}} ` done # Assign randomString to global result variable. result=${randomString} targetLength() # Declare variable in function-level scope. randomString='' short=1 # Check the number of parameters to process. if [[ ${#} = 2 ]]; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Check if the telephone number is too short. while [[ ${short} 0 ]]; do # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$((${formatLength}-${length})) # Assign new value to randomString. randomString=`echo ${randomString}${RANDOM:0:${short}} ` done # Assign randomString to global result variable. result=${randomString}}As always, I hope this helps those you want to learn or solve a problem. One of the key problems with Oracle s deployment is that you can not use the up-arrow key to navigate the sqlplus command-line history. Here s little Bash shell function that you can put in your .bashrc file. It requires you to have your system administrator install the rlwrap package, which wraps the sqlplus command-line history.You should also set the $ORACLE_HOME environment variable before you put this function in your .bashrc file.sqlplus # Discover the fully qualified program name. path=`which rlwrap 2 /dev/null` file='' # Parse the program name from the path. if -n ${path} ; then file=${path##/*/} # Wrap when there is a file and it is rewrap. if -n ${file} ${file} = rlwrap ; then rlwrap sqlplus ${@} else echo Command-line history unavailable: Install the rlwrap package. $ORACLE_HOME/bin/sqlplus ${@} sqlplus () # Discover the fully qualified program name. path=`which rlwrap 2 /dev/null` file='' # Parse the program name from the path. if [ -n ${path} ]; then file=${path##/*/} # Wrap when there is a file and it is rewrap. if [ -n ${file} ] [[ ${file} = rlwrap ]]; then rlwrap sqlplus ${@} else echo Command-line history unavailable: Install the rlwrap package. $ORACLE_HOME/bin/sqlplus ${@} }If you port this shell script to an environment where rlwrap is not installed, it simply prints the error message and advises you to install the rlwrap package.As always, I hope this helps those looking for a solution. In April, when I updated from MySQL 8.0.17 to MySQL 8.0.19, I found that my Java connection example failed. That s because of a change in the JDBC driver, which I blogged about then. Starting yesterday, I began updating a base Fedora 30 configuration again to MySQL 8.0.20. I wrote a testing program for the Java JDBC file last time, and when I ran it this time it told me that I didn t have the JDBC driver installed, or in the $CLASSPATH. My Java diagnostic script, MySQLDriver.java, returned the following error message:Error: Could not find or load main class MySQLDriverError: Could not find or load main class MySQLDriverThe Java JDBC test program code is in the prior post. It simply loads the user, password, database, host, and port statically for my student image, which you can update for your MySQL deployment.You can find the MySQL 8.0 Connector/J installation in Chapter 4 of the MySQL Connector/J 8.0 Developer Guide. The link to page where you can download the Connector/J driver is in Chapter 4, but to save time you can click on the following download link. You ll find Ubuntu, Debian, SUSE Enterprise Linux Server, Red Hat Enterprise Linux/Oracle Linux, Fedora, Platform Independent, and Source Code at that download link.I downloaded Fedora 30, 31, and 32 because eventually they get deprecated and sometimes I need the deprecated versions. Since Python 2.7 is deprecated, keeping an old Fedora 30 with both Python 2.7 and 3.7 is very attractive as a developer, keeping these Java libraries is also a potential convenience. On the Fedora 30 platform, you should download:mysql-connector-java-8.0.20-1.fc30.noarch.rpmmysql-connector-java-8.0.20-1.fc30.noarch.rpmYou install the RPM package with the following syntax:rpm -ivh mysql-connector-java-8.0.20-1.fc30.noarch.rpmrpm -ivh mysql-connector-java-8.0.20-1.fc30.noarch.rpmIt installs a single mysql-connector-java.jar file in the /usr/share/java directory. Then, you need to export the Java Archive file like so in your .bashrc file:# Set the Java CLASSPATH path.export CLASSPATH=/usr/share/java/mysql-connector-java.jar:.# Set the Java CLASSPATH path.export CLASSPATH=/usr/share/java/mysql-connector-java.jar:.Now, my MySQLDriver Java program returned:Cannot connect to database server:The server time zone value 'MDT' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver via the 'serverTimezone' configuration property to use a more specifc time zone value if you want to utilize time zone support.Cannot connect to database server:The server time zone value 'MDT' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.As qualified in the prior post, it required me to add the following line to my /etc/my.cnf configuration file, which synchronizes the database s timezone with the operating system.# Synchronize the MySQL clock with the computer system clock.default-time-zone='+00:00'# Synchronize the MySQL clock with the computer system clock.default-time-zone='+00:00'You need to restart the MySQL Daemon to apply the change, like:sudo systemctl restart mysqld.servicesudo systemctl restart mysqld.serviceThen, running my MySQL Driver connection test program worked like a charm. It returns the following:Database connection establishedMySQLDriver Version 8.0.20 Database connection terminatedDatabase connection establishedMySQLDriver Version [8.0.20]Database connection terminatedAs always, I hope this helps those looking for a quick solution. After I updated a Fedora 30 instance, I could no longer connect to the MySQL database. An attempt to connect raised the following error:Error: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)Error: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)My guess was correct, the mysqld.service got removed during the update (a synonym for upgrade). So, I ran the following command as a sudoer user:sudo systemctl enable mysqld.servicesudo systemctl enable mysqld.serviceIt creates the following symbolic link:Created symlink /etc/systemd/system/multi-user.target.wants/mysqld.service → /usr/lib/systemd/system/mysqld.service.Created symlink /etc/systemd/system/multi-user.target.wants/mysqld.service → /usr/lib/systemd/system/mysqld.service.That would start the MySQL Daemon (mysqld) on the next restart of the OS. However, I didn t want to restart to have access to the service. I simply started it with the following command:sudo systemctl start mysqld.servicesudo systemctl start mysqld.serviceThen, I could connect to the MySQL database. As always, I hope this helps those looking for a solution. Having played around with Erlang some twelve years ago, I felt it was time to experiment with the Elixir programming language. The install on Fedora was straightforward with Dandified YUM. Naturally, you need to be the root user or a user found in the sudoer s list:sudo dnf install -y elixirsudo dnf install -y elixirDisplay detailed console log Last metadata expiration check: 4:47:05 ago on Wed 20 May 2020 05:57:25 PM MDT.Dependencies resolved.====================================================================================================== Package Architecture Version Repository Size======================================================================================================Installing: elixir x86_64 1.9.0-1.fc30 updates 4.9 MInstalling dependencies: erlang-asn1 x86_64 21.3.8.15-1.fc30 updates 742 k erlang-compiler x86_64 21.3.8.15-1.fc30 updates 1.1 M erlang-crypto x86_64 21.3.8.15-1.fc30 updates 141 k erlang-erts x86_64 21.3.8.15-1.fc30 updates 2.2 M erlang-hipe x86_64 21.3.8.15-1.fc30 updates 2.8 M erlang-inets x86_64 21.3.8.15-1.fc30 updates 646 k erlang-kernel x86_64 21.3.8.15-1.fc30 updates 1.4 M erlang-mnesia x86_64 21.3.8.15-1.fc30 updates 767 k erlang-parsetools x86_64 21.3.8.15-1.fc30 updates 175 k erlang-public_key x86_64 21.3.8.15-1.fc30 updates 612 k erlang-runtime_tools x86_64 21.3.8.15-1.fc30 updates 215 k erlang-sasl x86_64 21.3.8.15-1.fc30 updates 279 k erlang-ssl x86_64 21.3.8.15-1.fc30 updates 931 k erlang-stdlib x86_64 21.3.8.15-1.fc30 updates 3.0 M erlang-syntax_tools x86_64 21.3.8.15-1.fc30 updates 410 k erlang-tools x86_64 21.3.8.15-1.fc30 updates 618 kTransaction Summary======================================================================================================Install 17 PackagesTotal download size: 21 MInstalled size: 42 MDownloading Packages:(1/17): erlang-compiler-21.3.8.15-1.fc30.x86_64.rpm 911 kB/s | 1.1 MB 00:01 (2/17): erlang-crypto-21.3.8.15-1.fc30.x86_64.rpm 1.1 MB/s | 141 kB 00:00 (3/17): erlang-asn1-21.3.8.15-1.fc30.x86_64.rpm 441 kB/s | 742 kB 00:01 (4/17): erlang-erts-21.3.8.15-1.fc30.x86_64.rpm 2.7 MB/s | 2.2 MB 00:00 (5/17): erlang-inets-21.3.8.15-1.fc30.x86_64.rpm 2.6 MB/s | 646 kB 00:00 (6/17): erlang-kernel-21.3.8.15-1.fc30.x86_64.rpm 1.9 MB/s | 1.4 MB 00:00 (7/17): erlang-mnesia-21.3.8.15-1.fc30.x86_64.rpm 922 kB/s | 767 kB 00:00 (8/17): erlang-parsetools-21.3.8.15-1.fc30.x86_64.rpm 669 kB/s | 175 kB 00:00 (9/17): erlang-public_key-21.3.8.15-1.fc30.x86_64.rpm 602 kB/s | 612 kB 00:01 (10/17): erlang-runtime_tools-21.3.8.15-1.fc30.x86_64.rpm 384 kB/s | 215 kB 00:00 (11/17): erlang-sasl-21.3.8.15-1.fc30.x86_64.rpm 421 kB/s | 279 kB 00:00 (12/17): erlang-hipe-21.3.8.15-1.fc30.x86_64.rpm 465 kB/s | 2.8 MB 00:06 (13/17): erlang-ssl-21.3.8.15-1.fc30.x86_64.rpm 325 kB/s | 931 kB 00:02 (14/17): erlang-syntax_tools-21.3.8.15-1.fc30.x86_64.rpm 246 kB/s | 410 kB 00:01 (15/17): elixir-1.9.0-1.fc30.x86_64.rpm 392 kB/s | 4.9 MB 00:12 (16/17): erlang-stdlib-21.3.8.15-1.fc30.x86_64.rpm 580 kB/s | 3.0 MB 00:05 (17/17): erlang-tools-21.3.8.15-1.fc30.x86_64.rpm 294 kB/s | 618 kB 00:02 ------------------------------------------------------------------------------------------------------Total 1.5 MB/s | 21 MB 00:13 Running transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Running transaction Preparing : 1/1 Installing : erlang-crypto-21.3.8.15-1.fc30.x86_64 1/17 Running scriptlet: erlang-erts-21.3.8.15-1.fc30.x86_64 2/17 Installing : erlang-erts-21.3.8.15-1.fc30.x86_64 2/17 Installing : erlang-kernel-21.3.8.15-1.fc30.x86_64 3/17 Installing : erlang-stdlib-21.3.8.15-1.fc30.x86_64 4/17 Installing : erlang-hipe-21.3.8.15-1.fc30.x86_64 5/17 Installing : erlang-compiler-21.3.8.15-1.fc30.x86_64 6/17 Installing : erlang-syntax_tools-21.3.8.15-1.fc30.x86_64 7/17 Installing : erlang-mnesia-21.3.8.15-1.fc30.x86_64 8/17 Installing : erlang-runtime_tools-21.3.8.15-1.fc30.x86_64 9/17 Installing : erlang-asn1-21.3.8.15-1.fc30.x86_64 10/17 Installing : erlang-public_key-21.3.8.15-1.fc30.x86_64 11/17 Installing : erlang-inets-21.3.8.15-1.fc30.x86_64 12/17 Installing : erlang-ssl-21.3.8.15-1.fc30.x86_64 13/17 Installing : erlang-tools-21.3.8.15-1.fc30.x86_64 14/17 Installing : erlang-sasl-21.3.8.15-1.fc30.x86_64 15/17 Installing : erlang-parsetools-21.3.8.15-1.fc30.x86_64 16/17 Installing : elixir-1.9.0-1.fc30.x86_64 17/17 Running scriptlet: elixir-1.9.0-1.fc30.x86_64 17/17 Verifying : elixir-1.9.0-1.fc30.x86_64 1/17 Verifying : erlang-asn1-21.3.8.15-1.fc30.x86_64 2/17 Verifying : erlang-compiler-21.3.8.15-1.fc30.x86_64 3/17 Verifying : erlang-crypto-21.3.8.15-1.fc30.x86_64 4/17 Verifying : erlang-erts-21.3.8.15-1.fc30.x86_64 5/17 Verifying : erlang-hipe-21.3.8.15-1.fc30.x86_64 6/17 Verifying : erlang-inets-21.3.8.15-1.fc30.x86_64 7/17 Verifying : erlang-kernel-21.3.8.15-1.fc30.x86_64 8/17 Verifying : erlang-mnesia-21.3.8.15-1.fc30.x86_64 9/17 Verifying : erlang-parsetools-21.3.8.15-1.fc30.x86_64 10/17 Verifying : erlang-public_key-21.3.8.15-1.fc30.x86_64 11/17 Verifying : erlang-runtime_tools-21.3.8.15-1.fc30.x86_64 12/17 Verifying : erlang-sasl-21.3.8.15-1.fc30.x86_64 13/17 Verifying : erlang-ssl-21.3.8.15-1.fc30.x86_64 14/17 Verifying : erlang-stdlib-21.3.8.15-1.fc30.x86_64 15/17 Verifying : erlang-syntax_tools-21.3.8.15-1.fc30.x86_64 16/17 Verifying : erlang-tools-21.3.8.15-1.fc30.x86_64 17/17 Installed: elixir-1.9.0-1.fc30.x86_64 erlang-asn1-21.3.8.15-1.fc30.x86_64 erlang-compiler-21.3.8.15-1.fc30.x86_64 erlang-crypto-21.3.8.15-1.fc30.x86_64 erlang-erts-21.3.8.15-1.fc30.x86_64 erlang-hipe-21.3.8.15-1.fc30.x86_64 erlang-inets-21.3.8.15-1.fc30.x86_64 erlang-kernel-21.3.8.15-1.fc30.x86_64 erlang-mnesia-21.3.8.15-1.fc30.x86_64 erlang-parsetools-21.3.8.15-1.fc30.x86_64 erlang-public_key-21.3.8.15-1.fc30.x86_64 erlang-runtime_tools-21.3.8.15-1.fc30.x86_64 erlang-sasl-21.3.8.15-1.fc30.x86_64 erlang-ssl-21.3.8.15-1.fc30.x86_64 erlang-stdlib-21.3.8.15-1.fc30.x86_64 erlang-syntax_tools-21.3.8.15-1.fc30.x86_64 erlang-tools-21.3.8.15-1.fc30.x86_64 Complete!The installation said to add it to the $PATH variable but on Fedora 30, a symbolic link of elixir is installed in the /usr/bin directory that points to /usr/share/elixir/1.9.0/bin/elixir shell script. Next, a version check, like this:elixir --versionelixir --versionit returnedErlang/OTP 21 [erts-10.3.5.11] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]Elixir 1.9.0 (compiled with Erlang/OTP 21)Erlang/OTP 21 [erts-10.3.5.11] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]Elixir 1.9.0 (compiled with Erlang/OTP 21)Next, I created a little elixir test program, naturally after I glanced through the documentation for a Hello World program, like this hello.exs file:IO.puts Hello, Elixir World! IO.puts Hello, Elixir World! Then, I ran it as stand alone file with only read and write, read, and read privileges:elixir hello.exselixir hello.exsIt returns:Hello, Elixir World!Hello, Elixir World!Next, I tried to read the file from the file system in the iex interactive shell. I thought the example on the website was odd because it gave the impression that you were to call iex recursively but I discovered that s not the case. You can only run it from the OS shell, and the file must have read, write, execute privileges for whomever calls it. Then, this syntax works from the Linux CLI interfaace:iex -S hello.exsiex -S hello.exsThen, you exit the shell by issuing a ^Z (CTRL+Z). If you try ^C, you ll need to follow that with ^D, which I found in some notes. The ^Z is the normal kill signal for the current process, which appears the proper way to exit the iex interactive shell. Overall, the interactive shell is virtually useless except to validate small syntax examples. It s amazing how old some of my students computers are. The oldest with least memory are the foreign students. Fortunately, I kept copies of the old Oracle Database 10g XE. I give it to some students who need to run the smallest possible option. Then, again I have students who get emotional about having to use Unix or Linux as an operating system, which means I now also support Oracle Database 18c.Anyway, I had to write a script that would support building a small 200 MB student schema in any of the Express Edition databases from 10g to 18c. Here s the script for those who would like to use it. It sets up a student schema for Oracle Database 10g and 11g databases and a c##student schema for Oracle s Containized Database 12c and 18c.1DECLARE /* Control variable. */ container BOOLEAN := FALSE;BEGIN /* Check if the current user is the superuser. */ FOR i IN SELECT USER FROM dual LOOP /* Perform tasks as superuser. */ IF i.USER = 'SYSTEM' THEN /* Check for a container-enabled column, which enables this to work in both pre-container Oracle databases, like 10g and 11g. */ FOR j IN SELECT DISTINCT column_name FROM dba_tab_columns WHERE column_name = 'CDB' LOOP /* Check for a container database. */ FOR k IN SELECT cdb FROM v$database WHERE cdb = 'YES' LOOP container := TRUE; END LOOP; END LOOP; /* Conditionally drop existing user and role. */ IF container THEN /* Conditionally drop a container user. */ FOR j IN SELECT username FROM dba_users WHERE username = 'C##STUDENT' LOOP EXECUTE IMMEDIATE 'DROP USER c##student CASCADE'; END LOOP; /* Conditionally rop the container c##studentrole role. */ FOR j IN SELECT ROLE FROM dba_roles WHERE ROLE = 'C##STUDENTROLE' LOOP EXECUTE IMMEDIATE 'DROP ROLE c##studentrole'; END LOOP; /* Create a container user with 200 MB of space. */ EXECUTE IMMEDIATE 'CREATE USER c##student'||CHR 10 || 'IDENTIFIED BY student'||CHR 10 || 'DEFAULT TABLESPACE users'||CHR 10 || 'QUOTA 200M ON users'||CHR 10 || 'TEMPORARY TABLESPACE temp'; /* Create a container role. */ EXECUTE IMMEDIATE 'CREATE ROLE c##studentrole CONTAINER = ALL'; /* Grant privileges to a container user. */ EXECUTE IMMEDIATE 'GRANT CREATE CLUSTER, CREATE INDEXTYPE,'||CHR 10 || 'CREATE PROCEDURE, CREATE SEQUENCE,'||CHR 10 || 'CREATE SESSION, CREATE TABLE,'||CHR 10 || 'CREATE TRIGGER, CREATE TYPE,'||CHR 10 || 'CREATE VIEW TO c##studentrole'; /* Grant role to user. */ EXECUTE IMMEDIATE 'GRANT c##studentrole TO c##student'; ELSE /* Conditonally drop the non-container database user. */ FOR j IN SELECT username FROM dba_users WHERE username = 'STUDENT' LOOP EXECUTE IMMEDIATE 'DROP USER student CASCADE'; END LOOP; /* Create the student database. */ EXECUTE IMMEDIATE 'CREATE USER student'||CHR 10 || 'IDENTIFIED BY student'||CHR 10 || 'DEFAULT TABLESPACE users'||CHR 10 || 'QUOTA 200M ON users'||CHR 10 || 'TEMPORARY TABLESPACE temp'; /* Grant necessary privileges to the student database. */ EXECUTE IMMEDIATE 'GRANT CREATE CLUSTER, CREATE INDEXTYPE,'||CHR 10 || 'CREATE PROCEDURE, CREATE SEQUENCE,'||CHR 10 || 'CREATE SESSION, CREATE TABLE,'||CHR 10 || 'CREATE TRIGGER, CREATE TYPE,'||CHR 10 || 'CREATE VIEW TO student'; END IF; ELSE /* Print an message that the user lacks privilegs. */ dbms_output.put_line 'You must be the SYSTEM user to drop and create a user.' ; END IF; END LOOP;/DECLARE /* Control variable. */ container BOOLEAN := FALSE;BEGIN /* Check if the current user is the superuser. */ FOR i IN (SELECT user FROM dual) LOOP /* Perform tasks as superuser. */ IF i.user = 'SYSTEM' THEN /* Check for a container-enabled column, which enables this to work in both pre-container Oracle databases, like 10g and 11g. */ FOR j IN (SELECT DISTINCT column_name FROM dba_tab_columns WHERE column_name = 'CDB') LOOP /* Check for a container database. */ FOR k IN (SELECT cdb FROM v$database WHERE cdb = 'YES') LOOP container := TRUE; END LOOP; END LOOP; /* Conditionally drop existing user and role. */ IF container THEN /* Conditionally drop a container user. */ FOR j IN (SELECT username FROM dba_users WHERE username = 'C##STUDENT') LOOP EXECUTE IMMEDIATE 'DROP USER c##student CASCADE'; END LOOP; /* Conditionally rop the container c##studentrole role. */ FOR j IN (SELECT role FROM dba_roles WHERE role = 'C##STUDENTROLE') LOOP EXECUTE IMMEDIATE 'DROP ROLE c##studentrole'; END LOOP; /* Create a container user with 200 MB of space. */ EXECUTE IMMEDIATE 'CREATE USER c##student'||CHR(10) || 'IDENTIFIED BY student'||CHR(10) || 'DEFAULT TABLESPACE users'||CHR(10) || 'QUOTA 200M ON users'||CHR(10) || 'TEMPORARY TABLESPACE temp'; /* Create a container role. */ EXECUTE IMMEDIATE 'CREATE ROLE c##studentrole CONTAINER = ALL'; /* Grant privileges to a container user. */ EXECUTE IMMEDIATE 'GRANT CREATE CLUSTER, CREATE INDEXTYPE,'||CHR(10) || 'CREATE PROCEDURE, CREATE SEQUENCE,'||CHR(10) || 'CREATE SESSION, CREATE TABLE,'||CHR(10) || 'CREATE TRIGGER, CREATE TYPE,'||CHR(10) || 'CREATE VIEW TO c##studentrole'; /* Grant role to user. */ EXECUTE IMMEDIATE 'GRANT c##studentrole TO c##student'; ELSE /* Conditonally drop the non-container database user. */ FOR j IN (SELECT username FROM dba_users WHERE username = 'STUDENT') LOOP EXECUTE IMMEDIATE 'DROP USER student CASCADE'; END LOOP; /* Create the student database. */ EXECUTE IMMEDIATE 'CREATE USER student'||CHR(10) || 'IDENTIFIED BY student'||CHR(10) || 'DEFAULT TABLESPACE users'||CHR(10) || 'QUOTA 200M ON users'||CHR(10) || 'TEMPORARY TABLESPACE temp'; /* Grant necessary privileges to the student database. */ EXECUTE IMMEDIATE 'GRANT CREATE CLUSTER, CREATE INDEXTYPE,'||CHR(10) || 'CREATE PROCEDURE, CREATE SEQUENCE,'||CHR(10) || 'CREATE SESSION, CREATE TABLE,'||CHR(10) || 'CREATE TRIGGER, CREATE TYPE,'||CHR(10) || 'CREATE VIEW TO student'; END IF; ELSE /* Print an message that the user lacks privilegs. */ dbms_output.put_line('You must be the SYSTEM user to drop and create a user.'); END IF; END LOOP;/As always, I hope this helps those looking for a solution beyond Quest s Toad for Oracle, APEX, or SQL Developer. Let me know if you like it. My students want to transfer their course setup MySQL files from the Linux VM that I provide to Microsoft Windows 10. This post explains how because I found a couple small errors in the Google d posts they would most likely see.The first step is required because when I didn t assign a name or domain to the the Fedora VM, which allows it to run as localhost on any student machine. In tandem, I didn t assign a static IP address but opted for dynamic IP assignment. That means, the first step to securely copy the files requires you to find the assigned IP address. You can do that with the following Linux command:ifconfig -a | grep 'inet[[:blank:]]' | head -1 | cut -c 14-30ifconfig -a | grep 'inet[[:blank:]]' | head -1 | cut -c 14-30It would return something like:192.168.147.198192.168.147.198After you have discovered the IP address, you need to download PuTTy from their web site because includes the pscp (PuTTy Secure Copy) utility. I recommend you click on the MSI (Microsoft Installer Package) file, and install it on your Windows machine. As a rule, you should accept the default location, which is C:\Program Files\PuTTy.While you could alter your system-level %PATH% environment variable after you install the software, I recommend you only include it in the %PATH% within the scope of a Command (cmd) shell. Navigate to your Windows Start and enter cmd in the search field. It should launch the Command Prompt terminal, which is a terminal emulator.In the terminal editor, type the following case sensitive command to add the PuTTy directory to your path (yes, it s case sensitive):SET PATH=%PATH%;%ProgramFiles%\PuTTySET PATH=%PATH%;%ProgramFiles%\PuTTyNow, you can securely copy the files and directory structure from Linux to Windows with the following command (where you replace the traditional server name with the dynamically assigned IP address). You should also be in the target directory where you want the files and directories copied:C:\Data\cit225 pscp -r student@192.168.147.198:/home/student/Data/cit225/mysql .C:\Data\cit225 pscp -r student@192.168.147.198:/home/student/Data/cit225/mysql .After entering the password for the student on the Linux VM, you should see the following copy over:Raiders2.png | 99 kB | 99.5 kB/s | ETA: 00:00:00 | 100%LordOfTheRings3.png | 119 kB | 119.8 kB/s | ETA: 00:00:00 | 100%HarryPotter4.png | 103 kB | 103.9 kB/s | ETA: 00:00:00 | 100%Raiders1.png | 92 kB | 92.4 kB/s | ETA: 00:00:00 | 100%Raiders3.png | 123 kB | 123.9 kB/s | ETA: 00:00:00 | 100%LordOfTheRings2.png | 111 kB | 111.7 kB/s | ETA: 00:00:00 | 100%LordOfTheRings1.png | 103 kB | 104.0 kB/s | ETA: 00:00:00 | 100%HarryPotter2.png | 118 kB | 118.7 kB/s | ETA: 00:00:00 | 100%HarryPotter7.png | 150 kB | 150.2 kB/s | ETA: 00:00:00 | 100%HarryPotter3.png | 106 kB | 106.1 kB/s | ETA: 00:00:00 | 100%HarryPotter5.png | 82 kB | 82.5 kB/s | ETA: 00:00:00 | 100%HarryPotter6.png | 129 kB | 129.9 kB/s | ETA: 00:00:00 | 100%HarryPotter1.png | 118 kB | 118.8 kB/s | ETA: 00:00:00 | 100%HarryPotter8.png | 150 kB | 150.9 kB/s | ETA: 00:00:00 | 100%HarryPotter8.txt | 8 kB | 8.5 kB/s | ETA: 00:00:00 | 100%HarryPotter3.txt | 5 kB | 5.8 kB/s | ETA: 00:00:00 | 100%HarryPotter5.txt | 7 kB | 7.9 kB/s | ETA: 00:00:00 | 100%HarryPotter1.txt | 6 kB | 6.6 kB/s | ETA: 00:00:00 | 100%HarryPotter2.txt | 7 kB | 7.8 kB/s | ETA: 00:00:00 | 100%Raiders3.txt | 5 kB | 5.6 kB/s | ETA: 00:00:00 | 100%HarryPotter4.txt | 7 kB | 7.5 kB/s | ETA: 00:00:00 | 100%HarryPotter7.txt | 5 kB | 5.4 kB/s | ETA: 00:00:00 | 100%HarryPotter6.txt | 7 kB | 7.4 kB/s | ETA: 00:00:00 | 100%LOTRFellowship.txt | 4 kB | 5.0 kB/s | ETA: 00:00:00 | 100%apply_store_base.sql | 1 kB | 1.6 kB/s | ETA: 00:00:00 | 100%query_membership.sql | 0 kB | 0.3 kB/s | ETA: 00:00:00 | 100%apply_mysql_lab1.sql | 1 kB | 1.9 kB/s | ETA: 00:00:00 | 100%configure_mysql_web.sql | 37 kB | 37.1 kB/s | ETA: 00:00:00 | 100%seed_mysql_store_ri2.sql | 58 kB | 58.5 kB/s | ETA: 00:00:00 | 100%cleanup_mysql_store.sql | 5 kB | 5.4 kB/s | ETA: 00:00:00 | 100%create_mysql_store_ri2.sq | 21 kB | 21.1 kB/s | ETA: 00:00:00 | 100%Raiders2.png | 99 kB | 99.5 kB/s | ETA: 00:00:00 | 100%LordOfTheRings3.png | 119 kB | 119.8 kB/s | ETA: 00:00:00 | 100%HarryPotter4.png | 103 kB | 103.9 kB/s | ETA: 00:00:00 | 100%Raiders1.png | 92 kB | 92.4 kB/s | ETA: 00:00:00 | 100%Raiders3.png | 123 kB | 123.9 kB/s | ETA: 00:00:00 | 100%LordOfTheRings2.png | 111 kB | 111.7 kB/s | ETA: 00:00:00 | 100%LordOfTheRings1.png | 103 kB | 104.0 kB/s | ETA: 00:00:00 | 100%HarryPotter2.png | 118 kB | 118.7 kB/s | ETA: 00:00:00 | 100%HarryPotter7.png | 150 kB | 150.2 kB/s | ETA: 00:00:00 | 100%HarryPotter3.png | 106 kB | 106.1 kB/s | ETA: 00:00:00 | 100%HarryPotter5.png | 82 kB | 82.5 kB/s | ETA: 00:00:00 | 100%HarryPotter6.png | 129 kB | 129.9 kB/s | ETA: 00:00:00 | 100%HarryPotter1.png | 118 kB | 118.8 kB/s | ETA: 00:00:00 | 100%HarryPotter8.png | 150 kB | 150.9 kB/s | ETA: 00:00:00 | 100%HarryPotter8.txt | 8 kB | 8.5 kB/s | ETA: 00:00:00 | 100%HarryPotter3.txt | 5 kB | 5.8 kB/s | ETA: 00:00:00 | 100%HarryPotter5.txt | 7 kB | 7.9 kB/s | ETA: 00:00:00 | 100%HarryPotter1.txt | 6 kB | 6.6 kB/s | ETA: 00:00:00 | 100%HarryPotter2.txt | 7 kB | 7.8 kB/s | ETA: 00:00:00 | 100%Raiders3.txt | 5 kB | 5.6 kB/s | ETA: 00:00:00 | 100%HarryPotter4.txt | 7 kB | 7.5 kB/s | ETA: 00:00:00 | 100%HarryPotter7.txt | 5 kB | 5.4 kB/s | ETA: 00:00:00 | 100%HarryPotter6.txt | 7 kB | 7.4 kB/s | ETA: 00:00:00 | 100%LOTRFellowship.txt | 4 kB | 5.0 kB/s | ETA: 00:00:00 | 100%apply_store_base.sql | 1 kB | 1.6 kB/s | ETA: 00:00:00 | 100%query_membership.sql | 0 kB | 0.3 kB/s | ETA: 00:00:00 | 100%apply_mysql_lab1.sql | 1 kB | 1.9 kB/s | ETA: 00:00:00 | 100%configure_mysql_web.sql | 37 kB | 37.1 kB/s | ETA: 00:00:00 | 100%seed_mysql_store_ri2.sql | 58 kB | 58.5 kB/s | ETA: 00:00:00 | 100%cleanup_mysql_store.sql | 5 kB | 5.4 kB/s | ETA: 00:00:00 | 100%create_mysql_store_ri2.sq | 21 kB | 21.1 kB/s | ETA: 00:00:00 | 100%My students will need to repeat this step to transfer all of the sample PHP files that demonstrate web application patterns. They also need to inspect individual files to ensure any path referencing commands are manually converted to their new Windows equivalent.They can move the physical files as the root superuser with the following pscp command provide you haven t stored the files somewhere other than the default location:C:\Data\temp pscp -r root@192.168.147.198:/var/lib/mysql .C:\Data\temp pscp -r root@192.168.147.198:/var/lib/mysql .As always, I hope this helps those trying to sort things out.

TAGS:MacLochlainns Weblog 

<<< Thank you for your visit >>>

Michael McLaughlin's Technical Blog

Websites to related :
WordStream: Online Advertising M

  Turn 60% more leads into customers while spending 10% less.WordStream can help make it happen for your small business. For AdvertisersOur software an

Arthur Cox Our practice encompa

  COVID-19: Resource CentreAt Arthur Cox we are very aware of the challenges that so many of our clients face as a result of the outbreak of the COVID-1

UC Davis Arts

  For website accounts, changes, class or event posting on the arts web pages please email:artswebhelp@ucdavis.edu

Music | SUNY Buffalo State Colle

  No events are scheduled at this time. Please check back frequently for upcoming events. Keep Learning The Keep Learning website is your one-stop sh

Upscale Dinner Cruises in Boston

  Please note some cruise elements (such as menus and entertainment) may vary due to COVID-19. For more important information regarding phased reopening

Terrapinn - Spark Something

  EVENTS TRANSFORMING BUSINESS AROUND THE WORLD EXPLORE OUR EVENTS We’ve been sparking ideas, innovations and relationships that transform business for

Driving School in Mountain View

  The most cool, calm and collected driving instructors around. Offering quality driving education at an affordable price for Silicon Valley, only at De

Get Commercial Truck Driver Trai

  CDL Now! Course Get on the road FAST with our CDL Now! Course. In just 4 weeks you’ll train and learn all you need to know to pass the CDL Class A ex

ID Driving School, Inc.

  Welcome Thank you for your interest in ID Driving School, Inc. Learning to drive is an exciting part of your life. It is important to learn the skills

A Club Driving School | Best Dri

  The right driving school is accredited according to state laws and will prepare you to be a safe confident driver. Driving is a serious activity that

ads

Hot Websites