Archive for the ‘MySQL’ Category
Somebody asked how to create a
SYNONYM in MySQL, which is interesting because MySQL doesn’t support synonyms. I thought the prior entry explained how to do it, but here’s a new post. However, you can create a view in one database that relies on a table in another database.
The following SQL statements create two databases and grant appropriate privileges to the
student as the
/* Create two databases. */ CREATE DATABASE seussdb; CREATE DATABASE appdb; /* Grant privileges to a student user. */ GRANT ALL ON seussdb.* TO student; GRANT ALL ON appdb.* TO student;
Log out from the
root superuser and reconnect as the
student user. Then, the following code connects to the
seuss database and create a
hat table; and inserts two rows into the
/* Use the appdb database. */ USE seussdb; /* Create a hat table. */ CREATE TABLE hat ( hat_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT , hat_text VARCHAR(20)); /* Insert two rows into the hat table. */ INSERT INTO hat (hat_text) VALUES ('Thing 1'); INSERT INTO hat (hat_text) VALUES ('Thing 2');
The following code connects to the
application database and creates a
hat view; and then the code inserts one additional row into the
/* Connect to the application database. */ USE appdb; /* Create a hat view. */ CREATE VIEW hat AS SELECT * FROM seussdb.hat; /* Query the contents of the view, or seuss.hat table. */ SELECT * FROM hat; /* Insert a new row into the hat table. */ INSERT INTO hat (hat_text) VALUES ('Thing 3'); /* Query the contents of the view, after insert to the view. */ SELECT * FROM hat;
The results will be the following:
+--------+----------+ | hat_id | hat_text | +--------+----------+ | 1 | Thing 1 | | 2 | Thing 2 | | 3 | Thing 3 | +--------+----------+
I hope this answers the question on how to mimic the Oracle database’s synonyms. The
appdb.hat view acts as a synonym to the
An acquaintance sent me this image from a cyber cafe or hotel in Russia. It says that my blog site is prohibited and violates Russian law, and that they’re blocking my site in accordance with the Russian Federal Law of 27.07.2006 No. 149-FZ.
All I can say is, “Wow!” I didn’t know that stuff about writing programs, web pages, and solving generic database and operating system problems was so sensitive. For that matter, I didn’t know what I post would interest any government. I half wonder whether my friend’s pulling my leg.
As I reflect on it, could it be that Oracle post on how to write an encrypted object type? or, how with proper OS credentials how you reset MySQL’s root password? or, how to verify a socket in Perl? Nah, it’s probably the one on how to set a static IP in Windows – that’s truly sensitive stuff.
Does anybody who reads the blog have any clue about what would drive the Russian government to block a technology blog site like mine? My serious guess is that there is somebody who hosts with the same provider who’s done something tacky.
SQL tidbits are always valuable and highly searched for by newbies (as opposed to reading the SQL documentation). Sometimes we seasoned SQL developers take for granted little things like when a single- or multiple-character wildcard comparison works. It seems we know what newbies don’t. That you need a wildcard comparison operator not simply and equality comparison operator.
The question posed to me was, “Why doesn’t my wildcard comparison work?” Here’s a simplified example of their question.
SELECT 'Valid' AS "Test" FROM dual WHERE 'Treat' = 'Tre_t' OR 'Treet' = 'Tre_t';
Naturally, the answer is that the equality operator compares the strings based on their exact match (character sensitively in Oracle and character insensitively in MySQL). It needs to be rewritten by replacing the equals (
=) comparison operator with the
LIKE comparison operator. The following query does that:
SELECT 'Valid' AS "Test" FROM dual WHERE 'Treat' LIKE 'Tre_t' OR 'Treet' LIKE 'Tre_t'
The same behavior exists for the multiple-character wildcard (
%). I hope this helps those looking for this answer.
I registered yesterday for Oracle OpenWorld 2013, and I’ll look forward to seeing friends there. Having worked in the Oracle 12c beta for a year, I’ll be interested in the presentations. Also, hearing more about Java 7 at JavaOne. On the downside, I’m missing MySQL Connect this year.
Cloud computing offers many possibilities, and container and pluggable databases are a great solution. We’ve two new acronyms with the Oracle 12c release. A containerized database is a CDB, and a pluggable database is a PDB. I’m looking forward to seeing more about the provisioning of PDBs during the conference. If you’re new to the changes, check out CDBs and PDBs in Chapter 17 in the Oracle 12c Concepts manual.
A couple of my favorite new features are Identity and Invisible Columns. If you’re unfamiliar with the new features for application development, let me recommend this Oracle White Paper. Also, for reference I’ve covered identity and invisible columns thoroughly in the Oracle Database 12c PL/SQL Programming book, which will be available in December.
Missing the MySQL Connect 2013 Bus
Unfortunately, travel budgets preclude me attending MySQL Connect 2013 this year (alas, I’ll miss the bus). It was hard because I’d like to see what’s up with MySQL (since I was a closet MySQL user at Oracle before they acquired it). Anyway, if you’re there, make sure you check out MySQL Workbench 6 for me. Also, I’d like to thank Dave Stokes for the AWESOME review he wrote on Amazon.com for my MySQL Workbench: Data Modeling & Development book. Maybe, I’ll get to go to MySQL Connect 2014 next year.
He kindly brought me a copy of my Oracle Database 11g and MySQL 5.6 Developer Handbook in simplified Chinese. He’s holding it in the photo.
That makes three books translated into Chinese, which made my day. It’ll be interesting to see if the new MySQL Workbench: Data Modeling & Development book gets translated into Chinese too. Oddly, I never hear about this from the publisher first.
The cover emphasized only the Dolphin, not the Oracle logo material. It made me wonder, how many MySQL users there might be in China. If anybody from China catches the post, it would be great to hear about the MySQL Community in China.
Likewise, if anybody in China catches the post and reads the book, please let me know if you liked it. Naturally, let me know if you found any problems with it too. By the way, I keep an errata for the book here.
Just went through all my PHP testing against a fresh instance of Oracle with Zend Server Community Edition 6, and found these warnings, guess that’s pretty clean for the Oracle part of the installation. I didn’t notice it before because generally I do most of my PHP development against a MySQL database. I should have been configuring the
php.ini file routinely, as qualified in this PHP forum discussion.
Warning: oci_set_client_info(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 47 Warning: oci_set_module_name(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 48 Warning: oci_set_action(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 69
Turns out Zend 6 doesn’t automatically set the
[Date] elements in the
php.ini file, which is required for the
oci_set_action() functions of the OCI. You can find the
php.ini file in the
C:\Program Files (x86)\Zend\ZendServer\etc folder on Windows:
[Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone ;date.timezone = ; http://php.net/date.default-latitude ;date.default_latitude = 31.7667 ; http://php.net/date.default-longitude ;date.default_longitude = 35.2333 ; http://php.net/date.sunrise-zenith ;date.sunrise_zenith = 90.583333 ; http://php.net/date.sunset-zenith ;date.sunset_zenith = 90.583333
You can find the values for
date.timezone here. Update the
date.timezone as follows:
date.timezone = America/Denver
Then, reboot the Zend Server, and it fixes the warning messages.
Somebody asked why you can’t implement MySQL triggers that write information when you want to stop the DML statement, like autonomous procedures in Oracle. The question was a surprise but I didn’t find anything on it, so here’s how you can do it. This is more or less like an autonomous process by leveraging both the InnoDB and MyISAM engine’s behaviors. This post leverages an earlier explanation of MySQL Triggers.
- First you create a MyISAM table, which is a persistent store that auto commits when you’re other InnoDB tables can be transactionally dependent. Here’s a simple MyISAM
CREATE TABLE logger ( logger_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY , logger_event VARCHAR(50) , logger_table VARCHAR(50) , logger_instring VARCHAR(100) , logger_outstring VARCHAR(100) , created_by INT UNSIGNED , creation_date DATE , last_updated_by INT UNSIGNED , last_update_date DATE) ENGINE=MyISAM;
- Next, you create an on-insert trigger that changes an input but doesn’t stop the transaction. It also writes to the logger MyISAM table in the scope of the transaction.
CREATE TRIGGER contact_insert BEFORE INSERT ON contact FOR EACH ROW BEGIN /* Check if last name contains a white space. */ IF new.last_name REGEXP '^.* .*$' THEN /* Insert into an MyISAM table, which auto commits in the scope of a transaction. */ INSERT INTO logger VALUES ( null ,'insert' ,'contact' , new.last_name , REPLACE(new.last_name,' ','-') , new.created_by , new.creation_date , new.last_updated_by , new.last_update_date ); /* Replace the name for the INSERT INTO the CONTACT table. */ SET new.last_name := REPLACE(new.last_name,' ','-'); END IF; END; $$
- Next, you create an on-update trigger that changes an update while aborting the transaction. It also writes to the logger MyISAM table because its outside the InnoDB scope of a transaction and auto committed on insert.
CREATE TRIGGER contact_update BEFORE UPDATE ON contact FOR EACH ROW BEGIN /* Check if last name contains a white space. */ IF new.last_name REGEXP '^.* .*$' THEN /* Insert into an MyISAM table, which auto commits in the scope of a transaction. */ INSERT INTO logger VALUES ( null ,'update' ,'contact' , new.last_name , null , old.created_by , old.creation_date , new.last_updated_by , new.last_update_date ); /* Throw an exception to force the business user to see they can't update a last name with a white space. */ SIGNAL SQLSTATE '42000'; END IF; END; $$
- Next, you create a test case with an
UPDATEstatement that meets the condition of the triggers.
/* Insert a row meeting the trigger condition. */ INSERT INTO contact VALUES ( null, 1001, 1003,'Catherine', null,'Zeta Jones', 1001, UTC_DATE(), 1001, UTC_DATE()); /* Update a row meeting the trigger condition. */ UPDATE contact SET last_name = 'Zeta Jones' , last_updated_by = 1003 , last_update_date = UTC_DATE() WHERE last_name = 'Zeta-Jones';
- Last, query the logger table. You have a record inserted for both the allowed behavior and the aborted behavior. This means you have the ability to capture material that should never be inserted or updated into a table and who did it by leveraging the who-audit columns of the table.
SELECT * FROM logger;
+-----------+--------------+--------------+-----------------+------------------+------------+---------------+-----------------+------------------+ | logger_id | logger_event | logger_table | logger_instring | logger_outstring | created_by | creation_date | last_updated_by | last_update_date | +-----------+--------------+--------------+-----------------+------------------+------------+---------------+-----------------+------------------+ | 1 | insert | contact | Zeta Jones | Zeta-Jones | 1001 | 2013-04-26 | 1001 | 2013-04-26 | | 2 | update | contact | Zeta Jones | NULL | 1001 | 2013-04-26 | 1003 | 2013-04-26 | +-----------+--------------+--------------+-----------------+------------------+------------+---------------+-----------------+------------------+ 2 rows in set (0.00 sec)
This effectively delivers in MySQL the equivalent of an autonomous transaction in Oracle. The result from the non-critical trigger records the before and after value, while the results from the critical update trigger only record the before values because the event is aborted by raising an error in the trigger. As always, I hope this helps somebody looking for a solution.
My install instructions on the web site were old, somebody wanted me to publish another set of screen capture for the MySQL 5.6 install and configuration. This is it for Windows 7 using the downloadable MSI file.
The installation from MySQL’s perspective is actually the installation and configuration of MySQL. For your convenience and reference, I’ve already installed the pre-requisites for MySQL. They’re:
- Visual Studio Tools for Office 20120 Runtime
- Microsoft .NET Framework 4 Client Profile
- Microsoft Excel 2007 or greater
- Microsoft .NET Framework 4 Client Profile
- Microsoft Visual C++ 2010 32-bit runtime
- Microsoft .NET Framework 4 Client Profile
Below are the installation steps after you download the current release
.msi file. The icon should look like the one to the right. For this example, I”m using the
mysql-installer-community-220.127.116.11.msi. Double-click the icon on your desktop or from your
C:\Users\username\Downloads folder. While working through the steps, you can launch any of the small images to the left if you’d like to see what your screen should look like (generally with a right click to open in a new window).
- The first screen is a Windows 7 dialog box. Click the Run button to install launch the MySQL 5.6 Installer.
- The second screen is a Windows 7 dialog box. It advises you that the MySQL Installer is working and lets you cancel that operation. Don’t click the Cancel button unless you want to stop the MySQL 5.6 Installer.
- The third screen is a MySQL Installer message box. It closes when ready to proceed. Ignore it, unless it’s there for more than a couple minutes. If that occurs you’ve got something wrong with your Windows installation or a very slow computer. If the former, kill the installation process; if the latter, wait patiently.
- The fourth screen in the process is the first MySQL Installer screen. Here you choose what you want to do. You can install MySQL products, inquire about MySQL, or check physical resource components. Provided you installed the prerequisites listed above, you should be prepared to install MySQL Products. Click the Install MySQL Products link to proceed or one of the others to explore.
- The second MySQL Installer screen is the license form. You must check the I accept the license terms checkbox to enable the Next button. Once the Next button is enabled, click it to proceed.
- The third MySQL Installer screen connects to the Internet and finds the latest product update. You can check the Skip the check for updates (not recommended) checkbox to skip this but for the example we’ll check anyway. Click the Execute button to proceed.
- The fourth MySQL Installer screen connects to the Internet and finds the latest product update. You can check the Skip the check for updates (not recommended) checkbox to skip this but for the example we’ll check anyway. Click the Execute button to proceed.
- The fifth MySQL Installer screen acknowledges the latest update is what you’re installing. Click the Next button to proceed.
The sixth MySQL Installer screen gives you five choices for the installation, which are listed below. You most likely want to install the Developer Default, so click the Developer Default radio button and then, click the Next button to proceed.
- MySQL Server
- Both the client and server software for the MySQL Server
- MySQL Workbench
- The GUI application to develop for and manage the server.
- MySQL Visual Studio Plugin
- To work with the MySQL Server from VS.
- MySQL Connectors
- Connector/Net, Java, C/C++, OBDC and others.
- Examples and tutorials
- To help you get started with your development.
- Allows you to read the documentation offline.
- The seventh MySQL Installer screen performs a system check for the pre-requisites, which I listed before the installation. Assuming you installed them, you should see a screen that confirms your system configuration is ready for installation. Click the Next button to proceed.
The eighth MySQL Installer screen performs displays the products that it’ll install, which are listed below and available in the full image to the left. Click the Execute button to install the products.
- MySQL Server 5.6.11
- MySQL Workbench CE 5.2.47
- MySQL Notifier 1.0.3
- MySQL for Excel 1.1.1
- Connector/ODBC 5.2.4
- Connector/C++ 1.1.2
- Connector/C++ 1.1.2
- Connector/J 5.1.24
- Connector/NET 6.6.5
- MySQL Documentation 5.6.11
- Samples and Examples 5.6.11
- The nineth MySQL Installer screen shows the installation by product and it can take a couple minutes. The screen to the left displays progress more than halfway complete. You don’t need to do anything in this step until all products are complete.
- The tenth MySQL Installer screen shows the completed installation. Everything should install successfully, as shown in the image. Click the Next button to proceed.
This concludes the installation of the MySQL products. The next section shows you how to configure MySQL.
You have two basic options, the simple one and the advanced one. These steps will show you how to perform an advanced configuration. I’ve opted to maintain the step numbering from the beginning of the installation. Here are the steps:
- The eleventh MySQL Installer screen is the first MySQL Configuration screen. You can click the Show Details button or begin the configuration. Click the Next button to proceed.
- The second MySQL Configuration screen sets the server configuration type, enables TCP/IP networking (as opposed to a socket model), and lets you enable Advanced Configuration. For this installation, we enable the Show Advanced Options checkbox before you click the Next button.
The third MySQL Configuration screen sets the password and lets you create MySQL User Accounts. It’s much easier to let the install proceed and use MySQL Workbench to create databases, users, and roles; plus grant permissions through the GUI environment. Enter the
rootpassword twice, a trivial and unsecure password
cangetinis what I recommend to my students who won’t have any meaningful information in the database. Make sure you can remember the password you enter. Clearly, a better password is required for real environments. After entering the password twice, click the Next button to proceed.
The fourth MySQL Configuration screen sets the Windows Service Name, and you should probably make sure there isn’t another MySQL56 service on the machine before you proceed. You have the choice of running the Windows Service using the Standard System Account or a Customer User account. Unless you’re an expert at Windows 7 administration, you should probably choose the Standard System Account as the one running the Windows 7 service. Click the Next button.
The fifth MySQL Configuration screen sets the logging options. You only need Show Query Log typically, but the Error Log is helpful. Make the choices and click the Next button.
- The sixth MySQL Configuration screen explains the next step. It installs the sample files and example databases. You can see what you’ve installed when you click the Show Details button, which is what I did to get the image at the left. The default choice installs the samples and example databases, which can’t hurt. You’ll need the test database if you install DBD::mysql for Perl. If you don’t want them, you can drop them from the database.
The seventh MySQL Configuration screen explains you’ve completed configuring the MySQL Server. Click the Next button to proceed.
The eighth MySQL Configuration screen explains you’ve finished everything. You can copy the log file to clipboard, which allows you to see everything that was done. Click the Finish button to complete the installation and configuration.
Just one caveat (that’s a warning), this installation doesn’t put the MySQL executable into your System
%PATH% variable. You’ll need to do that, and if I get a chance I’ll put a post together for that. I know one or two of my students may need it later.
I hope this helps those you are using the new installer for the first time. It’s a superior tool to the old one, which was also a good tool.
While I tried to deflect how you perform SQL Injection attacks against a MySQL procedure, my students requested that I post examples of what to do to avoid SQL injection, and what not to do to invite attacks. The best practice to avoid SQL injection attacks is too always bind inputs to data types, and avoid providing completely dynamic
Here’s the correct way to dynamically generate a result from a MySQL Stored Procedure:
CREATE PROCEDURE hello (IN pv_input VARCHAR(50)) BEGIN SELECT sample_id , sample_name FROM sample WHERE sample_name = pv_input; END; $$
A call to this
hello procedure will only return the row or rows where the
pv_input value matches the
sample_name column value. Any attempt to exploit it like the one below fails.
CALL hello('\'Harriet\' OR 1 = 1');
It fails because there’s no matching
'Harriet' OR 1 = 1 in the table’s
sample_name column. However, it works well when we submit
'Harriet' by herself, without the intended SQL inject phrase “
OR 1 = 1“, as you can see:
+-----------+-------------+ | sample_id | sample_name | +-----------+-------------+ | 2 | Harriet | +-----------+-------------+ 1 row in set (0.00 sec)
There are two approaches that you should never put in your code because they can be readily exploited unless you carefully parse the incoming string parameter. The problem in both cases is causes by concatenation rather than binding. The first example is extremely unlikely as an error but possible.
CREATE PROCEDURE hello (IN pv_input VARCHAR(50)) BEGIN SET @sql := CONCAT('SELECT sample_id, sample_name FROM sample WHERE sample_name = ',pv_input); PREPARE stmt FROM @sql; EXECUTE stmt; END; $$
hello procedure using concatenation and a prepared statement is vulnerable to SQL injection. A call like the following would return all two rows in the
CALL hello('\'Juliette\' OR 1 = 1');
It would display:
+-----------+-------------+ | sample_id | sample_name | +-----------+-------------+ | 1 | Hank | | 2 | Harriet | +-----------+-------------+ 2 rows in set (0.00 sec)
While there’s no
sample_name value of
'Juliette', the “
OR 1 = 1” is true. Therefore, the
SELECT statement filters out nothing and returns all the data from the table. It’s probably clear you’d never do this type of prepared statement inside a stored procedure, but most SQL Injection attacks exploit your scripting language implementation. Unfortunately, bad coding practices can infrequently expose this type of vulnerability; and they typically occur when a junior programmers is following a bad coding example.
A solution with the
WHERE clause as part of the parameter would look like this:
CREATE PROCEDURE hello (IN pv_where VARCHAR(50)) BEGIN SELECT sample_id , sample_name FROM sample pv_where; END; $$
The modified call:
CALL hello('WHERE sample_name = \'Juliette\' OR 1 = 1');
returns all rows from the table.
A solution without the
WHERE clause as part of the parameter would look like the following but it fails during runtime and returns no rows [Updated in response to comment]. The failure has nothing to do with the comment’s dialog on the
CONCAT function, which also added nothing to the example once I tested it.
CREATE PROCEDURE hello (IN pv_where VARCHAR(50)) BEGIN SELECT sample_id , sample_name FROM sample WHERE pv_where; END; $$
It’s simply works only when you provide a “1 = 1″ or other comparison without embedded apostrophes (‘) but fails with embedded apostrophes. That means the following statement fails:
CALL hello('sample_name = \'Juliette\' OR 1 = 1');
but this SQL injection statement works:
CALL hello('1 = 1');
returns all rows from the table.
This example, when you omit the white space also works with embedded strings or numeric operands and an operator:
CREATE PROCEDURE hello (IN pv_where VARCHAR(50)) BEGIN SELECT sample_id , sample_name FROM sample WHEREpv_where; END; $$
It returns all rows with a call like this:
CALL hello('sample_name = \'Juliette\' OR 1 = 1');
My take initially was that it might be a bug, and I logged one (Bug 68903). That’s was a dumb thing to do because
WHEREpv_where simply becomes a table alias in the query.
In conclusion, the first example is a good practice. The other two should never exist! Well, they shouldn’t exist unless you’re parsing the web form inputs vigilantly.
Hope this helps those trying to understand how to avoid SQL injection attacks. Always try to solve dynamic SQL statement problems by binding variables into statements.
I’m off to speak at the Utah Oracle User’s Group Training Days 2013 tomorrow and Thursday. I’m presenting on Oracle Database Triggers and on techniques leveraging PHP and MySQL Striped Views. If you’re not there, you can check this older (but recently updated) post on PHP and MySQL Striped Views.
Hope to see a few folks who use the blog. BTW, I won’t be at Collaborate 2013 this year. Travel budgets are always tight, and this year they’re more than tight.