@@ -1527,69 +1527,71 @@ static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
15271527 zval * result_arg ;
15281528 zend_long flag = 0 ;
15291529 zend_long i , array_cnt = 0 ;
1530- ibase_result * ib_result ;
1530+ ibase_query * ib_query ;
15311531
15321532 RESET_ERRMSG ;
15331533
15341534 if (zend_parse_parameters (ZEND_NUM_ARGS (), "r|l" , & result_arg , & flag )) {
1535- return ;
1535+ RETURN_FALSE ;
15361536 }
15371537
1538- ib_result = (ibase_result * )zend_fetch_resource_ex (result_arg , LE_RESULT , le_result );
1538+ if (_php_ibase_fetch_query_res (result_arg , & ib_query )) {
1539+ RETURN_FALSE ;
1540+ }
1541+
1542+ assert (ib_query -> out_fields_count > 0 );
15391543
1540- if (ib_result -> out_sqlda == NULL || !ib_result -> has_more_rows ) {
1544+ if (ib_query -> out_sqlda == NULL || !ib_query -> has_more_rows || ! ib_query -> is_open ) {
15411545 RETURN_FALSE ;
15421546 }
15431547
1544- if (ib_result -> statement_type != isc_info_sql_stmt_exec_procedure ) {
1545- if (isc_dsql_fetch (IB_STATUS , & ib_result -> stmt , 1 , ib_result -> out_sqlda )) {
1546- ib_result -> has_more_rows = 0 ;
1548+ if (ib_query -> statement_type != isc_info_sql_stmt_exec_procedure ) {
1549+ if (isc_dsql_fetch (IB_STATUS , & ib_query -> stmt , 1 , ib_query -> out_sqlda )) {
1550+ ib_query -> has_more_rows = 0 ;
1551+ ib_query -> is_open = 0 ;
1552+
15471553 if (IB_STATUS [0 ] && IB_STATUS [1 ]) { /* error in fetch */
15481554 _php_ibase_error ();
15491555 }
1556+
1557+ if (isc_dsql_free_statement (IB_STATUS , & ib_query -> stmt , DSQL_close )){
1558+ _php_ibase_error ();
1559+ }
1560+
15501561 RETURN_FALSE ;
15511562 }
15521563 } else {
1553- ib_result -> has_more_rows = 0 ;
1564+ ib_query -> has_more_rows = 0 ;
1565+ ib_query -> is_open = 0 ;
15541566 }
15551567
1556- array_init (return_value );
1557-
1558- for (i = 0 ; i < ib_result -> out_sqlda -> sqld ; ++ i ) {
1559- XSQLVAR * var = & ib_result -> out_sqlda -> sqlvar [i ];
1560- char buf [METADATALENGTH + 4 ], * alias = var -> aliasname ;
1561-
1562- if (! (fetch_type & FETCH_ROW )) {
1563- int i = 0 ;
1564- char const * base = "FIELD" ; /* use 'FIELD' if name is empty */
1565-
1566- /**
1567- * Ensure no two columns have identical names:
1568- * keep generating new names until we find one that is unique.
1569- */
1570- switch (* alias ) {
1571- void * p ;
1572-
1573- default :
1574- i = 1 ;
1575- base = alias ;
1576-
1577- while ((p = zend_symtable_str_find_ptr (
1578- Z_ARRVAL_P (return_value ), alias , strlen (alias ))) != NULL ) {
1568+ assert (ib_query -> out_fields_count == ib_query -> out_sqlda -> sqld );
15791569
1580- case '\0' :
1581- snprintf (alias = buf , sizeof (buf ), "%s_%02d" , base , i ++ );
1582- }
1570+ HashTable * ht_ret ;
1571+ if (!(fetch_type & FETCH_ROW )) {
1572+ if (!ib_query -> ht_aliases ){
1573+ if (_php_ibase_alloc_ht_aliases (ib_query )){
1574+ _php_ibase_error ();
1575+ RETURN_FALSE ;
15831576 }
15841577 }
1578+ ht_ret = zend_array_dup (ib_query -> ht_aliases );
1579+ } else {
1580+ if (!ib_query -> ht_ind )_php_ibase_alloc_ht_ind (ib_query );
1581+ ht_ret = zend_array_dup (ib_query -> ht_ind );
1582+ }
1583+
1584+ for (i = 0 ; i < ib_query -> out_fields_count ; ++ i ) {
1585+ XSQLVAR * var = & ib_query -> out_sqlda -> sqlvar [i ];
15851586
1587+ // TODO: just continue and unnest. All fields are set to NULL already
15861588 if (((var -> sqltype & 1 ) == 0 ) || * var -> sqlind != -1 ) {
1587- zval result ;
1589+ zval * result = zend_hash_get_current_data ( ht_ret ) ;
15881590
15891591 switch (var -> sqltype & ~1 ) {
15901592
15911593 default :
1592- _php_ibase_var_zval (& result , var -> sqldata , var -> sqltype , var -> sqllen ,
1594+ _php_ibase_var_zval (result , var -> sqldata , var -> sqltype , var -> sqllen ,
15931595 var -> sqlscale , flag );
15941596 break ;
15951597 case SQL_BLOB :
@@ -1604,7 +1606,7 @@ static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
16041606 blob_handle .bl_handle = 0 ;
16051607 blob_handle .bl_qd = * (ISC_QUAD * ) var -> sqldata ;
16061608
1607- if (isc_open_blob (IB_STATUS , & ib_result -> link -> handle , & ib_result -> trans -> handle ,
1609+ if (isc_open_blob (IB_STATUS , & ib_query -> link -> handle , & ib_query -> trans -> handle ,
16081610 & blob_handle .bl_handle , & blob_handle .bl_qd )) {
16091611 _php_ibase_error ();
16101612 goto _php_ibase_fetch_error ;
@@ -1639,8 +1641,8 @@ static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
16391641 }
16401642
16411643 if (max_len == 0 ) {
1642- ZVAL_STRING (& result , "" );
1643- } else if (SUCCESS != _php_ibase_blob_get (& result , & blob_handle ,
1644+ ZVAL_STRING (result , "" );
1645+ } else if (SUCCESS != _php_ibase_blob_get (result , & blob_handle ,
16441646 max_len )) {
16451647 goto _php_ibase_fetch_error ;
16461648 }
@@ -1652,24 +1654,24 @@ static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
16521654
16531655 } else { /* blob id only */
16541656 ISC_QUAD bl_qd = * (ISC_QUAD * ) var -> sqldata ;
1655- ZVAL_NEW_STR (& result , _php_ibase_quad_to_string (bl_qd ));
1657+ ZVAL_NEW_STR (result , _php_ibase_quad_to_string (bl_qd ));
16561658 }
16571659 break ;
16581660 case SQL_ARRAY :
16591661 if (flag & PHP_IBASE_FETCH_ARRAYS ) { /* array can be *huge* so only fetch if asked */
16601662 ISC_QUAD ar_qd = * (ISC_QUAD * ) var -> sqldata ;
1661- ibase_array * ib_array = & ib_result -> out_array [array_cnt ++ ];
1663+ ibase_array * ib_array = & ib_query -> out_array [array_cnt ++ ];
16621664 void * ar_data = emalloc (ib_array -> ar_size );
16631665
1664- if (isc_array_get_slice (IB_STATUS , & ib_result -> link -> handle ,
1665- & ib_result -> trans -> handle , & ar_qd , & ib_array -> ar_desc ,
1666+ if (isc_array_get_slice (IB_STATUS , & ib_query -> link -> handle ,
1667+ & ib_query -> trans -> handle , & ar_qd , & ib_array -> ar_desc ,
16661668 ar_data , & ib_array -> ar_size )) {
16671669 _php_ibase_error ();
16681670 efree (ar_data );
16691671 goto _php_ibase_fetch_error ;
16701672 }
16711673
1672- if (FAILURE == _php_ibase_arr_zval (& result , ar_data , ib_array -> ar_size , ib_array ,
1674+ if (FAILURE == _php_ibase_arr_zval (result , ar_data , ib_array -> ar_size , ib_array ,
16731675 0 , flag )) {
16741676 efree (ar_data );
16751677 goto _php_ibase_fetch_error ;
@@ -1678,27 +1680,18 @@ static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
16781680
16791681 } else { /* blob id only */
16801682 ISC_QUAD ar_qd = * (ISC_QUAD * ) var -> sqldata ;
1681- ZVAL_NEW_STR (& result , _php_ibase_quad_to_string (ar_qd ));
1683+ ZVAL_NEW_STR (result , _php_ibase_quad_to_string (ar_qd ));
16821684 }
16831685 break ;
16841686 _php_ibase_fetch_error :
1685- zval_ptr_dtor_nogc (& result );
16861687 RETURN_FALSE ;
16871688 } /* switch */
1688-
1689- if (fetch_type & FETCH_ROW ) {
1690- add_index_zval (return_value , i , & result );
1691- } else {
1692- add_assoc_zval (return_value , alias , & result );
1693- }
1694- } else {
1695- if (fetch_type & FETCH_ROW ) {
1696- add_index_null (return_value , i );
1697- } else {
1698- add_assoc_null (return_value , alias );
1699- }
17001689 }
1690+
1691+ zend_hash_move_forward (ht_ret );
17011692 } /* for field */
1693+
1694+ RETVAL_ARR (ht_ret );
17021695}
17031696/* }}} */
17041697
@@ -2204,4 +2197,82 @@ static int _php_ibase_get_vars_count(ibase_query *ib_query)
22042197 return rv ;
22052198}
22062199
2200+ static int _php_ibase_fetch_query_res (zval * from , ibase_query * * ib_query )
2201+ {
2202+ * ib_query = zend_fetch_resource_ex (from , LE_QUERY , le_query );
2203+
2204+ if (* ib_query == NULL ) {
2205+ // TODO: throw something or not? notice? warning?
2206+ php_error_docref (NULL , E_NOTICE , "query already freed" );
2207+ return FAILURE ;
2208+ }
2209+
2210+ return SUCCESS ;
2211+ }
2212+
2213+ // We can't rely on aliasname coming from XSQLVAR if we want long field names
2214+ // (>31). We also can't rely on parsing buffer from isc_dsql_sql_info() because
2215+ // it's 32KB limit can be easily overflown with combination of long field names
2216+ // and large amounts of fields. So I added wrapper to use newer API but that
2217+ // also require runtime fbclient > 40 hence the runtime checks. Ideally rewrite
2218+ // everything using newer API but that's a bit of work.
2219+ static int _php_ibase_alloc_ht_aliases (ibase_query * ib_query )
2220+ {
2221+ ALLOC_HASHTABLE (ib_query -> ht_aliases );
2222+ zend_hash_init (ib_query -> ht_aliases , ib_query -> out_fields_count , NULL , ZVAL_PTR_DTOR , 0 );
2223+
2224+ #if FB_API_VER >= 40
2225+ if (IBG (fb_get_master_interface ) && IBG (fb_get_statement_interface )) {
2226+ if (fb_insert_aliases (IB_STATUS , ib_query )){
2227+ return FAILURE ;
2228+ }
2229+ } else {
2230+ #endif
2231+ // Old API
2232+ for (size_t i = 0 ; i < ib_query -> out_fields_count ; i ++ ){
2233+ XSQLVAR * var = & ib_query -> out_sqlda -> sqlvar [i ];
2234+
2235+ _php_ibase_insert_alias (ib_query -> ht_aliases ,
2236+ var -> aliasname , MIN (31 , var -> aliasname_length ));
2237+ }
2238+ #if FB_API_VER >= 40
2239+ }
2240+ #endif
2241+
2242+ return SUCCESS ;
2243+ }
2244+
2245+ static void _php_ibase_alloc_ht_ind (ibase_query * ib_query )
2246+ {
2247+ ALLOC_HASHTABLE (ib_query -> ht_ind );
2248+ zend_hash_init (ib_query -> ht_ind , ib_query -> out_fields_count , NULL , ZVAL_PTR_DTOR , 0 );
2249+
2250+ zval t2 ;
2251+ ZVAL_NULL (& t2 );
2252+
2253+ for (size_t i = 0 ; i < ib_query -> out_fields_count ; i ++ ) {
2254+ zend_hash_index_add (ib_query -> ht_ind , i , & t2 );
2255+ }
2256+ }
2257+
2258+ static void _php_ibase_free_query_impl (INTERNAL_FUNCTION_PARAMETERS )
2259+ {
2260+ zval * query_arg ;
2261+ ibase_query * ib_query ;
2262+
2263+ RESET_ERRMSG ;
2264+
2265+ if (zend_parse_parameters (ZEND_NUM_ARGS (), "r" , & query_arg ) == FAILURE ) {
2266+ return ;
2267+ }
2268+
2269+ if (_php_ibase_fetch_query_res (query_arg , & ib_query )) {
2270+ return ;
2271+ }
2272+
2273+ zend_list_close (Z_RES_P (query_arg ));
2274+
2275+ RETURN_TRUE ;
2276+ }
2277+
22072278#endif /* HAVE_IBASE */
0 commit comments