package AdaBase is type Null_Priority is (native, nulls_first, nulls_last); end AdaBase;
AdaBase.Statement.Base.[STMT] function
AdaBase.Driver.Base.[DB].prepare_select (
distinct : Boolean := False;
tables : String;
columns : String;
conditions : String := "";
groupby : String := "";
having : String := "";
order : String := "";
null_sort : Null_Priority := native;
limit : Trax_ID := 0;
offset : Trax_ID := 0)
This function assembles a driver-specific (SQL dialect-specific) SELECT query based on which arguments are provided. Generally the limit, offset and null_sort parameters are the ones that vary the most between dialects, with the latter not being supported by all SQL drivers. The only required parameters are tables and columns.
Afterwards, the assembled SQL string is prepared and returns the statement object that hasn't yet been executed. The user has the option to bind values and variables to the templated SQL prior to the query execution. This is allowed when the SQL has one or more named (:name) or question mark (?) parameters in the string, and both types can be present as the named parameters are internally converted the question marks in the same order. The named parameters can only be used in one location though.
Once the statement has been prepared, it can be executed repeatedly after updating the parameter bindings before each execution. THis is a big performance improvement over executing similar statements directly and individually. Another major benefit is that SQL injection attacks are prevented by using parameter bindings instead of passing text strings.
with AdaBase; with Connect; with Ada.Text_IO; with CommonText; with AdaBase.Results.Sets; with AdaBase.Logger.Facility; procedure Prep_Stmt is package CON renames Connect; package TIO renames Ada.Text_IO; package CT renames CommonText; package AR renames AdaBase.Results; package ARS renames AdaBase.Results.Sets; package ALF renames AdaBase.Logger.Facility; begin CON.DR.command_standard_logger (device => ALF.screen, action => ALF.attach); CON.connect_database; declare max_calories : aliased AR.Byte2 := 200; min_calories : constant AR.Byte2 := 5; row : ARS.Datarow; stmt : CON.Stmt_Type := CON.DR.prepare_select (tables => "fruits", columns => "*", conditions => "color = ? and calories > :mincal and calories < ?"); begin stmt.assign (1, "red"); stmt.assign ("mincal", min_calories); stmt.assign (3, max_calories'Unchecked_Access); if stmt.execute then TIO.Put_Line ("execute succeeded"); for c in Natural range 1 .. stmt.column_count loop TIO.Put_Line ("Column" & c'Img & " heading: " & stmt.column_name (c)); end loop; TIO.Put_Line ("returned rows: " & stmt.rows_returned'Img); loop row := stmt.fetch_next; exit when row.data_exhausted; TIO.Put_Line (row.column (2).as_string & " (" & row.column ("color").as_string & ") " & row.column ("calories").as_string & " calories"); end loop; else TIO.Put_Line ("execute failed"); end if; end; declare sql : String := "INSERT INTO fruits (fruit, color, calories) " & "VALUES ('potato','tan', 77)"; stmt : CON.Stmt_Type := CON.DR.prepare (sql); begin if stmt.execute then TIO.Put_Line ("Inserted row " & stmt.last_insert_id'Img); TIO.Put_Line ("Affected rows: " & stmt.rows_affected'Img); end if; CON.DR.rollback; end; CON.DR.disconnect; end Prep_Stmt;
2016-05-02 18:28:28 mysql : Connect : Connection to adabase_examples database succeeded. 2016-05-02 18:28:28 mysql : Prepare Stmt : SELECT ALL * FROM fruits WHERE color = ? and calories > ? and calories < ? 2016-05-02 18:28:28 mysql : Execute Stmt : Exec with 3 bound parameters execute succeeded Column 1 heading: id Column 2 heading: fruit Column 3 heading: color Column 4 heading: calories returned rows: 2 apple (red) 95 calories tomato (red) 9 calories 2016-05-02 18:28:28 mysql : Prepare Stmt : INSERT INTO fruits (fruit, color, calories) VALUES ('potato','tan', 77) 2016-05-02 18:28:28 mysql : Execute Stmt : Exec without bound parameters Inserted row 36 Affected rows: 1 2016-05-02 18:28:28 mysql : Disconnect : Disconnect From database
2016-05-12 14:27:02 sqlite : Connect : Connection to file:///home/marino/adabase.sqlite database succeeded. 2016-05-12 14:27:02 sqlite : Prepare Stmt : SELECT ALL * FROM fruits WHERE color = ? and calories > ? and calories < ? 2016-05-12 14:27:02 sqlite : Execute Stmt : Exec with 3 bound parameters execute succeeded Column 1 heading: id Column 2 heading: fruit Column 3 heading: color Column 4 heading: calories returned rows: 0 apple (red) 95 calories tomato (red) 9 calories 2016-05-12 14:27:02 sqlite : Prepare Stmt : INSERT INTO fruits (fruit, color, calories) VALUES ('potato','tan', 77) 2016-05-12 14:27:02 sqlite : Execute Stmt : Exec without bound parameters Inserted row 38 Affected rows: 1 2016-05-12 14:27:02 sqlite : Disconnect : Disconnect From database
2016-05-24 20:19:37 pgsql : Connect : Connection to adabase_examples database succeeded. 2016-05-24 20:19:37 pgsql : Prepare Stmt : SELECT ALL * FROM fruits WHERE color = $1 and calories > $2 and calories < $3 2016-05-24 20:19:37 pgsql : Execute Stmt : Exec with 3 bound parameters execute succeeded Column 1 heading: id Column 2 heading: fruit Column 3 heading: color Column 4 heading: calories returned rows: 2 apple (red) 95 calories tomato (red) 9 calories 2016-05-24 20:19:37 pgsql : Prepare Stmt : INSERT INTO fruits (fruit, color, calories) VALUES ('potato','tan', 77) 2016-05-24 20:19:37 pgsql : Execute Stmt : Exec without bound parameters Inserted row 46 Affected rows: 1 2016-05-24 20:19:37 pgsql : Transaction : ROLLBACK TRANSACTION 2016-05-24 20:19:37 pgsql : Disconnect : Disconnect From database
[DB] is "MySQL.MySQL_Driver", "PostgreSQL.PostgreSQL_Driver", or "SQLite.SQLite_Driver"
[STMT] is "MySQL.MySQL_statement", "PostgreSQL.PostgreSQL_statement", or "SQLite.SQLite_statement"