Annotation of parser3/src/main/pa_sql_driver_manager.C, revision 1.69.2.3
1.54 paf 1: /** @file
2: Parser: sql driver manager implementation.
3:
1.69 paf 4: Copyright (c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.61 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.63 paf 6: */
1.54 paf 7:
1.69.2.3! paf 8: static const char* IDENT_SQL_DRIVER_MANAGER_C="$Date: 2003/01/27 16:19:11 $";
1.54 paf 9:
10: #include "pa_sql_driver_manager.h"
11: #include "ltdl.h"
12: #include "pa_sql_connection.h"
13: #include "pa_exception.h"
14: #include "pa_common.h"
15: #include "pa_threads.h"
16: #include "pa_vhash.h"
17: #include "pa_vtable.h"
18:
19: // globals
20:
1.69.2.1 paf 21: SQL_Driver_manager SQL_driver_manager;
1.54 paf 22:
23: // consts
24:
25: const int EXPIRE_UNUSED_CONNECTION_SECONDS=60;
26: const int CHECK_EXPIRED_CONNECTIONS_SECONDS=EXPIRE_UNUSED_CONNECTION_SECONDS*2;
27:
28: // helpers
29:
1.69.2.3! paf 30: ConstStringPtr SQL_Driver_services_impl::url_without_login() const {
! 31: StringPtr result(new String);
! 32: *result << url->mid(0, furl->pos(":")) << "://****";
1.54 paf 33:
1.69.2.3! paf 34: int at_pos=url->pos("@");
1.54 paf 35: if(at_pos>0)
1.69.2.3! paf 36: *result << url->mid(at_pos, furl->size());
1.54 paf 37:
38: return result;
39: }
40:
41: // helpers
42:
1.69.2.3! paf 43: static void expire_connection(SQL_Connection& connection, time_t older_dies) {
1.54 paf 44: if(connection.connected() && connection.expired(older_dies))
45: connection.disconnect();
46: }
1.69.2.3! paf 47: static void expire_connections(
! 48: SQL_Driver_manager::connection_cache_type::key_type /*key*/,
! 49: SQL_Driver_manager::connection_cache_type::value_type stack,
! 50: time_t older_dies) {
! 51: for(int i=0; i<=stack->top_index(); i++)
! 52: expire_connection(*stack->get(i), older_dies);
1.54 paf 53: }
54:
55: // SQL_Driver_manager
56:
1.69.2.1 paf 57: SQL_Driver_manager::SQL_Driver_manager():
58: prev_expiration_pass_time(0) {
1.54 paf 59: }
60:
61: SQL_Driver_manager::~SQL_Driver_manager() {
1.69.2.3! paf 62: connection_cache.for_each(expire_connections, time(0)+(time_t)1/*=in future=expire all*/);
1.54 paf 63: }
64:
65: /// @param request_url protocol://[driver-dependent]
1.69.2.3! paf 66: SQL_ConnectionPtr SQL_Driver_manager::get_connection(Pool& pool, ConstStringPtr aurl,
! 67: ConstStringPtr aorigin,
1.54 paf 68: Table *protocol2driver_and_client) {
69: // we have table for locating protocol's library
70: if(!protocol2driver_and_client)
1.62 paf 71: throw Exception("parser.runtime",
1.69.2.3! paf 72: aurl,
1.54 paf 73: "$"MAIN_SQL_NAME":"MAIN_SQL_DRIVERS_NAME" table must be defined");
74:
75: // first trying to get cached connection
1.58 paf 76: SQL_Connection *connection=get_connection_from_cache(request_url);
77: if(connection) {
1.69.2.3! paf 78: connection->set_pool(pool);
1.58 paf 79: if(!connection->ping()) { // we have some cached connection, is it pingable?
80: connection->disconnect(); // kill unpingabe=dead connection
81: connection=0;
1.54 paf 82: }
83: }
84:
85: char *request_url_cstr;
1.58 paf 86: if(connection)
1.54 paf 87: request_url_cstr=0; // calm, compiler
88: else { // no cached connection or it were unpingabe: connect/reconnect
89: int pos=request_url.pos("://", 3);
90: if(pos<0)
1.62 paf 91: throw Exception("parser.runtime",
1.54 paf 92: request_url.size()?&request_url:&request_origin,
1.62 paf 93: "connection string must start with protocol://");
1.54 paf 94:
95: // make global_url C-string on global pool
96: request_url_cstr=request_url.cstr();
97: char *global_url_cstr=(char *)malloc(strlen(request_url_cstr)+1);
98: strcpy(global_url_cstr, request_url_cstr);
99: // make global_url string on global pool
100: String& global_url=*new(this->pool()) String(this->pool(), global_url_cstr);
101:
102: char *request_protocol_cstr=lsplit(&request_url_cstr, ':');
103: // skip "//" after ':'
104: while(*request_url_cstr=='/')
105: request_url_cstr++;
106: // make global_protocol C-string on global pool
107: char *global_protocol_cstr=(char *)malloc(strlen(request_protocol_cstr)+1);
108: strcpy(global_protocol_cstr, request_protocol_cstr);
109: // make global_protocol string on global pool
110: String& global_protocol=*new(this->pool()) String(this->pool(),
111: global_protocol_cstr);
112:
113: SQL_Driver *driver;
114: // first trying to get cached driver
115: if(!(driver=get_driver_from_cache(global_protocol))) {
116: // no cached
117: const String *library=0;
118: const String *dlopen_file_spec=0;
119: if(protocol2driver_and_client->locate(0, global_protocol)) {
120: if(!(library=protocol2driver_and_client->item(1)) || library->size()==0)
1.62 paf 121: throw Exception("parser.runtime",
1.54 paf 122: protocol2driver_and_client->origin_string(),
123: "driver library column for protocol '%s' is empty",
124: request_protocol_cstr);
125: dlopen_file_spec=protocol2driver_and_client->item(2);
126: } else
1.62 paf 127: throw Exception("parser.runtime",
1.54 paf 128: &request_url,
129: "undefined protocol '%s'",
130: request_protocol_cstr);
131:
132: if(lt_dlinit())
1.62 paf 133: throw Exception(0,
1.54 paf 134: library,
135: "prepare to dynamic loading failed, %s", lt_dlerror());
136:
137: const char *filename=library->cstr(String::UL_FILE_SPEC);
138: lt_dlhandle handle=lt_dlopen(filename);
139: if (!handle)
1.62 paf 140: throw Exception(0,
1.54 paf 141: library,
142: "can not open the module, %s", lt_dlerror());
143:
144: SQL_Driver_create_func create=(SQL_Driver_create_func)lt_dlsym(handle,
145: SQL_DRIVER_CREATE_NAME);
146: if(!create)
1.62 paf 147: throw Exception(0,
1.54 paf 148: library,
149: "function '"SQL_DRIVER_CREATE_NAME"' was not found");
150:
151: // create library-driver!
152: driver=(*create)();
153:
154: // validate driver api version
155: int driver_api_version=driver->api_version();
156: if(driver_api_version!=SQL_DRIVER_API_VERSION)
1.62 paf 157: throw Exception(0,
1.54 paf 158: library,
159: "driver implements API version 0x%04X not equal to 0x%04X",
160: driver_api_version, SQL_DRIVER_API_VERSION);
161:
162: // initialise by connecting to sql client dynamic link library
163: char *dlopen_file_spec_cstr=
164: dlopen_file_spec && dlopen_file_spec->size()?
165: dlopen_file_spec->cstr(String::UL_AS_IS):0;
166: if(const char *error=driver->initialize(
167: dlopen_file_spec_cstr))
1.62 paf 168: throw Exception(0,
1.54 paf 169: library,
170: "driver failed to initialize client library '%s', %s",
171: dlopen_file_spec_cstr?dlopen_file_spec_cstr:"unspecifed",
172: error);
173:
174: // cache it
175: put_driver_to_cache(global_protocol, *driver);
176: }
177:
178: // allocate in global pool
179: // associate with services[request]
180: // NOTE: never freed up!
1.58 paf 181: connection=new(this->pool()) SQL_Connection(this->pool(), global_url, *driver);
1.54 paf 182: // associate with services[request] (deassociates at close)
1.69.2.3! paf 183: connection->set_pool(pool);
1.54 paf 184: }
185:
1.58 paf 186: // if not connected yet, do that now, when connection has services
187: if(!connection->connected())
188: connection->connect(request_url_cstr);
189: // return autoclosing object for it
1.69.2.1 paf 190: return SQL_ConnectionPtr(connection);
1.54 paf 191: }
192:
1.69.2.2 paf 193: void SQL_Driver_manager::close_connection(ConstStringPtr url,
1.54 paf 194: SQL_Connection& connection) {
195: // deassociate from services[request]
1.69.2.3! paf 196: connection.set_pool(0);
1.54 paf 197: put_connection_to_cache(url, connection);
198: }
199:
200:
201: // driver cache
202:
1.69.2.2 paf 203: SQL_Driver *SQL_Driver_manager::get_driver_from_cache(ConstStringPtr protocol) {
1.54 paf 204: SYNCHRONIZED;
205:
206: return static_cast<SQL_Driver *>(driver_cache.get(protocol));
207: }
208:
1.69.2.2 paf 209: void SQL_Driver_manager::put_driver_to_cache(ConstStringPtr protocol,
1.54 paf 210: SQL_Driver& driver) {
211: SYNCHRONIZED;
212:
213: driver_cache.put(protocol, &driver);
214: }
215:
216: // connection cache
217: /// @todo get rid of memory spending Stack [zeros deep inside got accumulated]
1.69.2.3! paf 218: SQL_ConnectionPtr SQL_Driver_manager::get_connection_from_cache(ConstStringPtr url) {
1.54 paf 219: SYNCHRONIZED;
220:
221: if(Stack *connections=static_cast<Stack *>(connection_cache.get(url)))
222: while(connections->top_index()>=0) { // there are cached connections to that 'url'
223: SQL_Connection *result=static_cast<SQL_Connection *>(connections->pop());
224: if(result->connected()) // not expired?
225: return result;
226: }
227:
228: return 0;
229: }
230:
1.69.2.2 paf 231: void SQL_Driver_manager::put_connection_to_cache(ConstStringPtr url,
1.54 paf 232: SQL_Connection& connection) {
233: SYNCHRONIZED;
234:
235: Stack *connections=static_cast<Stack *>(connection_cache.get(url));
236: if(!connections) { // there are no cached connections to that 'url' yet?
237: connections=NEW Stack(pool()); // NOTE: never freed up!
238: connection_cache.put(url, connections);
239: }
240: connections->push(&connection);
241: }
242:
243: void SQL_Driver_manager::maybe_expire_cache() {
244: time_t now=time(0);
245:
246: if(prev_expiration_pass_time<now-CHECK_EXPIRED_CONNECTIONS_SECONDS) {
247: connection_cache.for_each(expire_connections,
248: reinterpret_cast<void *>(now-EXPIRE_UNUSED_CONNECTION_SECONDS));
249:
250: prev_expiration_pass_time=now;
251: }
252: }
253:
254: static void add_connection_to_status_cache_table(Array::Item *value, void *info) {
255: SQL_Connection& connection=*static_cast<SQL_Connection *>(value);
256: Table& table=*static_cast<Table *>(info);
257:
258: if(connection.connected()) {
259: Pool& pool=table.pool();
260: Array& row=*new(pool) Array(pool);
261:
262: // url
263: row+=&url_without_login(pool, connection.get_url());
264: // time
1.58 paf 265: time_t time_used=connection.get_time_used();
266: const char *unsafe_time_cstr=ctime(&time_used);
1.54 paf 267: int time_buf_size=strlen(unsafe_time_cstr);
268: char *safe_time_buf=(char *)pool.malloc(time_buf_size);
269: memcpy(safe_time_buf, unsafe_time_cstr, time_buf_size);
270: row+=new(pool) String(pool, safe_time_buf, time_buf_size);
271:
272: table+=&row;
273: }
274: }
275: static void add_connections_to_status_cache_table(const Hash::Key& key, Hash::Val *value, void *info) {
276: Stack& stack=*static_cast<Stack *>(value);
277: Array_iter iter(stack);
278: for(int countdown=stack.top_index(); countdown-->=0; )
279: add_connection_to_status_cache_table(iter.next(), info);
280: }
1.69.2.2 paf 281: ValuePtr SQL_Driver_manager::get_status(Pool& pool, ConstStringPtr source) {
1.54 paf 282: VHash& result=*new(pool) VHash(pool);
283:
284: // cache
285: {
286: Array& columns=*new(pool) Array(pool);
287: columns+=new(pool) String(pool, "url");
288: columns+=new(pool) String(pool, "time");
289: Table& table=*new(pool) Table(pool, 0, &columns, connection_cache.size());
290:
291: connection_cache.for_each(add_connections_to_status_cache_table, &table);
292:
293: result.hash(source).put(*new(pool) String(pool, "cache"), new(pool) VTable(pool, &table));
294: }
295:
296: return result;
1.57 paf 297: }
E-mail: