Use the Ruby DBI module
Original Paul Dubois Paul@kitebird.com
Translation: Liubin 2004/11/9 http://www.ruby-cn.org/ Original Address: http://www.kitebird.com/articles/ruby-dbi.html
Document version: 1.02 Last updated: 2003-05-27
table of Contents
Prepare preparation to install a simple DBI script processing query
Processing the query processing of the result set Returns the results set query reference, placeholders, and parameter binding query metadata (Methods Take Code Blocks) Re-Connecting Database Error Processing and Debugging Transaction Processing Support for some useful DBI modules and tool resources using DRIVER-Specific Capabilities)
Sequencement
The Ruby DBI module provides an interface-independent interface that is unrelated to the database, just like the DBI module of Perl. This article will tell how to write DBI-based Ruby programs. This article is complementary to the SPI specification document, not to replace the specification document, see "Resources" section.
Ruby's DBI module architecture is divided into two layers:
Database interface layer (Database Interface DBI). This layer is unrelated to the database, which provides some unrelated access methods that are not related to the database you specifically used. . Database Drive DBD. This layer is related to the database, different drives to access different databases. A driver is used to access mysql, another use to access PostgreSQL, each particular database has different drivers. Each driver interprets the request of the DBI layer, and converts to a request corresponding to the specific database, sent to the database. The database used in this article is mysql, but most of the other databases can be applied.
ready
The Ruby DBI module includes code that implements general DBIs, and some DBD layers, many of these drivers require you to install additional software. For example, the driver for mysql uses Ruby writes, binds with the Ruby MySQL module, and the Ruby MySQL drive is written in the C language to help the mysql C language client API. That is to say, if you want to use DBI to access the mysql database, both of the Ruby MySQL modules, and C APIs need to be installed. See the "Resources" section for more information on the Ruby MySQL module. Here we assume that you have installed Ruby MySQL and can be used for DBI.
installation
Once you meet the previous conditions, you can install the Ruby DBI module, you can get from here:
http://ruby-dbi.sourceforge.net/
The DBI module is published in a compressed TAR format. After the download, it should be decompressed. For example, the current version is 0.0.19, as follows, can be decompressed:
% TAR ZXF Ruby-dbi-all-0.0.19.tar.gz
% gunzip After decompression, enter the top-level directory of the package, configure with the setup.rb script. The general configuration command is like this, there is no parameters behind the config: % Ruby Setup.rB Config This command sets the default installation of all drivers, and more efficiently is to add -with parameters behind the CONFIG, specify the part you need to install. For example, in order to configure only the main DBI module and MySQL DBD driver, run the following command:% ruby setup.rb config --with = DBI, DBD_MYSQL Once you have the software you want to install, you can build and install it: % Ruby Setup.rb Setup % Ruby Setup.rb Install Run install needs root privileges. The following sections will be used in the following representations: "DBI Module" refers to a collection including a DBI layer and a DBD layer unless the context illustrates that this layer is independent of the database. "DBD :: mysql" refers to the MYSQL database driver for DBI. "Ruby MySQL Module" refers to the base module for building DBD :: mysql. A simple DBI script After installing the Ruby DBI module, you can access the mysql database in your Ruby program. Suppose our database is running in this unit, that is, localhost, the database name is TEST, passing by a user name Testuser, the password is a TestPass user access. We can log in to the mysql program with root, then perform the following command to establish such a user: MySQL> Grant All on test. * to 'testuser' @ 'localhost' identified by 'TestPass' If the Test database does not exist, create it with the following command: Mysql> Create Database Test; If you want to use different databases, servers, users, and passwords, only need to change the values in the example to your own. Below this script, Simple.rb is a very short DBI program that is connected to the database, then query the version of the database and display it, and then disconnect. You can download this code from the link provided in "Resources" or copy it into the text editor: # Simple.rb - Simple MySQL Script Using Ruby DBI Module Require "DBI" Begin #connect to the mysql server DBH = DBI.CONNECT ("DBI: Mysql: Test: Localhost", "Testuser", "Testpass") # Get Server Version String and Display IT Row = dbh.select_one ("SELECT VERSION ()") PUTS "Server Version:" Row [0] Rescue dbi :: databaseError => e PUTS "an error occurred" Puts "Error Code: # {E.ERR}" PUTS "Error Message: # {e.errstr}" Ensure # disconnect from Server DBH.Disconnect IF DBH end Simple.rb exhibits some of the most basic concepts of DBI. The following discussion will tell him how he works, and then more about other aspects of DBI. Simple.rb starts with a row of Require, introducing the DBI module recently; if this line does not, the DBI method will be wrong, and the following code is included in a Begin / Rescue / EnSure structure: the Begin section handles all database requests. The rescue section is used to handle the error message, which will get the error message and display it. EnSure Block Make sure that the program will close the database connection regardless of whether the error is wrong. method Connect is used to establish a connection with the database server and return to this connection. The first parameter is the data source name (Data Source Name DSN), which specifies the driver name (MySQL for mysql servers), the default database name and the server name, second, and three parameters are users and passwords. There are other DSN Writings, which will be described later in the "Connection Database" later. SIMPLE.RB calls method select_one with database handle, which sends a query statement to the server and returns the first line of the result set as an array to the caller. "SELECT VERSION ()" returns to returns a single value, so version information will exist in Row [0], which is the first element of this array is also the only element. Run this program, the result is like this: % Ruby Simple.rb Server Version: 4.0.13-log If an error, it will result in an exception, exception may be various, but most of them belong to the database error, mostly DatabaseError is abnormal, this exception object includes ERR and Errstr property, Err is an error number, Errstr is an error message. Simple.rb gets these exception values and prints them, but ignores other exceptions, if other exceptions appear, the Ruby execution environment will be thrown. Simple.rb is disconnected from the Disconnect method, which is executed in EnSure, so that the database connection will be disconnected regardless of the error or not. Processing query Ruby DBI provides many ways to perform query statements. This will be discussed here, but there are other things. Most examples use table people, which are as follows: Create Table People ( ID INT UNSIGNED NOT NULL AUTO_INCREMENT, # id Number Name char (20) Not null, # name Height float, # height in inches Primary Key (ID) ); Processing does not return the result set If a statement does not need to return a result, you can use the DO method of the database sector, the parameter of this method is the SQL statement to be executed, and the number of rows that are affected are returned. The following example creates a table person and insert several records, and uses the DO method: DBH.DO ("Drop Table IF EXISTS People") DBH.DO ("Create Table People (ID INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY Key (ID), Name Char (20) NOT NULL, Height Float) ROWS = DBH .do ("INSERT INTO PEOPLE (Name, Height) Values ('Wanda', 62.5), ('Robert', 75), ('Phillip', 71.5), ('Sarah', 68)") Printf "% D Rows WERE INSERTED / N ", rows need to be aware that the INSERT statement returns a value, that is, the number of rows inserted, and print it out. Processing the query of the return result set Like SELECT and SHOW, the statement is to return to the line record, to process such a statement, first submit a query to the server, process each record generated by the query, and then set the result. One way is to call prepare to generate a Statement handle, use this handle to perform the query, retrieve the result, then release the result set: Sth = dbh.prepare (statement) Sth.execute ... Fetch Rows ... Sth.finish Or send the statement directly to the database connection handle to execute without call prepare: Sth = dbh.execute (statement) ... Fetch Rows ... Sth.finish There are also many ways to achieve results from the executed statement, and you can call the FETCH method in a loop until NIL is returned: Sth = dbh.execute ("SELECT * from person") While Row = sth.Fetch Do Printf "ID:% D, Name:% s, Height:% .1F / N", Row [0], Row [1], ROW [2] end Sth.finish Fetch can also be used as an Iterator to use, Each. method. The following two are the same role: Sth = dbh.execute ("SELECT * from person") Sth.Fetch Do | ROW | Printf "ID:% D, Name:% s, Height:% .1F / N", Row [0], Row [1], ROW [2] end Sth.finish Sth = dbh.execute ("SELECT * from person") Sth.each Do | ROW | Printf "ID:% D, Name:% s, Height:% .1F / N", Row [0], Row [1], ROW [2] end Sth.finish Fetch and Each is produced DBI :: ROW object, this object provides a way to access them: You can use BY_INDEX or BY_FIELD to access the field value in order or name: Val = row.by_index (2) Val = row.by_field ("HEIGHT") Field values can also get the ROW object as an array: Val = row [2] Val = row ["height"] iterative method Each_with_name Generates each field name and their values: Sth = dbh.execute ("SELECT * from person") Sth.each Do | ROW | Row.each_with_name do | Val, Name | Printf "% s:% s,", name, val.to_s end Print "/ N" end Sth.finish The DBI :: ROW object provides a method column_names to get an array containing each field name. Field_names is the alias of column_names. Other methods of returning row data include Fetch_Array and Fetch_hash, they don't return DBI :: ROW object, but will return the next line of data as an array or hash, and if the last thing of the result set is also returned, NIL will return. Fetch_hash returns the hash structure, and the column name is the value corresponding to this key. These two methods can be used independently or in iterations. The following example uses the Hash method: Sth = dbh.execute ("SELECT * from person") While row = sth.Fetch_hash Printf "ID:% D, Name:% S, Height:% .1f / N", Row ["ID"], row ["name"], row ["height"] end Sth.finish Sth = dbh.execute ("SELECT * from person") Sth.Fetch_hash DO | ROW | Printf "ID:% D, Name:% S, Height:% .1f / N", Row ["ID"], row ["name"], row ["height"] end Sth.finish You can also perform your statement without in accordance with the "Query - Take Results - Complete", the database segment can retrieve all the results at a time: Row = dbh.select_one (statement) Rows = dbh.select_all (statement) SELECT_ONE performs a query and then returns the first line of the result as an array, or returns NIL if there is no matching. SELECT_ALL returns one DBI :: ROW array, (you can use the previously discussed method to access the content inside). If there is no match, return an an array. Be care not NIL. MySQL driver checks the metadata (metadata) of the returned result set, then enforces the value of this field to the corresponding Ruby type (for example, the value of the ID, Name, the value of the Height field) will be converted to Fixnum String and float objects). However, if the value of a column is NULL, use nil to represent, and its type is nilclass. There is also this is not the hard regulation of the DBI specification manual, so some drivers may not do such a job. Quote, placeholder and parameter binding The Ruby DBI provides a placeholder mechanism that allows you to write the literal value of the data value in the query statement, but use some special symbols to mark the location of the data, when you really want to execute, use true The data value fills the location of the occupancy. DBI will replace the placeholder with data values, complete the extension number of the string, the escape of special characters (if needed), etc., without having to do itself, and the placeholder mechanism can deal with NULL value, You only need to provide a NIL value, which will be automatically replaced into NULL in the query. The following example explains how it works. Join you to insert a record in the People table, this person's name is Na'il, this name includes a single quotes, his height is 76 inches. In the query statement, use? As a placeholder inserted, it is not required to be quoted, and then the value to be inserted as a DO parameters, as follows: DBH.DO ("INSERT INTO People (ID, Name, Height) VALUES (?,?,?)", NIL, "Na'il", 76) This statement is sent to the database, like this: INSERT INTO People (ID, Name, Height) Values (NULL, 'NA /' IL ', 76) This is more suitable for you to execute a query multiple times, you can make a prerequisite statement statement, then use the data value to fill it. If the data to be imported is in the text file people.txt, each line is split with Tab, consisting of Name, Height two columns, the following code demonstrates how to read data from the data file, then execute the INSERT statement to insert each line into the database : # Prepare Statement for Use with Insert Loop Sth = dbh.prepare ("INSERT INTO PEOPLE (ID, Name, Height) VALUES (?,?,?)") # ied Each Line from File, Split Into Values, And Insert Into Database f = file.open ("people.txt", "r") F.each_Line DO | LINE | Name, Height = line.chomp.split ("/ t") Sth.execute (NIL, Name, Height) end f.close Generate a preprocessing statement, then execute it multiple times in the loop, more effective than using a loop, mainly because the database can generate an execution plan for the pre-processing statement, and each execution will use this execution plan to execute Increase efficiency. Of course, MySQL currently does not support this feature, Oracle support. If you want to use a placeholder to perform a SELECT statement, you should first consider whether to use the pre-processed statement: If you get a Statement handle with the prepare method, perform the query with this handle and provide the data value fill placeholder: Sth = dbh.prepare ("SELECT * from people where name =?") Sth.execute ("na'il") Sth.Fetch Do | ROW | Printf "ID:% D, Name:% s, Height:% .1F / N", Row [0], Row [1], ROW [2] end Sth.finish If you don't have to prepare the first parameter of the Execute method is the statement to be executed, the back parameters are the data value to be filled with: sth = dbh.execute ("SELECT * from people = =?", "Na'il ") Sth.Fetch Do | ROW | Printf "ID:% D, Name:% s, Height:% .1F / N", Row [0], Row [1], ROW [2] end Sth.finish Other drivers may need to use different placeholders, such as you may need to write : Name or : n is indicated whether it is corresponding to the name or according to the location. Method Quote can handle special characters, escape, etc. in a value, and return to this result. This applies to generating SQL statements for use, for example, you want to convert the contents of the top txt file to a group of INSERT statements that can be executed in the mysql command line, only need the following procedures: # ied each line from file, split into values, and write insert statement f = file.open ("people.txt", "r") F.each_Line DO | LINE | Name, Height = line.chomp.split ("/ t") Printf "INSERT INTO People (ID, Name, Height) VALUES (% s,% s,% s); / n", dbh.quote (nil), dbh.quote (name), dbh.quote (height) end f.close Query Metadata (Metadata) For statements that do not need to return results, such as INSERT, DELETE, etc., the DO method returns the number of lines of INSERT or DELETE. For queries that return the results, such as SELECT, you can use the Statement handle with the statement handle after the Execute method, and the information of each column: The number of rows and columns cannot be obtained directly. In order to get the number of lines, you can loop the time to count each line, or put the result in a data structure, then look at how many elements have this data structure. To get the number of columns returned, you can get from sth.column_names.size. Methods Column_info returns detailed information on each column. The following example shows how to get Metadata from a query: Sth = dbh.execute (query) PUTS "Query:" Query if sth.column_names.size == 0 THEN PUTS "Query Has No Result Set" Printf "Number of Rows Affected:% D / N", Sth.Rows Else PUTS "Query Has A Result Set" Rows = st.fetch_all Printf "NUMBER OF ROWS:% D / N", Rows.Size Printf "Number of Column:% D / N", sth.column_names.size Sth.column_info.each_with_index do | info, i | Printf "--- Column% D (% s) --- / n", i, info.name Printf "precision:% s / n", info.precisionprintf "scale:% s / n", info.scale end end Sth.finish Note: The earlier version of this document says that you can get the number of rows returned from sth.rows, and now they are not supported. (Although it can now be used in MySQL driver, you should not rely on this function) Method for accepting code blocks (Methods Take Code Blocks) Some methods that generate handles can be used to call in the block, when using this method, they provide the handle as a parameter to the Block, and automatically destroy these handles after the block is completed. Dbi.connect produces a database handle (DITABASE HANDLE), which is called Disconnect after the block is completed. DBH.PRepare generates a statement handle, after the end, automatically calls the finish method, inside the block, you must call the Execute method to perform the statement. Dbh.execute is also similar to the above, but you don't need to call the Execute method inside the block, and Statement will execute automatically. The following example illustrates the three problems above: # Connect Can Take a Code Block, Passs The Database Handle To IT, # and automatically disconnects the handle at the end of the block DBI.Connect ("DBI: Mysql: Test: Localhost", "Testuser", "TestPass") DO | DBH | # Prepare Can Take a Code Block, Passes The Statement Handle # To it, and automatically calls finish at the end of the block DBH.PREPARE ("Show Databases") Do | Sth | Sth.execute PUTS "Databases:" sth.Fetch_all.join (",") end # Execute Can Take a Code Block, Passs The Statement Handle # To it, and automatically calls finish at the end of the block DBH.EXECUTE ("Show Databases") DO | STH | PUTS "Databases:" sth.Fetch_all.join (",") end end There is also one The Transaction method can receive a block that will be discussed in "Transaction Support" below. Agglomery connection database The simple.rb script discussed earlier is connected to the database server with DBI's CONNECT method: DBH = DBI.CONNECT ("DBI: Mysql: Test: Localhost", "Testuser", "Testpass") The first parameter DSN of Connect, which indicates that the type of connection is to be connected, and the back parameters are the username and password. The DSN can be one of the following formats: DBI: driver_name DBI: driver_name: db_name: Host_name DBI: driver_name: key = val; key = val ... DSN always starts with DBI or DBI (without both uppercase and lowercase letters) and driver name, the drive name is mysql, for other drivers, you need to specify the correct name. DBI (or DBI) must be available in the DSN, and if there is no other information behind the driver, the drive will try to connect to the database with the default database and the machine name. The mysql requires that the database name must be specified, so the first write method above cannot be used for mysql, and other ways must be used. The second write method requires two values, a database name, a machine name, two values separated by colon. The third format allows a series of parameters to be specified in param = value format, and the parameters are divided, for example, the following three ways are completely equivalent: DBI: MySQL: Test: Localhost DBI: MySQL: Host = localhost; Database = TEST DBI: MySQL: Database = Test; host = localhost Use in DSN syntax PARAM = The Value format is flexible, and the location of each parameter can be set at will. And some unique parameters for different drives can be set, that is to say, it can be expanded in terms of the parameters it receive. For example, mysql, in addition to the Host and Database parameters, you can also set parameters such as Port, Socket, Flag. (These parameters correspond to each parameter in the REAL_CONNECT method of the Ruby MySQL module, while dbd :: mysql is also based on this Ruby MySQL module) Error handling and debugging If a DBI method is white, an exception will be thrown. The DBI method can throw several exceptions, but the database is related to the database generally throws the DatabaseError exception, which has three attributes, err, errstr, and state. DBI's documentation does not say what these three attributes are meaning, but it seems to indicate the error number, a string type error description and some "standard" error code. At present, MySQL drivers only support Errstr, but it is easy to use patches to support ERR attributes. Assume that these two attributes are available, then the following method explains how to get these values: Rescue dbi :: databaseError => e PUTS "an error occurred" Puts "Error Code: # {E.ERR}" PUTS "Error Message: # {e.errstr}" Tracking can be used in order to get debugging information when your statement is executed. To like this, first you have to load the DBI / TRACE module: Require "DBI / Trace" Module DBI / TRACE is not included in the DBI module by default because it requires 0.3.3 The above version of the ASPECTR module, which may not exist on your machine. The DBI / Trace module provides a TRACE method that can be used to control tracking mode and output target: Trace (Mode, DESTINATION) The Mode value is 0 (OFF), 1, 2, 3, the default is 2; Destination is an IO object, default is STDERR. Trace can be called as a class method, so that the handle that is then created can be used; or as a separate driver, a database, a STATEMENT handle object method, any subclass of these objects can inherit these tracking settings. For example, if you allow a database handle to track, the STATEMENT handle created from this handle also has the same tracking setting. Transaction support DBI provides transaction support, but how to support the implementation of your underlying database and DBD layer database drive. For example, MySQL drivers are available before DBI 0.0.19, so you have to use Statement's automatic submission function to achieve the same purpose, such as: dbh.do ("set autocmit = 0") dbh.do ("begin") ... Statements That Make Up The Transaction ... dbh.do ("commit") For DBI 0.0.19 and later, you can use MySQL transaction control, you can set the database handle to set whether to submit: DBH ['AutoCommit'] = TRUE DBH ['AutoCommit'] = false When automatic submission is banned, you have two ways to implement transaction control. The following example illustrates these two methods, a table account, to implement a transactional operation in the fund transfer of two people: The first is the commit or cancel transactions displayed by DBI's commit and rollback method: DBH ['AutoCommit'] = false Begin dbh.do ("Update Account Set Balance = Balance - 50 WHERE Name = 'BILL' ") Dbh.do ("Update Account Set Balance = Balance 50 WHERE Name = 'bob' ") dbh.commit Rescue PUTS "Transaction Failed" DBH. ROLLBACK end The second method uses the Transaction method. This method is very simple. It accepts a process block that requires business operations, the Transaction method executes this block, and then the execution result is successful or failed to automatically perform the commit or rollback. DBH ['AutoCommit'] = false DBH.TRANSACTION DO | DBH | dbh.do ("Update Account Set Balance = Balance - 50 WHERE Name = 'BILL' ") Dbh.do ("Update Account Set Balance = Balance 50 WHERE Name = 'bob' ") end DRIVER-Specific Capabilities (DRIVER-Specific Capability) DBI provides a FUNC method, you can perform different database-driven features, for example, the MySQL C API provides a mysq_insert_id () method, which returns the latest value of Auto_InCrement. The Ruby MySQL module provides a function that binds to this function: database handle INSERT_ID method. This method is provided in dbd :: mysql so that you can access by DBI. The first parameter of Func is the name of the database unique to the database you want, the back parameter is the parameter of this database unique method, and if there is no parameters, you can not fill. INSERT_ID does not have a parameters, so want to access the latest auto_increment value, you can: DBH.DO ("INSERT INTO People (Name, Height) Values ('Mike', 70.5)") ID = dbh.func (: INSERT_ID) PUTS "ID for new record is:" id.to_s Other methods provided by DBD :: mysql include: DBH.FUNC (: createdb, db_name) Create a database DBH.FUNC (: DropDB, DB_NAME) Remove the database DBH.FUNC (: Reload) Re-load (Reload) DBH.Func (: shutdown) Close the database Note Yes, only your MySQL version is available in 4 or more, you can create a database and delete the database. Sometimes, the use of database-specific methods can be particularly a bit, even if the usual other methods can achieve the same role. For example, the functionality of dbd :: mysql's INSERT_ID method and execution query statement "SELECT LAST_INSERT_ID ()", all returns the same value, but INSERT_ID is more effective because it saves this value in the client, do not repeat it again Perform a lookup. This value changes after each new insertion, so you have to reomete this auto_increment value. In contrast, the result of the last_insert_id () is saved on the server, so it is persistent, it does not change because of the other query statements. Some useful DBI modules and tools Module DBI :: Utils contains some other interesting methods (including methods in submodules): DBI :: Utils :: Measure Accept a block, then calculate how long it takes to execute this block: Elapsed = dbi :: utils :: measure do dbh.do (query) end PUTS "Query:" Query Puts "Elapsed Time:" ELAPSED.TO_S Module DBI :: Utils :: TableFormatter method ASCII is used to print a result set (including the header), the first parameter is an array containing the column name, and the second parameter is an array of ROW objects. In order to print the contents of the table, you can use the following code: Sth = dbh.execute ("SELECT * from person") Rows = st.fetch_all COL_NAMES = sth.column_names Sth.finish DBI :: Utils :: TableFormatter.ascii (col_names, rows) The output is as follows: ---- -------- -------- | ID | NAME | Height | ---- -------- -------- | 1 | WANDA | 62.5 | | 2 | ROBERT | 75.0 | | 3 | Phillip | 71.5 | | 4 | Sarah | 68.0 | ---- -------- -------- Modules DBI :: Utils :: XMLFormatter Contains Row and Table methods to output a line or data of the entire result set with the XML format. This makes it easy to generate an XML document from the database. The following example demonstrates the Table method: DBI :: Utils :: xmlformatter.table (dbh.select_all ("select * from mind")) The output is as follows: XML Version = "1.0" encoding = "UTF-8"?> row> row> row> row> rows> method ASCII and Table supports more parameters to provide more control and more formats and output methods for results, you can see the source code for this module for more information. Resource The script used in this article can be downloaded from the address: http://www.kitebird.com/articles/ You can also find another article "Use Ruby MySQL Module", this article discusses the Ruby MySQL module based on DBD: mysql. You will find that the following additional resources is very helpful to your use of Ruby DBi: You can get the Ruby DBI module and specification manual from DBI SourceForge: http://ruby-dbi.sourceforge.net/ If you want to use DBI / Trace modules to perform TRACING with DBI, you must install the AspeCtr Ruby module. Aspectr can get in SourceForge: http://aspectr.sourceforge.net/ Ruby Homepage provides information about Ruby: http://www.ruby-lang.org/ You can get a mysql database from here: http://www.mysql.com/