Warning, /sdk/codevis/thirdparty/soci/docs/languages/ada/idioms.md is written in an unsupported language. File is not indexed.

0001 # Ada Idioms
0002 
0003 As any other library, SOCI-Ada has its set of idioms that ensure optimal work in terms of performance and resource usage. Still, the optimal use will depend on the concrete usage scenario - the places where programmer choices are needed will be described explicitly.
0004 
0005 The idioms below are provided as *complete programs* with the intent to make them more understandable and to give complete context of use for each idiom. The programs assume that the target database is PostgreSQL, but this can be changed by a different connection string in each place where the sessions are established. The programs use the Ada 2005 interface and some minor changes will be required to adapt them for Ada 95 compilers.
0006 
0007 ## Single query without data transfer
0008 
0009 This type of query is useful for DDL commands and can be executed directly on the given session, without explicit statement management.
0010 
0011 ```ada
0012 with SOCI;
0013 
0014 procedure My_Program is
0015 
0016     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0017 
0018 begin
0019 
0020     SQL.Execute ("drop table some_table");
0021 
0022 end My_Program;
0023 
0024 ```
0025 
0026 Note: The session object is initialized by a constructor function call. An alternative would be to declare it without initialization and later use the `Open` operation to establish a physical connection with the database.
0027 
0028 ## Simple query without parameters resulting in one row of data
0029 
0030 This type of query requires only single into elements, which together with the statement have to be manipulated explicitly.
0031 
0032 ```ada
0033 with SOCI;
0034 with Ada.Text_IO;
0035 
0036 procedure My_Program is
0037 
0038     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0039     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0040     Pos : SOCI.Into_Position;
0041 
0042     Num_Of_Persons : SOCI.DB_Integer;
0043 
0044 begin
0045 
0046     Pos := St.Into_Integer;
0047     St.Prepare ("select count(*) from persons");
0048     St.Execute (True);
0049 
0050     Num_Of_Persons := St.Get_Into_Integer (Pos);
0051 
0052     Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
0053 
0054 end My_Program;
0055 ```
0056 
0057 Note: The into element is inspected by providing the position value that was obtained at the time if was created. No operations are defined for the position type. There can be many into elements with a single query.
0058 
0059 ## Simple query with parameters and without results
0060 
0061 This type of query requires only use elements.
0062 
0063 ```ada
0064 with SOCI;
0065 
0066 procedure My_Program is
0067 
0068     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0069     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0070 
0071 begin
0072 
0073     St.Use_Integer ("increase");
0074     St.Set_Use_Integer ("increase", 1000);
0075 
0076     St.Prepare ("update persons set salary = salary + :increase");
0077     St.Execute (True);
0078 
0079 end My_Program;
0080 ```
0081 
0082 Note: The "`:increase`" in the query is a placeholder variable. There can be many such variables and each of them needs to be filled in by respective use element.
0083 
0084 ## Repeated query with parameters and without results
0085 
0086 This type of query requires only use elements, but they can be set differently for each statement execution.
0087 
0088 ```ada
0089 with SOCI;
0090 
0091 procedure My_Program is
0092 
0093     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0094     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0095 
0096 begin
0097 
0098     St.Use_String ("name");
0099 
0100     St.Prepare ("insert into countries(country_name) values(:name)");
0101 
0102     St.Set_Use_String ("name", "Poland");
0103     St.Execute (True);
0104 
0105     St.Set_Use_String ("name", "Switzerland");
0106     St.Execute (True);
0107 
0108     St.Set_Use_String ("name", "France");
0109     St.Execute (True);
0110 
0111 end My_Program;
0112 ```
0113 
0114 Note: Each time the query is executed, the *current* values of use elements are transferred to the database.
0115 
0116 ## Batch query with parameters and without results
0117 
0118 This type of query requires vector use elements. Compare with the previous example.
0119 
0120 ```ada
0121 with SOCI;
0122 
0123 procedure My_Program is
0124 
0125     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0126     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0127     First : SOCI.Vector_Index;
0128 
0129     use type SOCI.Vector_Index;
0130 
0131 begin
0132 
0133     St.Use_Vector_String ("name");
0134 
0135     St.Use_Vectors_Resize (3);
0136 
0137     First := St.Use_Vectors_First_Index;
0138 
0139     St.Set_Use_Vector_String ("name", First + 0, "Poland");
0140     St.Set_Use_Vector_String ("name", First + 1, "Switzerland");
0141     St.Set_Use_Vector_String ("name", First + 2, "France");
0142 
0143     St.Prepare ("insert into countries(country_name) values(:name)");
0144     St.Execute (True);
0145 
0146 end My_Program;
0147 ```
0148 
0149 Note:
0150 
0151 The whole bunch of data is transferred to the database if the target database server supports it and the statement is automatically repeated otherwise. This is the preferred way to transfer many rows of data to the server when the data for all rows are known before executing the query.
0152 
0153 Note:
0154 
0155 The query can be executed many times and each time a new batch of data can be transferred to the server. The size of the batch (set by calling `Use_Vectors_Resize`) can be different each time the query is executed, but cannot be larger than the size that was used the first time. The size of the batch defines a tradeoff between the amount of data being transmitted in a single step (this influences the memory used by the user program and the time of a single call) and the number of executions required to handle big data sets. The optimal size of the batch will therefore differ depending on the application, but in general tens of thousands is a reasonable limit for a batch size - the performance of the whole operation is usually not affected above this value so there is no need to imply higher memory usage at the client side.
0156 
0157 ## Simple query with many rows of results
0158 
0159 This type of query requires simple into elements.
0160 
0161 ```ada
0162 with SOCI;
0163 with Ada.Text_IO;
0164 
0165 procedure My_Program is
0166 
0167     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0168     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0169     Pos : SOCI.Into_Position;
0170 
0171 begin
0172 
0173     Pos := St.Into_String;
0174 
0175     St.Prepare ("select country_name from countries");
0176     St.Execute;
0177 
0178     while St.Fetch loop
0179 
0180         Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
0181 
0182     end loop;
0183 
0184 end My_Program;
0185 ```
0186 
0187 Note:
0188 
0189 The loop above executes as many times as there are rows in the result. After each row is read, the into elements contain the respective values from that row. The `Execute` operation is called without parameter, which is `False` by default, meaning that no data transfer is intended. The data is being transferred only during the `Fetch` operation, which returns `False` when no data has been retrieved and the result is exhausted.
0190 
0191 This type of query can have simple parameters which are fixed at the execution time.
0192 
0193 ## Batch query with many rows of results
0194 
0195 This type of query requires vector into elements. Compare with previous example.
0196 
0197 ```ada
0198 with SOCI;
0199 with Ada.Text_IO;
0200 
0201 procedure My_Program is
0202 
0203     SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
0204     St : SOCI.Statement := SOCI.Make_Statement (SQL);
0205     Pos : SOCI.Into_Position;
0206 
0207     Batch_Size : constant := 10;
0208 
0209 begin
0210 
0211     Pos := St.Into_Vector_String;
0212     St.Into_Vectors_Resize (Batch_Size);
0213 
0214     St.Prepare ("select country_name from countries");
0215     St.Execute;
0216 
0217     while St.Fetch loop
0218 
0219         for I in St.Into_Vectors_First_Index .. St.Into_Vectors_Last_Index loop
0220 
0221             Ada.Text_IO.Put_Line (St.Get_Into_Vector_String (Pos, I));
0222 
0223         end loop;
0224 
0225         St.Into_Vectors_Resize (Batch_Size);
0226 
0227     end loop;
0228 
0229 end My_Program;
0230 ```
0231 
0232 Note:
0233 
0234 The loop above is nested. The outer `while` loop fetches consecutive batches of rows from the database with requested batch size; the returned batch can be smaller than requested (the into vector elements are downsized automatically if needed) and the intended batch size is requested again before repeating the `Fetch` operation. For each returned batch, the into vector elements are inspected in the inner `for` loop. This scheme ensures correct operation independently on the size of returned batch and is therefore a recommended idiom for efficiently returning many rows of data.
0235 
0236 There is a tradeoff between efficiency and memory usage and this tradeoff is controlled by the requested batch size. Similarly to one of the examples above, there is no benefit from using batches bigger than tens of thousands of rows.
0237 
0238 This type of query can have simple (not vectors) parameters that are fixed at execution time.
0239 
0240 ## Final note
0241 
0242 Follow good database usage principles and avoid constructing queries by concatenating strings computed at run-time. Thanks to a good type system Ada is much better in preventing various SQL-injection attacks than weaker languages like PHP, but there is still a potential for vulnerability or at least performance loss. As a rule of thumb, rely on *use elements* to parameterize your queries and to provide clean separation between data and code. This will prevent many security vulnerabilities and will allow some servers to optimize their work by reusing already cached execution plans.