1-- This file is covered by the Internet Software Consortium (ISC) License 2-- Reference: ../../License.txt 3 4with Ada.Exceptions; 5 6package body AdaBase.Driver.Base.MySQL is 7 8 package EX renames Ada.Exceptions; 9 10 --------------- 11 -- execute -- 12 --------------- 13 overriding 14 function execute (driver : MySQL_Driver; sql : String) 15 return Affected_Rows 16 is 17 trsql : String := CT.trim_sql (sql); 18 nquery : Natural := CT.count_queries (trsql); 19 aborted : constant Affected_Rows := 0; 20 err1 : constant CT.Text := 21 CT.SUS ("ACK! Execution attempted on inactive connection"); 22 err2 : constant String := 23 "Driver is configured to allow only one query at " & 24 "time, but this SQL contains multiple queries: "; 25 begin 26 if not driver.connection_active then 27 -- Fatal attempt to query an unccnnected database 28 driver.log_problem (category => execution, 29 message => err1, 30 break => True); 31 return aborted; 32 end if; 33 34 if nquery > 1 and then not driver.trait_multiquery_enabled then 35 -- Fatal attempt to execute multiple queries when it's not permitted 36 driver.log_problem (category => execution, 37 message => CT.SUS (err2 & trsql), 38 break => True); 39 return aborted; 40 end if; 41 42 declare 43 result : Affected_Rows; 44 begin 45 -- MySQL execute is configured to support multiquery at this point 46 -- so it is not necessary to loop through subqueries. We send the 47 -- trimmed compound query as it was received. 48 driver.connection.execute (trsql); 49 driver.log_nominal (execution, CT.SUS (trsql)); 50 result := driver.connection.rows_affected_by_execution; 51 return result; 52 exception 53 when ACM.QUERY_FAIL => 54 driver.log_problem (category => execution, 55 message => CT.SUS (trsql), 56 pull_codes => True); 57 return aborted; 58 end; 59 end execute; 60 61 62 ------------------------------------------------------------------------ 63 -- ROUTINES OF ALL DRIVERS NOT COVERED BY INTERFACES (TECH REASON) -- 64 ------------------------------------------------------------------------ 65 66 67 ------------- 68 -- query -- 69 ------------- 70 function query (driver : MySQL_Driver; sql : String) 71 return ASM.MySQL_statement is 72 begin 73 return driver.private_query (sql); 74 end query; 75 76 77 --------------- 78 -- prepare -- 79 --------------- 80 function prepare (driver : MySQL_Driver; sql : String) 81 return ASM.MySQL_statement is 82 begin 83 return driver.private_prepare (sql); 84 end prepare; 85 86 87 -------------------- 88 -- query_select -- 89 -------------------- 90 function query_select (driver : MySQL_Driver; 91 distinct : Boolean := False; 92 tables : String; 93 columns : String; 94 conditions : String := blankstring; 95 groupby : String := blankstring; 96 having : String := blankstring; 97 order : String := blankstring; 98 null_sort : Null_Priority := native; 99 limit : Trax_ID := 0; 100 offset : Trax_ID := 0) 101 return ASM.MySQL_statement is 102 begin 103 return driver.private_query 104 (driver.sql_assemble (distinct => distinct, 105 tables => tables, 106 columns => columns, 107 conditions => conditions, 108 groupby => groupby, 109 having => having, 110 order => order, 111 null_sort => null_sort, 112 limit => limit, 113 offset => offset)); 114 end query_select; 115 116 117 ---------------------- 118 -- prepare_select -- 119 ---------------------- 120 function prepare_select (driver : MySQL_Driver; 121 distinct : Boolean := False; 122 tables : String; 123 columns : String; 124 conditions : String := blankstring; 125 groupby : String := blankstring; 126 having : String := blankstring; 127 order : String := blankstring; 128 null_sort : Null_Priority := native; 129 limit : Trax_ID := 0; 130 offset : Trax_ID := 0) 131 return ASM.MySQL_statement is 132 begin 133 return driver.private_prepare 134 (driver.sql_assemble (distinct => distinct, 135 tables => tables, 136 columns => columns, 137 conditions => conditions, 138 groupby => groupby, 139 having => having, 140 order => order, 141 null_sort => null_sort, 142 limit => limit, 143 offset => offset)); 144 end prepare_select; 145 146 147 ------------------------------------------------------------------------ 148 -- PUBLIC ROUTINES NOT COVERED BY INTERFACES -- 149 ------------------------------------------------------------------------ 150 151 --------------------------------- 152 -- trait_compressed_protocol -- 153 --------------------------------- 154 function trait_protocol_compressed (driver : MySQL_Driver) return Boolean 155 is 156 begin 157 return driver.connection.compressed; 158 end trait_protocol_compressed; 159 160 161 -------------------------------- 162 -- trait_query_buffers_used -- 163 -------------------------------- 164 function trait_query_buffers_used (driver : MySQL_Driver) return Boolean 165 is 166 begin 167 return driver.connection.useBuffer; 168 end trait_query_buffers_used; 169 170 171 -------------------------------------- 172 -- set_trait_compressed_protocol -- 173 ------------------------------------- 174 procedure set_trait_protocol_compressed (driver : MySQL_Driver; 175 trait : Boolean) 176 is 177 begin 178 driver.connection.setCompressed (compressed => trait); 179 end set_trait_protocol_compressed; 180 181 182 ------------------------------ 183 -- set_query_buffers_used -- 184 ------------------------------ 185 procedure set_trait_query_buffers_used (driver : MySQL_Driver; 186 trait : Boolean) 187 is 188 begin 189 driver.connection.setUseBuffer (buffered => trait); 190 end set_trait_query_buffers_used; 191 192 ------------------------------------------------------------------------ 193 -- PRIVATE ROUTINES NOT COVERED BY INTERFACES -- 194 ------------------------------------------------------------------------ 195 196 ------------------ 197 -- initialize -- 198 ------------------ 199 overriding 200 procedure initialize (Object : in out MySQL_Driver) 201 is 202 begin 203 Object.connection := Object.local_connection'Unchecked_Access; 204 Object.dialect := driver_mysql; 205 end initialize; 206 207 208 ----------------------- 209 -- private_connect -- 210 ----------------------- 211 overriding 212 procedure private_connect (driver : out MySQL_Driver; 213 database : String; 214 username : String; 215 password : String; 216 hostname : String := blankstring; 217 socket : String := blankstring; 218 port : Posix_Port := portless) 219 is 220 err1 : constant CT.Text := 221 CT.SUS ("ACK! Reconnection attempted on active connection"); 222 nom : constant CT.Text := 223 CT.SUS ("Connection to " & database & " database succeeded."); 224 begin 225 if driver.connection_active then 226 driver.log_problem (category => execution, 227 message => err1); 228 return; 229 end if; 230 driver.connection.connect (database => database, 231 username => username, 232 password => password, 233 socket => socket, 234 hostname => hostname, 235 port => port); 236 237 driver.connection_active := driver.connection.all.connected; 238 239 driver.log_nominal (category => connecting, message => nom); 240 exception 241 when Error : others => 242 driver.log_problem 243 (category => connecting, 244 break => True, 245 message => CT.SUS (ACM.EX.Exception_Message (X => Error))); 246 end private_connect; 247 248 249 --------------------- 250 -- private_query -- 251 --------------------- 252 function private_query (driver : MySQL_Driver; sql : String) 253 return ASM.MySQL_statement 254 is 255 duplicate : aliased String := sql; 256 err1 : constant CT.Text := 257 CT.SUS ("ACK! Query attempted on inactive connection"); 258 begin 259 if driver.connection_active then 260 declare 261 err2 : constant CT.Text := CT.SUS ("Query failed!"); 262 statement : ASM.MySQL_statement 263 (type_of_statement => AID.ASB.direct_statement, 264 log_handler => logger'Access, 265 mysql_conn => ACM.MySQL_Connection_Access 266 (driver.connection), 267 initial_sql => duplicate'Unchecked_Access, 268 con_error_mode => driver.trait_error_mode, 269 con_case_mode => driver.trait_column_case, 270 con_max_blob => driver.trait_max_blob_size, 271 con_buffered => driver.trait_query_buffers_used); 272 begin 273 if statement.successful then 274 driver.log_nominal (category => execution, 275 message => CT.SUS ("query succeeded," & 276 statement.rows_returned'Img & 277 " rows returned")); 278 else 279 driver.log_nominal (category => execution, message => err2); 280 end if; 281 return statement; 282 exception 283 when RES : others => 284 -- Fatal attempt to create a direct statement 285 driver.log_problem 286 (category => execution, 287 message => CT.SUS (EX.Exception_Message (RES)), 288 pull_codes => True, 289 break => True); 290 end; 291 else 292 -- Fatal attempt to query an unconnected database 293 driver.log_problem (category => execution, 294 message => err1, 295 break => True); 296 end if; 297 -- We never get here, the driver.log_problem throws exception first 298 raise ACM.STMT_NOT_VALID 299 with "failed to return MySQL statement"; 300 end private_query; 301 302 303 ----------------------- 304 -- private_prepare -- 305 ----------------------- 306 function private_prepare (driver : MySQL_Driver; sql : String) 307 return ASM.MySQL_statement 308 is 309 duplicate : aliased String := sql; 310 err1 : constant CT.Text := 311 CT.SUS ("ACK! Query attempted on inactive connection"); 312 begin 313 if driver.connection_active then 314 declare 315 statement : ASM.MySQL_statement 316 (type_of_statement => AID.ASB.prepared_statement, 317 log_handler => logger'Access, 318 mysql_conn => ACM.MySQL_Connection_Access 319 (driver.connection), 320 initial_sql => duplicate'Unchecked_Access, 321 con_error_mode => driver.trait_error_mode, 322 con_case_mode => driver.trait_column_case, 323 con_max_blob => driver.trait_max_blob_size, 324 con_buffered => driver.trait_query_buffers_used); 325 begin 326 return statement; 327 exception 328 when RES : others => 329 -- Fatal attempt to prepare a statement 330 driver.log_problem 331 (category => statement_preparation, 332 message => CT.SUS (EX.Exception_Message (RES)), 333 pull_codes => True, 334 break => True); 335 end; 336 else 337 -- Fatal attempt to query an unconnected database 338 driver.log_problem (category => statement_preparation, 339 message => err1, 340 break => True); 341 end if; 342 -- We never get here, the driver.log_problem throws exception first 343 raise ACM.STMT_NOT_VALID 344 with "failed to return MySQL statement"; 345 end private_prepare; 346 347 348 -------------------- 349 -- sql_assemble -- 350 -------------------- 351 function sql_assemble (driver : MySQL_Driver; 352 distinct : Boolean := False; 353 tables : String; 354 columns : String; 355 conditions : String := blankstring; 356 groupby : String := blankstring; 357 having : String := blankstring; 358 order : String := blankstring; 359 null_sort : Null_Priority := native; 360 limit : Trax_ID := 0; 361 offset : Trax_ID := 0) return String 362 is 363 vanilla : String := assembly_common_select 364 (distinct, tables, columns, conditions, groupby, having, order); 365 begin 366 if null_sort /= native then 367 driver.log_nominal 368 (category => execution, 369 message => CT.SUS ("Note that NULLS FIRST/LAST is not " & 370 "supported by MySQL so the null_sort setting is ignored")); 371 end if; 372 if limit > 0 then 373 if offset > 0 then 374 return vanilla & " LIMIT" & limit'Img & " OFFSET" & offset'Img; 375 else 376 return vanilla & " LIMIT" & limit'Img; 377 end if; 378 end if; 379 return vanilla; 380 end sql_assemble; 381 382 383 ----------------------------- 384 -- call_stored_procedure -- 385 ----------------------------- 386 function call_stored_procedure (driver : MySQL_Driver; 387 stored_procedure : String; 388 proc_arguments : String) 389 return ASM.MySQL_statement 390 is 391 SQL : String := "CALL " & stored_procedure & " (" & proc_arguments & ")"; 392 begin 393 return driver.query (SQL); 394 end call_stored_procedure; 395 396 397end AdaBase.Driver.Base.MySQL; 398