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.