Parameters and Placeholders

Most database client libraries support the prepare-bind-execute model.

C Library prepare-bind-execute Model

  1. SQL containing placeholders is prepared. The placeholders stand for information to be provided upon execution. The server assigns some kind of handle/identifier to the query, which is how the client will reference it.

    The form of the placeholder is, like the rest of the SQL syntax, determined by the server (and, in some cases, by the driver). ODBC drivers usually use ? as a placeholder, but other drivers use names with a "special" prefix.

  2. For each placeholder, the client binds a buffer, i.e. describes the datatype represented by the placeholder and a buffer where the value will be provided.

  3. The client populates the buffer and calls for execution.

  4. Repeat previous step as needed.

dbstreams simplifies the work by taking on the bind step. The application describes the parameter by providing the first value; the rest is inferred.

Example 2-3. Placeholders and Parameters

	
		typedef struct { char *name; int value; } nvp;
		static const nvp values[] =  
				{ { "four", 4 }
				, { "six",  6 }
				};
				
		q = "select * from T where s = ? and t = ?";

		for( int i=0; i < sizeof(values)/sizeof(values[0]); i++ ){
	
			db << q;	// ... prepare ...
		
			if( i == 0 ) {
				parameter_type p0(values[i].name, -1, 0);
				parameter_type p1(values[i].value, 1);

				// describe the parameters and their values
				db << p0 << p1 << endl;	

				// (make sure the list is right)
				cout << "parameter list: ";
				std::for_each(  db.parameter_list().begin(), 
						db.parameter_list().end(), 
						parameter_writer(cout)
					      );
				cout << '\n';

			} else {
				// reuse the parameters
				db << p(0) << values[i].name 
				   << p(1) << values[i].value << endl;
			}
			
			// read the last parameter, just for fun
			parameter_value<int> param(1);
			db >> param;
			cout << "last parameter value: " << param.data << "\n";