-
MySQL is basically free for Unix and OS/2 platforms, but Rapid-Q only supports MySQL for Linux and Windows since my resources are limited to such machines. Under Windows, if you like MySQL, then you are required to pay a license fee after a 30 day trial period. Not to be confused with paying me of course, but if you want, I'll oblige. Why did I choose MySQL over other database servers? The reason is fairly simple, yet deserves some explanation anyway. MySQL is free, mostly, and it's multiplatform, which fits into Rapid-Q quite nicely. It's also easier for me to test, and quite simple to implement under Rapid-Q. It's fairly lightweight, fast, robust, and did I mention free? Perhaps in the future, other database servers will be supported, ie. through MyODBC or Window's ODBC.
Required files:
MySQL serverversion 3.22.xx or better
- Rapid-Q MySQL Libraries
- MySQL Client if you don't want to run a server
After installing the MySQL server, test it to make sure it works (read their nice documentation and help files), you'll also need to copy LIBMYSQL.DLL (under Windows) and libmysqlclient.* (under Linux) to your library path directory. C:\WINDOWS\SYSTEM under Windows, and maybe /usr/lib under Linux (you can also just add a library path to the file instead of moving it). These library files are required by Rapid-Q, they should have come with your MySQL distribution, probably in C:\MYSQL\LIB and /usr/local/lib/mysql depending on your installation. If you already have MySQL installed, which is possible, make sure it's up to date, or if you have an SQL Administrator, check with the that person.
- 14.2 Connecting to MySQL
-
The introduction assumed that you wanted to install a MySQL server on your machine, but you can access a MySQL database from anywhere, as long as you have access of course. If you don't want to install a MySQL server (why wouldn't you?) then make sure you have the proper client libraries, ie. LIBMYSQL.DLL. To begin with, I'll assume you have the MySQL server installed. To connect to your local MySQL server is simple enough:
DIM MySQL AS QMYSQL
Host$ = ""
User$ = ""
Password$ = ""
MySQL.Connect(Host$, User$, Password$)
IF MySQL.Connected THEN PRINT "Successfully connected"
Change User$ and Password$ to suit your needs. Host$ can also take the value of "localhost" but is not necessary in this case. Now to connect to a remote MySQL server located, say, in allsql.com
DIM MySQL AS QMYSQL
Host$ = "allsql.com"
User$ = "anonymous"
Password$ = "guest"
MySQL.Connect(Host$, User$, Password$)
IF MySQL.Connected THEN PRINT "Successfully connected"
Most servers will use the standard MySQL port 3306, but if you know the exact port, then you can use the method RealConnect.
DIM MySQL AS QMYSQL
Host$ = "allsql.com"
User$ = "anonymous"
Password$ = "guest"
Port% = 12345
MySQL.RealConnect(Host$, User$, Password$, "", Port%, "", 0)
IF MySQL.Connected THEN PRINT "Successfully connected"
See appendix section for the parameters of QMYSQL.RealConnect. It is suggested by the MySQL documentation that you use RealConnect instead of the using Connect.
- 14.3 How to perform queries and obtain the results
-
Write your queries like you would normally, for example, if you want to select everything from a certain table, say you have a table named user, then you can do this:
DIM MySQL AS QMYSQL
'' First connect to server
'' Use database
MySQL.SelectDB("mysql")
'' Perform query
MySQL.Query("select * from user")
The result of this query is stored internally, and sequentially. To obtain the result, you'll need to Fetch it. You can visualize a 2 dimensional table:
Since the result is stored sequentially, you have to fetch each row one at a time (not necessarily true, but assume we have no other choice). To do this is quite simple:
WHILE MySQL.FetchRow '' Get next row
MySQL.FieldSeek(0) '' Reset field position
FOR I = 0 TO MySQL.FieldCount-1
PRINT MySQL.Row(I) '' Read current row element I
NEXT
WEND
MySQL.FetchRow fetches the next row. As long as there are still rows available to fetch, MySQL.FetchRow returns TRUE (non-zero number). MySQL.FieldCount is used to count how many fields per row there are in the current table. The rest is self explanatory.
- 14.4 Interfacing QMYSQL with QSTRINGGRID
-
As you may notice, it would be nice if the results could just be preloaded into a string grid instead of having to fetch everything and doing it manually. Unfortunately I haven't got any motivation to do this right now, but you can use this simple code until I get off my butt and implement it internally:
SUB ExecuteQuery(MySQL AS QMYSQL, Grid AS QSTRINGGRID, Query AS STRING)
IF MySQL.Query(Query) = 0 THEN
'' Failed to execute
EXIT SUB
END IF
DEFINT I = 0, J
''-- Write header
Grid.ColCount = MySQL.FieldCount
WHILE MySQL.FetchField
Grid.Cell(I,0) = MySQL.Field.Name
I++
WEND
Grid.RowCount = MySQL.RowCount+1
J = 1
WHILE MySQL.FetchRow '' Get next row
MySQL.FieldSeek(0) '' Reset field position
FOR I = 0 TO MySQL.FieldCount-1
Grid.Cell(I,J) = MySQL.Row(I)
NEXT
J++
WEND
END SUB
''-- Example
ExecuteQuery(MySQL, MyGrid, "select * from user")
Obviously, you can interface QMYSQL with any component, or components, you want.
- 14.5 Storing and retrieving blobs
-
Blobs are just binary data that require special processing. Since blobs can contain NULL characters and other special characters that affect MySQL and Rapid-Q, you'll need to do some special processing with your binary data. Please note that blob sizes vary, depending on the field type. A BLOB, by default can hold up to 65535 bytes (a LONGBLOB should be able to handle 2^32-1 bytes). If you want to change the buffer size, you'll have to read up on it (max_allowed_packet). To store binary data, like images, you'll have to preprocess certain characters before you can send it over to your MySQL database.
$INCLUDE "RAPIDQ.INC"
DIM MySQL AS QMYSQL
''-- Connect to database code, etc...
DIM File AS QFILESTREAM
File.Open("test.bmp", fmOpenRead)
''-- Read file as binary, preserves NULL chars
Buffer$ = File.ReadBinStr(File.Size)
''-- Here's the special processing
''-- Converts NULLs to \0 and others
ProcessedBuffer$ = MySQL.EscapeString(Buffer$, File.Size)
''-- Insert blob in our table
MySQL.Query("insert into MyImageTable values('Label 1', '" + _
ProcessedBuffer$ + "')")
File.Close
MySQL.Close
Since we're dealing with binary data, you have to be aware that we don't use LEN() or any other STRING processing functions with our binary data. Note that we can store text data in blob fields, in which case you can use any STRING processing functions you want. You may notice that MySQL.EscapeString converts the binary data to a usable format that can be passed to the MySQL database. Basically all it does is convert NULLs to '\0', ' to \', " to \", and CRLF to \r and \n respectively. Once parsed, the new string is returned which you can use in a query. So far so good, now we consider retrieving our blobs:
$INCLUDE "RAPIDQ.INC"
$INCLUDE "MYSQL.INC"
DIM MySQL AS QMYSQL
''-- Connect to database code, etc...
DIM File AS QFILESTREAM
File.Open("out.bmp", fmCreate) ''-- Create new file
''-- Your query here
MySQL.Query("select * from MyImageTable")
WHILE MySQL.FetchRow
MySQL.FieldSeek(0)
MySQL.FetchLengths ''-- Required for binary data
FOR I = 0 TO MySQL.NumField-1
MySQL.FetchField
IF MySQL.Field.Type = FIELD_TYPE_BLOB THEN
''-- Read binary data from table
Buffer$ = MySQL.RowBlob(I, MySQL.Length(I))
File.WriteBinStr(Buffer$, MySQL.Length(I))
ELSE
Buffer$ = MySQL.Row(I)
END IF
NEXT
WEND
File.Close
MySQL.Close
Retrieving blobs from the database is a little more confusing than storing blobs. The first thing you have to note is that reading binary data and text strings are 2 different operations. Since text strings are NULL terminated, you don't have to worry about how many bytes to read, this is automatically calculated for you. However, since binary data can contain NULL characters, this makes reading blobs a little bit tricky. You have to fetch the lengths of each field, since we have blobs in our table, this is done by calling MySQL.FetchLengths for each row. Then to find out the length of each field, you use the internal array MySQL.Length(index%) which returns the length for field index%. Why do we have to do this? Because we need to know the size of our blob before we can read it in. To read binary data from our database, you use the specialized function MySQL.RowBlob(Row%, Bytes%) which returns a binary string. With this binary string, you can write the data to a file or memory, or manipulate the data directly. In the above example, we're just writing the data to a file.
- 14.6 Using LOADBLOB and SAVEBLOB
-
In the previous section, we uncovered the natural way of storing and retrieving blobs (binary data). However, there is an easier and faster way of doing the samething.
''-- Recall the natural way
ProcessedBuffer$ = MySQL.EscapeString(Buffer$, File.Size)
MySQL.Query("insert into MyImageTable values('Label 1', '" + _
ProcessedBuffer$ + "')")
''-- Using LOADBLOB instead
ProcessedBuffer$ = MySQL.LoadBlob("test.bmp")
MySQL.Query("insert into MyImageTable values('Label 1', '" + _
ProcessedBuffer$ + "')")
Using LOADBLOB eliminates the use of MySQL.EscapeString since it is just implemented internally for you. It simply takes a single argument (the name of the file to include), and returns the processed string. To retrieve a blob from the database and save it to a file, you can try this:
WHILE MySQL.FetchRow
MySQL.FieldSeek(0)
'' MySQL.FetchLengths
FOR I = 0 TO MySQL.NumField-1
MySQL.FetchField
IF MySQL.Field.Type = FIELD_TYPE_BLOB THEN
''-- Save data to file
MySQL.SaveBlob(I, "temp.bmp")
ELSE
Buffer$ = MySQL.Row(I)
END IF
NEXT
WEND
In this instance we can ignore using MySQL.FetchLengths since it is, again, implemented internally for you. MySQL.SaveBlob takes two arguments. The first is the index (or field number if you will), and the second argument is the name of the file to save to. There are no return values associated with this function.
- 14.7 In conclusion
-
Using MySQL can save you a lot of time and effort, since you don't need to design your own database file structure, and searching is quite fast. Not to mention that MySQL is multiplatform, so you can use MySQL with Windows and Linux. If you already have prior knowledge with programming in MySQL, you'll probably notice some differences. For example,
I = MySQL.SelectDB("mysql")
Under Rapid-Q, this function returns 0 if database cannot be opened, and a non-zero number otherwise. However, if you've used LIBMYSQL.DLL before, this function (called mysql_select_db) returns 0 on success and -1 on failure. So basically, just reverse your way of thinking when using Rapid-Q.
Prev ChapterContentsNext Chapter