1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
module DBI::DBD::Mysql
#
# Models the DBI::BaseStatement API to create DBI::StatementHandle objects.
#
class Statement < DBI::BaseStatement
include Util
def initialize(parent, handle, statement, mutex)
super(nil)
@parent, @handle, @mutex = parent, handle, mutex
@params = []
@prep_stmt = DBI::SQL::PreparedStatement.new(@parent, statement)
end
#
# See DBI::BaseStatement#bind_param. This method will also raise
# DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
# binding.
#
def bind_param(param, value, attribs)
raise InterfaceError, "only ? parameters supported" unless param.is_a? Fixnum
@params[param-1] = value
end
#
# See DBI::BaseStatement#execute. If DBI thinks this is a query via DBI::SQL.query?(),
# it will force the row processed count to 0. Otherwise, it will return
# what MySQL thinks is the row processed count.
#
def execute
sql = @prep_stmt.bind(@params)
@mutex.synchronize {
@handle.query_with_result = true
@res_handle = @handle.query(sql)
@column_info = self.column_info
@current_row = 0
@rows = DBI::SQL.query?(sql) ? 0 : @handle.affected_rows
}
rescue MyError => err
error(err)
end
def finish
@res_handle.free if @res_handle
rescue MyError => err
error(err)
end
#
# Helper method to aid #fetch. Do not call directly.
#
def fill_array(rowdata)
return nil if rowdata.nil?
return rowdata.dup
end
def fetch
@current_row += 1
fill_array(@res_handle.fetch_row)
rescue MyError => err
error(err)
end
#
# See DBI::BaseStatement#fetch_scroll. These additional constants are also supported:
#
# * DBI::SQL_FETCH_PRIOR: Fetch the row previous to the current one.
# * DBI::SQL_FETCH_FIRST: Fetch the first row.
# * DBI::SQL_FETCH_ABSOLUTE: Fetch the row at the offset provided.
# * DBI::SQL_FETCH_RELATIVE: Fetch the row at the current point + offset.
#
def fetch_scroll(direction, offset)
case direction
when DBI::SQL_FETCH_NEXT
@current_row += 1
fill_array(@res_handle.fetch_row)
when DBI::SQL_FETCH_PRIOR
@res_handle.data_seek(@current_row - 1)
fill_array(@res_handle.fetch_row)
when DBI::SQL_FETCH_FIRST
@current_row = 1
@res_handle.data_seek(@current_row - 1)
fill_array(@res_handle.fetch_row)
when DBI::SQL_FETCH_LAST
@current_row = @res_handle.num_rows
@res_handle.data_seek(@current_row - 1)
fill_array(@res_handle.fetch_row)
when DBI::SQL_FETCH_ABSOLUTE
@current_row = offset + 1
@res_handle.data_seek(@current_row - 1)
fill_array(@res_handle.fetch_row)
when DBI::SQL_FETCH_RELATIVE
@current_row += offset + 1
@res_handle.data_seek(@current_row - 1)
fill_array(@res_handle.fetch_row)
else
raise NotSupportedError
end
#end
end
#
# See DBI::BaseStatement#column_info, and DBI::DBD::Mysql::Database#columns.
#
# This method provides all the attributes the +columns+ method
# provides, and a few others:
#
# * mysql_type: These correspond to constants in the Mysql::Types
# package, in the lower-level 'mysql' package.
# * mysql_type_name: A text representation of +mysql_type+.
# * mysql_length: The length of the column.
# * mysql_max_length: The max length of the column. FIXME DESCRIBE
# DIFFERENCE
# * mysql_flags: Internal MySQL flags on this column.
#
def column_info
retval = []
return [] if @res_handle.nil?
unique_key_flag = MysqlField.const_get(:UNIQUE_KEY_FLAG)
multiple_key_flag = MysqlField.const_get(:MULTIPLE_KEY_FLAG)
indexed = (unique_key_flag | multiple_key_flag)
# Note: Cannot get 'default' column attribute because MysqlField.def
# is set only by mysql_list_fields()
@res_handle.fetch_fields.each {|col|
mysql_type_name, dbi_type = Database::TYPE_MAP[col.type] rescue [nil, nil]
xopen_info = Database::MYSQL_to_XOPEN[mysql_type_name] ||
Database::MYSQL_to_XOPEN[nil]
sql_type = xopen_info[0]
type_name = DBI::SQL_TYPE_NAMES[sql_type]
retval << {
# Standard Ruby DBI column attributes
'name' => col.name,
'sql_type' => sql_type,
'type_name' => type_name,
# XXX it seems mysql counts the literal decimal point when weighing in the "length".
'precision' => type_name == "NUMERIC" ? col.length - 2 : col.length,
'scale' => col.decimals,
'nullable' => !col.is_not_null?,
'indexed' => ((col.flags & indexed) != 0) ||
col.is_pri_key?,
'primary' => col.is_pri_key?,
'unique' => ((col.flags & unique_key_flag) != 0) ||
col.is_pri_key?,
# MySQL-specific attributes (signified by leading "mysql_")
'mysql_type' => col.type,
'mysql_type_name' => mysql_type_name,
'mysql_length' => col.length,
'mysql_max_length' => col.max_length,
'mysql_flags' => col.flags
}
if retval[-1]['sql_type'] == DBI::SQL_TINYINT and retval[-1]['precision'] == 1
retval[-1]['dbi_type'] = DBI::Type::Boolean
elsif dbi_type
retval[-1]['dbi_type'] = dbi_type
end
}
retval
rescue MyError => err
error(err)
end
def rows
@rows
end
# def []=(attr, value)
# case attr
# when 'mysql_use_result'
# @attr['mysql_store_result'] = ! value
# @attr['mysql_use_result'] = value
# when 'mysql_store_result'
# @attr['mysql_use_result'] = ! value
# @attr['mysql_store_result'] = value
# else
# raise NotSupportedError
# end
# end
end # class Statement
end
|