#include "dbe_text.h"
void dbe_drv_free( drv_def_t *dbe_drv ) {
#if CSV_DEBUG > 1
debug_free();
#endif
}
void dbe_mem_free( void *ptr ) {
Safefree( ptr );
}
drv_con_t *dbe_drv_connect( dbe_con_t *dbe_con, char **args, int argc ) {
int i, j;
size_t l;
drv_con_t *con;
csv_t *csv;
char *key, *val, *s1;
dbe_str_t *extensions = NULL;
Newxz( con, 1, drv_con_t );
csv = csv_init( &con->csv );
for( i = 0; i < argc; i += 2 ) {
#ifdef CSV_DEBUG
_debug( "argument %d [%s] => %s\n", i / 2 + 1, args[i], args[i + 1] );
#endif
key = args[i];
val = args[i + 1];
if( my_stricmp( "DBQ", key ) == 0 ||
my_stricmp( "DEFAULTDIR", key ) == 0
) {
if( CSV_OK != csv_set_path( csv, val, 0 ) ) {
g_dbe->set_error(
dbe_con, csv->last_errno, csv->last_error );
goto error;
}
}
else if( my_stricmp( "USESCHEMA", key ) == 0 ) {
if( my_stricmp( "FALSE", val ) == 0 ||
my_stricmp( "NO", val ) == 0 ||
*val == '0'
)
csv->flags &= (~CSV_USESCHEMA);
else
csv->flags |= CSV_USESCHEMA;
}
else if( my_stricmp( "COLNAMEHEADER", key ) == 0 ) {
if( *val == '0' ||
my_stricmp( "FALSE", val ) == 0 ||
my_stricmp( "NO", val ) == 0
)
csv->flags &= (~CSV_COLNAMEHEADER);
else
csv->flags |= CSV_COLNAMEHEADER;
}
else if( my_stricmp( "QUOTECHAR", key ) == 0 ) {
if( *val != '0' )
csv->quote = *val;
}
else if( my_stricmp( "DECIMALSYMBOL", key ) == 0 ||
my_stricmp( "DEC", key ) == 0
) {
if( *val != '0' )
csv->decimal_symbol = *val;
}
else if( my_stricmp( "FORMAT", key ) == 0 ||
my_stricmp( "FMT", key ) == 0
) {
if( (val = strchr( val, '(' )) != NULL )
csv->delimiter = *(val + 1);
else if( my_stricmp( "CSVDELIMITED", val ) == 0 )
csv->delimiter = ',';
else if( my_stricmp( "TABDELIMITED", val ) == 0 )
csv->delimiter = '\t';
}
else if( my_stricmp( "DELIMITER", key ) == 0 ) {
if( *val != '0' )
csv->delimiter = *val;
}
else if( my_stricmp( "MAXSCANROWS", key ) == 0 ||
my_stricmp( "MSR", key ) == 0
) {
csv->max_scan_rows = atoi( val );
if( csv->max_scan_rows < 1 )
csv->max_scan_rows = 1;
}
else if( my_stricmp( "EXTENSIONS", key ) == 0 ) {
j = 0;
con->extensions_length = 0;
for( j = 0; *val != '\0'; ) {
for( ; isSPACE( *val ); val ++ );
for( s1 = val; *s1 != ',' && *s1 != '\0'; s1 ++ );
for( ; isSPACE( *s1 ); s1 -- );
if( s1 > val ) {
if( (j % 4) == 0 )
Renew( extensions, 4, dbe_str_t );
extensions[j].length = l = s1 - val;
con->extensions_length += l;
Newx( extensions[j].value, l + 1, char );
Copy( val, extensions[j].value, l, char );
extensions[j].value[l] = '\0';
#ifdef CSV_DEBUG
_debug( "found extension [%s]\n", extensions[j].value );
#endif
j ++;
}
if( *s1 == '\0' )
break;
val = s1 + 1;
}
con->extensions = extensions;
con->extensions_count = j;
}
else if( my_stricmp( "CHARSET", key ) == 0 ||
my_stricmp( "CHARACTERSET", key ) == 0
) {
enum n_charset csid = get_charset_id( val );
if( csid == CS_UNKNOWN ) {
g_dbe->set_error( con->pcon, CSV_ERR_UNKNOWNCHARSET,
"Unknown character set '%s'", val );
goto error;
}
con->csv.client_charset = csid;
}
}
con->pcon = dbe_con;
if( extensions == NULL ) {
Newx( extensions, 2, dbe_str_t );
extensions[0].length = 3;
Newx( extensions[0].value, 4, char );
my_strcpy( extensions[0].value, "csv" );
extensions[1].length = 3;
Newx( extensions[1].value, 4, char );
my_strcpy( extensions[1].value, "txt" );
con->extensions = extensions;
con->extensions_count = 2;
con->extensions_length = 6;
}
return con;
error:
Safefree( con );
return NULL;
}
int dbe_con_reconnect( drv_con_t *drv_con ) {
return DBE_OK;
}
int dbe_con_ping( drv_con_t *drv_con ) {
return DBE_OK;
}
void dbe_con_free( drv_con_t *con ) {
int i;
#ifdef CSV_DEBUG
_debug( "dbe_con_free 0x%x called\n", (size_t) con );
#endif
csv_free( &con->csv );
for( i = 0; i < con->extensions_count; i ++ )
Safefree( con->extensions[i].value );
Safefree( con->extensions );
Safefree( con );
}
int dbe_con_set_attr( drv_con_t *con, const char *name, SV *value ) {
char *str;
STRLEN len;
switch( toupper( *name ) ) {
case 'C':
if( my_stricmp( "CHARSET", name ) == 0 ||
my_stricmp( "CHARACTERSET", name ) == 0
) {
enum n_charset csid;
str = SvPV( value, len );
csid = get_charset_id( str );
if( csid == CS_UNKNOWN ) {
g_dbe->set_error( con->pcon,
CSV_ERR_UNKNOWNCHARSET, "Unknown character set '%s'", str );
return DBE_ERROR;
}
con->csv.client_charset = csid;
return DBE_OK;
}
break;
}
return DBE_INVALIDARG;
}
int dbe_con_get_attr( drv_con_t *con, const char *name, SV **rval ) {
switch( toupper( *name ) ) {
case 'C':
if( my_stricmp( "CHARSET", name ) == 0 ||
my_stricmp( "CHARACTERSET", name ) == 0
) {
*rval = newSVpv( get_charset_name( con->csv.client_charset ), 0 );
return DBE_OK;
}
break;
}
return DBE_INVALIDARG;
}
int dbe_con_query(
drv_con_t *con, const char *sql, size_t sql_len, dbe_res_t **dbe_res
) {
drv_res_t *res;
csv_stmt_t *stmt;
csv_result_t *result;
if( CSV_OK != csv_prepare( &con->csv, sql, sql_len, &stmt ) ) {
g_dbe->set_error(
con->pcon, con->csv.last_errno, con->csv.last_error );
return DBE_ERROR;
}
if( CSV_OK != csv_execute( stmt ) ) {
csv_stmt_free( stmt );
g_dbe->set_error(
con->pcon, con->csv.last_errno, con->csv.last_error );
return DBE_ERROR;
}
result = csv_get_result( stmt );
if( result != NULL ) {
Newxz( res, 1, drv_res_t );
res->con = con;
res->res = result;
(*dbe_res) = g_dbe->alloc_result(
con->pcon, res, (u_long) result->column_count );
}
csv_stmt_free( stmt );
return DBE_OK;
}
xlong dbe_con_affected_rows( drv_con_t *con ) {
return (xlong) con->csv.affected_rows;
}
int dbe_con_prepare(
drv_con_t *con, const char *sql, size_t sql_len, drv_stmt_t **drv_stmt
) {
drv_stmt_t *stmt;
csv_stmt_t *csv_stmt;
if( CSV_OK != csv_prepare( &con->csv, sql, sql_len, &csv_stmt ) ) {
g_dbe->set_error(
con->pcon, con->csv.last_errno, con->csv.last_error );
return DBE_ERROR;
}
Newxz( stmt, 1, drv_stmt_t );
stmt->stmt = csv_stmt;
stmt->con = con;
(*drv_stmt) = stmt;
return DBE_OK;
}
void dbe_stmt_free( drv_stmt_t *stmt ) {
csv_stmt_free( stmt->stmt );
Safefree( stmt );
}
int dbe_stmt_param_count( drv_stmt_t *stmt ) {
return (int) stmt->stmt->parse.qmark_count;
}
int dbe_stmt_bind_param( drv_stmt_t *stmt, u_long p_num, SV *val, char type ) {
int rv;
char *s;
STRLEN l;
switch( type ) {
case 'i':
rv = csv_bind_param_iv( stmt->stmt, (WORD) p_num, (int) SvIV( val ) );
goto check_rv;
case 'd':
rv = csv_bind_param_nv( stmt->stmt, (WORD) p_num, (double) SvNV( val ) );
goto check_rv;
case 's':
case 'b':
s = SvPV( val, l );
rv = csv_bind_param_sv( stmt->stmt, (WORD) p_num, s, l );
goto check_rv;
}
// auto detect
if( SvIOK( val ) ) {
rv = csv_bind_param_iv( stmt->stmt, (WORD) p_num, (int) SvIV( val ) );
}
else if( SvNOK( val ) ) {
rv = csv_bind_param_nv( stmt->stmt, (WORD) p_num, (double) SvNV( val ) );
}
else {
s = SvPV( val, l );
rv = csv_bind_param_sv( stmt->stmt, (WORD) p_num, s, l );
}
check_rv:
return rv == CSV_OK ? DBE_OK : DBE_ERROR;
}
int dbe_stmt_execute( drv_stmt_t *stmt, dbe_res_t **dbe_res ) {
drv_res_t *res;
drv_con_t *con = stmt->con;
csv_result_t *result;
if( CSV_OK != csv_execute( stmt->stmt ) ) {
g_dbe->set_error(
con->pcon, con->csv.last_errno, con->csv.last_error );
return DBE_ERROR;
}
result = csv_get_result( stmt->stmt );
if( result != NULL ) {
Newxz( res, 1, drv_res_t );
res->con = con;
res->res = result;
(*dbe_res) = g_dbe->alloc_result(
con->pcon, res, (u_long) result->column_count );
}
return DBE_OK;
}
void dbe_res_free( drv_res_t *res ) {
csv_result_free( res->res );
Safefree( res->fields );
Safefree( res );
}
xlong dbe_res_num_rows( drv_res_t *res ) {
return (xlong) res->res->row_count;
}
int dbe_res_fetch_names( drv_res_t *res, dbe_str_t *names, int *flags ) {
csv_result_t *result = res->res;
size_t i;
for( i = 0; i < result->column_count; i ++ ) {
names[i].value = result->columns[i].name;
names[i].length = result->columns[i].name_length;
}
return DBE_OK;
}
int dbe_res_fetch_row( drv_res_t *res, SV **prow, int names ) {
csv_result_t *result = res->res;
csv_row_t *row;
size_t i;
csv_var_t *v;
if( result->row_pos >= result->row_count )
return DBE_NO_DATA;
row = result->rows[result->row_pos ++];
for( i = 0; i < result->column_count; i ++ ) {
v = &row->data[i];
if( v->flags & VAR_HAS_IV )
prow[i] = newSViv( v->iv );
else if( v->flags & VAR_HAS_NV )
prow[i] = newSVnv( v->nv );
else if( v->flags & VAR_HAS_SV )
prow[i] = newSVpvn( v->sv, v->sv_len );
else
prow[i] = NULL;
}
return DBE_OK;
}
xlong dbe_res_row_seek( drv_res_t *res, u_xlong offset ) {
csv_result_t *result = res->res;
xlong lrp = result->row_pos;
if( offset >= result->row_count )
result->row_pos = result->row_count - 1;
else
result->row_pos = (u_long) offset;
return lrp;
}
xlong dbe_res_row_tell( drv_res_t *res ) {
return (xlong) res->res->row_pos;
}
#define FIELD_ITEM_COUNT 7 * 2
int dbe_res_fetch_field( drv_res_t *res, SV ***sv, int *flags ) {
csv_result_t *result = res->res;
csv_column_def_t *col;
SV **args;
if( result->column_pos >= result->column_count )
return 0;
col = result->columns + result->column_pos ++;
if( res->fields == NULL )
Newx( res->fields, FIELD_ITEM_COUNT, SV * );
args = res->fields;
args[0] = newSVpvn( "COLUMN_NAME", 11 );
args[1] = newSVpvn( col->name, col->name_length );
args[2] = newSVpvn( "TABLE_NAME", 10 );
args[3] = col->tablename_length
? newSVpvn( col->tablename, col->tablename_length ) : NULL;
args[4] = newSVpvn( "COLUMN_SIZE", 11 );
args[5] = newSVuv( col->size );
args[6] = newSVpvn( "COLUMN_DEF", 10 );
args[7] = newSVpvn( col->defval, col->defval_length );
args[8] = newSVpvn( "DATA_TYPE", 9 );
args[10] = newSVpvn( "TYPE_NAME", 9 );
switch( col->type ) {
case FIELD_TYPE_INTEGER:
args[9] = newSViv( SQL_INTEGER );
args[11] = newSVpvn( "INTEGER", 7 );
break;
case FIELD_TYPE_DOUBLE:
args[9] = newSViv( SQL_DOUBLE );
args[11] = newSVpvn( "DOUBLE", 6 );
break;
case FIELD_TYPE_BLOB:
args[9] = newSViv( SQL_BLOB );
args[11] = newSVpvn( "BLOB", 4 );
break;
case FIELD_TYPE_CHAR:
default:
args[9] = newSViv( SQL_CHAR );
args[11] = newSVpvn( "CHAR", 4 );
break;
}
args[12] = newSVpvn( "NULLABLE", 8 );
args[13] = newSViv( 1 );
(*sv) = args;
//(*flags) = DBE_FREE_ARRAY;
return FIELD_ITEM_COUNT;
}
u_long dbe_res_field_seek( drv_res_t *res, u_long offset ) {
csv_result_t *result = res->res;
u_long lcp = result->column_pos;
if( offset > result->column_count )
result->column_pos = result->column_count;
else
result->column_pos = offset;
return lcp;
}
u_long dbe_res_field_tell( drv_res_t *res ) {
return res->res->column_pos;
}
SV *dbe_con_quote( drv_con_t *con, const char *str, size_t str_len ) {
return charset_quote( con->csv.client_charset, str, str_len );
}
SV *dbe_con_quote_bin(
drv_con_t *con, const char *bin, size_t bin_len
) {
char *tmp, *s1;
const unsigned char *s2, *s4;
SV *ret;
Newx( tmp, bin_len * 2 + 2, char );
s1 = tmp, *s1 ++ = '0', *s1 ++ = 'x';
for( s2 = (unsigned char *) bin, s4 = s2 + bin_len; s2 < s4; s2 ++ ) {
*s1 ++ = HEX_FROM_CHAR[*s2 / 16];
*s1 ++ = HEX_FROM_CHAR[*s2 % 16];
}
ret = newSVpvn( tmp, s1 - tmp );
Safefree( tmp );
return ret;
}
SV *dbe_con_quote_id( drv_con_t *con, const char **args, int argc ) {
return charset_quote_id( con->csv.client_charset, args, argc );
}
int dbe_con_getinfo (
drv_con_t *con, int type, void *val, int cbvalmax, int *pcbval,
enum dbe_getinfo_type *rtype
) {
switch( type ) {
case SQL_IDENTIFIER_QUOTE_CHAR:
*rtype = DBE_GETINFO_STRING, *pcbval = 1;
*((char *) val) = con->csv.quote, *((char *) val + 1) = '\0';
break;
case SQL_DRIVER_NAME:
*pcbval = (int) strlen( DRV_NAME );
*rtype = DBE_GETINFO_STRING;
if( cbvalmax <= *pcbval )
return DBE_GETINFO_TRUNCATE;
my_strcpy( (char *) val, DRV_NAME );
break;
case SQL_DRIVER_VER:
case SQL_DBMS_VER:
*pcbval = (int) strlen( XS_VERSION );
*rtype = DBE_GETINFO_STRING;
if( cbvalmax <= *pcbval )
return DBE_GETINFO_TRUNCATE;
my_strcpy( (char *) val, XS_VERSION );
break;
case SQL_SERVER_NAME:
*pcbval = (int) strlen( g_drv_def.description );
*rtype = DBE_GETINFO_STRING;
if( cbvalmax <= *pcbval )
return DBE_GETINFO_TRUNCATE;
my_strcpy( (char *) val, g_drv_def.description );
break;
case SQL_DBMS_NAME:
*rtype = DBE_GETINFO_STRING, *pcbval = 4;
my_strcpy( (char *) val, "Text" );
break;
case SQL_AGGREGATE_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM;
break;
case SQL_NUMERIC_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_FN_NUM_ABS | SQL_FN_NUM_ROUND;
break;
case SQL_KEYWORDS:
case SQL_PROCEDURE_TERM:
case SQL_SCHEMA_TERM:
case SQL_SPECIAL_CHARACTERS:
case SQL_USER_NAME:
*rtype = DBE_GETINFO_STRING, *pcbval = 0;
*((char *) val) = '\0';
break;
case SQL_COLUMN_ALIAS:
case SQL_EXPRESSIONS_IN_ORDERBY:
case SQL_LIKE_ESCAPE_CLAUSE:
case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
case SQL_ACCESSIBLE_TABLES:
*rtype = DBE_GETINFO_STRING, *pcbval = 1;
*((char *) val) = 'Y', *((char *) val + 1) = '\0';
break;
case SQL_CATALOG_NAME:
case SQL_DESCRIBE_PARAMETER:
case SQL_MULT_RESULT_SETS:
case SQL_NEED_LONG_DATA_LEN:
case SQL_ORDER_BY_COLUMNS_IN_SELECT:
case SQL_PROCEDURES:
case SQL_ACCESSIBLE_PROCEDURES:
*rtype = DBE_GETINFO_STRING, *pcbval = 1;
*((char *) val) = 'N', *((char *) val + 1) = '\0';
break;
case SQL_ALTER_DOMAIN:
case SQL_ALTER_TABLE:
case SQL_CREATE_SCHEMA:
case SQL_CREATE_VIEW:
case SQL_DROP_SCHEMA:
case SQL_DROP_VIEW:
case SQL_DDL_INDEX:
case SQL_INDEX_KEYWORDS:
case SQL_MAX_CHAR_LITERAL_LEN:
case SQL_MAX_INDEX_SIZE:
case SQL_MAX_ROW_SIZE:
case SQL_MAX_STATEMENT_LEN:
case SQL_SCHEMA_USAGE:
case SQL_SQL92_FOREIGN_KEY_UPDATE_RULE:
case SQL_SQL92_GRANT:
case SQL_SUBQUERIES:
case SQL_SYSTEM_FUNCTIONS:
case SQL_TXN_ISOLATION_OPTION:
case SQL_UNION:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = 0;
break;
case SQL_CURSOR_COMMIT_BEHAVIOR:
case SQL_CURSOR_ROLLBACK_BEHAVIOR:
case SQL_MAX_CATALOG_NAME_LEN:
case SQL_MAX_COLUMN_NAME_LEN:
case SQL_MAX_COLUMNS_IN_GROUP_BY:
case SQL_MAX_COLUMNS_IN_INDEX:
case SQL_MAX_COLUMNS_IN_ORDER_BY:
case SQL_MAX_COLUMNS_IN_SELECT:
case SQL_MAX_COLUMNS_IN_TABLE:
case SQL_MAX_CURSOR_NAME_LEN:
case SQL_MAX_IDENTIFIER_LEN:
case SQL_MAX_SCHEMA_NAME_LEN:
case SQL_MAX_TABLE_NAME_LEN:
case SQL_MAX_TABLES_IN_SELECT:
case SQL_MAX_USER_NAME_LEN:
case SQL_TXN_CAPABLE:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = 0;
break;
case SQL_CATALOG_LOCATION:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_CL_START;
break;
case SQL_CATALOG_NAME_SEPARATOR:
*rtype = DBE_GETINFO_STRING, *pcbval = 1;
*((char *) val) = '/', *((char *) val + 1) = '\0';
break;
case SQL_CATALOG_TERM:
*pcbval = 9;
*rtype = DBE_GETINFO_STRING;
my_strcpy( (char *) val, "directory" );
break;
case SQL_CATALOG_USAGE:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_CU_DML_STATEMENTS | SQL_CU_TABLE_DEFINITION;
break;
case SQL_CONCAT_NULL_BEHAVIOR:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_CB_NULL;
break;
case SQL_CONVERT_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_FN_CVT_CONVERT;
break;
case SQL_CORRELATION_NAME:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_CN_ANY;
break;
case SQL_CREATE_TABLE:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_CT_CREATE_TABLE | SQL_CT_COLUMN_DEFAULT;
break;
case SQL_DROP_TABLE:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_DT_DROP_TABLE;
break;
case SQL_DATA_SOURCE_NAME:
*pcbval = (int) con->csv.path_length;
*rtype = DBE_GETINFO_STRING;
if( cbvalmax <= *pcbval )
return DBE_GETINFO_TRUNCATE;
my_strcpy( (char *) val, con->csv.path );
break;
case SQL_GROUP_BY:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_GB_NO_RELATION;
break;
case SQL_IDENTIFIER_CASE:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_IC_MIXED;
break;
case SQL_INSERT_STATEMENT:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED;
break;
case SQL_NON_NULLABLE_COLUMNS:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_NNC_NON_NULL;
break;
case SQL_NULL_COLLATION:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_NC_LOW;
break;
case SQL_OJ_CAPABILITIES:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = 0;
break;
case SQL_QUOTED_IDENTIFIER_CASE:
*rtype = DBE_GETINFO_SHORTINT, *pcbval = 2;
*((short *) val) = SQL_IC_SENSITIVE;
break;
case SQL_SEARCH_PATTERN_ESCAPE:
*rtype = DBE_GETINFO_STRING, *pcbval = 1;
*((char *) val) = '\\', *((char *) val + 1) = '\0';
break;
case SQL_SQL_CONFORMANCE:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_SC_SQL92_ENTRY;
break;
case SQL_SQL92_DATETIME_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_SDF_CURRENT_DATE | SQL_SDF_CURRENT_TIME |
SQL_SDF_CURRENT_TIMESTAMP;
break;
case SQL_SQL92_PREDICATES:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_SP_EXISTS | SQL_SP_ISNOTNULL | SQL_SP_ISNULL | SQL_SP_LIKE |
SQL_SP_BETWEEN | SQL_SP_COMPARISON;
break;
case SQL_SQL92_RELATIONAL_JOIN_OPERATORS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = 0;
break;
case SQL_SQL92_ROW_VALUE_CONSTRUCTOR:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) = SQL_SRVC_VALUE_EXPRESSION | SQL_SRVC_NULL;
break;
case SQL_SQL92_STRING_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_SSF_LOWER | SQL_SSF_UPPER | SQL_SSF_SUBSTRING |
SQL_SSF_TRIM_BOTH | SQL_SSF_TRIM_LEADING | SQL_SSF_TRIM_TRAILING;
break;
case SQL_STRING_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_FN_STR_CONCAT | SQL_FN_STR_LTRIM | SQL_FN_STR_LENGTH |
SQL_FN_STR_LOCATE | SQL_FN_STR_LCASE | SQL_FN_STR_RTRIM |
SQL_FN_STR_SUBSTRING | SQL_FN_STR_UCASE | SQL_FN_STR_ASCII |
SQL_FN_STR_LOCATE_2 | SQL_FN_STR_CHAR_LENGTH |
SQL_FN_STR_CHARACTER_LENGTH | SQL_FN_STR_OCTET_LENGTH |
SQL_FN_STR_POSITION;
break;
case SQL_TABLE_TERM:
*rtype = DBE_GETINFO_STRING, *pcbval = 4;
my_strcpy( (char *) val, "file" );
break;
case SQL_TIMEDATE_FUNCTIONS:
*rtype = DBE_GETINFO_32BITMASK, *pcbval = 4;
*((unsigned int *) val) =
SQL_FN_TD_NOW | SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME |
SQL_FN_TD_CURRENT_TIMESTAMP;
break;
default:
return DBE_GETINFO_INVALIDARG;
}
return DBE_OK;
}
#define SET_FIELD(str,sdata,slen) \
str.value = (char *) (sdata), str.length = (size_t) (slen)
int dbe_con_type_info( drv_con_t *con, int type, dbe_res_t **dbe_res ) {
dbe_res_t *vres;
int type_count, i, data_type, col_size, case_sens, searchable, mins, maxs;
int radix, nullable, typid;
const int *types;
const int d_types[] = { SQL_BLOB, SQL_DOUBLE, SQL_INTEGER, SQL_VARCHAR };
dbe_str_t data[19];
Zero( data, 19, dbe_str_t );
vres = g_dbe->vres_create( con->pcon );
g_dbe->vres_add_column( vres, "TYPE_NAME", 9, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( vres, "DATA_TYPE", 9, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "COLUMN_SIZE", 11, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "LITERAL_PREFIX", 14, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( vres, "LITERAL_SUFFIX", 14, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( vres, "CREATE_PARAMS", 13, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( vres, "NULLABLE", 8, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "CASE_SENSITIVE", 14, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "SEARCHABLE", 10, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "UNSIGNED_ATTRIBUTE", 18, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "FIXED_PREC_SCALE", 16, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "AUTO_UNIQUE_VALUE", 17, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "LOCAL_TYPE_NAME", 15, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( vres, "MINIMUM_SCALE", 13, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "MAXIMUM_SCALE", 13, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "SQL_DATATYPE", 12, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "SQL_DATETIME_SUB", 16, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "NUM_PREC_RADIX", 14, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( vres, "INTERVAL_PRECISION", 18, DBE_TYPE_INTEGER );
if( type == SQL_ALL_TYPES )
types = d_types, type_count = 4;
else
typid = (int) type, types = &typid, type_count = 1;
data[1].value = (char *) &data_type;
data[2].value = (char *) &col_size;
data[6].value = (char *) &nullable, nullable = 1;
data[7].value = (char *) &case_sens;
data[8].value = (char *) &searchable, searchable = SQL_SEARCHABLE;
data[15].value = data[1].value;
for( i = 0; i < type_count; i ++ ) {
switch( types[i] ) {
case SQL_TINYINT:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_BIGINT:
SET_FIELD( data[0], "INTEGER", 7 );
data_type = SQL_INTEGER;
col_size = 20;
SET_FIELD( data[3], NULL, 0 );
SET_FIELD( data[4], NULL, 0 );
case_sens = 0;
data[13].value = data[14].value = NULL;
data[17].value = (char *) &radix, radix = 10;
break;
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
case SQL_NUMERIC:
case SQL_DECIMAL:
SET_FIELD( data[0], "DOUBLE", 6 );
data_type = SQL_DOUBLE;
col_size = 17;
SET_FIELD( data[3], NULL, 0 );
SET_FIELD( data[4], NULL, 0 );
case_sens = 0;
data[13].value = (char *) &mins, mins = 0;
data[14].value = (char *) &maxs, maxs = 15;
data[17].value = (char *) &radix, radix = 2;
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_CLOB:
SET_FIELD( data[0], "TEXT", 4 );
data_type = SQL_VARCHAR;
col_size = 0x7fffffff;
SET_FIELD( data[3], "'", 1 );
SET_FIELD( data[4], "'", 1 );
case_sens = 1;
data[17].value = data[14].value = data[13].value = NULL;
break;
case SQL_BLOB:
case SQL_BINARY:
case SQL_VARBINARY:
default:
SET_FIELD( data[0], "BLOB", 4 );
data_type = SQL_VARBINARY;
col_size = 0x7fffffff;
SET_FIELD( data[3], "0x", 2 );
SET_FIELD( data[4], NULL, 0 );
case_sens = 1;
data[17].value = data[14].value = data[13].value = NULL;
break;
}
g_dbe->vres_add_row( vres, data );
}
(*dbe_res) = vres;
return DBE_OK;
}
int dbe_con_tables(
drv_con_t *con, const char *catalog, size_t catalog_len, const char *schema,
size_t schema_len, const char *table, size_t table_len, const char *type,
size_t type_len, dbe_res_t **dbe_res
) {
regexp *re_tb = NULL;
char tmp[1024], *s1;
DIR *dh;
Direntry_t *de;
dbe_res_t *pres = NULL;
dbe_str_t data[5];
size_t len;
int r;
if( table_len > 0 )
re_tb = g_dbe->regexp_from_pattern( table, table_len, TRUE );
else {
s1 = my_strcpy( tmp, "/\\.(" );
for( r = 0; r < con->extensions_count; r ++ ) {
s1 = my_strcpy( s1, con->extensions[r].value );
*s1 ++ = '|';
}
s1 = my_strcpy( s1 - 1, ")$/i" );
re_tb = g_dbe->regexp_from_string( tmp, s1 - tmp );
}
if( con->csv.path != NULL )
s1 = my_strcpy( tmp, con->csv.path );
else
s1 = my_strcpy( tmp, "." );
if( (dh = PerlDir_open( tmp )) == NULL ) {
g_dbe->set_error( con->pcon,
-1, "Unable to open directory '%s'", tmp );
return DBE_ERROR;
}
pres = g_dbe->vres_create( con->pcon );
g_dbe->vres_add_column( pres, "TABLE_CAT", 9, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "TABLE_SCHEM", 11, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "TABLE_NAME", 10, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "TABLE_TYPE", 10, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "REMARKS", 7, DBE_TYPE_VARCHAR );
SET_FIELD( data[0], NULL, 0 );
SET_FIELD( data[1], NULL, 0 );
SET_FIELD( data[3], "TABLE", 5 );
SET_FIELD( data[4], NULL, 0 );
while( (de = PerlDir_read( dh )) != NULL ) {
if( de->d_name[0] == '.' )
continue;
len = strlen( de->d_name );
if( ! g_dbe->regexp_match( re_tb, de->d_name, len ) )
continue;
#ifdef CSV_DEBUG
_debug( "entry %s\n", de->d_name );
#endif
SET_FIELD( data[2], de->d_name, len );
if( g_dbe->vres_add_row( pres, data ) != DBE_OK )
goto error;
}
(*dbe_res) = pres;
r = DBE_OK;
goto exit;
error:
r = DBE_ERROR;
exit:
PerlDir_close( dh );
g_dbe->regexp_free( re_tb );
return r;
}
int dbe_con_columns(
drv_con_t *con, const char *catalog, size_t catalog_len, const char *schema,
size_t schema_len, const char *table, size_t table_len, const char *column,
size_t column_len, dbe_res_t **dbe_res
) {
regexp *re_tb = NULL, *re_cl = NULL;
int sft, tc = 0, i, j, r, radix, ord, dec, bl, ocl, nullable;
char **tn = NULL, tmp[1024], *s1, *tablename = NULL;
DIR *dh;
Direntry_t *de;
size_t len;
csv_table_def_t *table_def;
struct st_csv_parse parse;
dbe_res_t *pres = NULL;
csv_column_def_t *col;
dbe_str_t data[18];
Zero( data, 18, dbe_str_t );
Zero( &parse, 1, struct st_csv_parse );
parse.csv = &con->csv;
if( table_len ) {
re_tb = g_dbe->regexp_from_pattern( table, table_len, FALSE );
if( re_tb == NULL ) {
sft = 0;
Newx( tablename, table_len + 1, char );
Copy( table, tablename, table_len + 1, char );
table_len = g_dbe->unescape_pattern( tablename, table_len, '\\' );
table = tablename;
}
else {
sft = 1;
}
}
else
sft = 1;
if( sft ) {
if( con->csv.path != NULL )
s1 = my_strcpy( tmp, con->csv.path );
else
s1 = my_strcpy( tmp, "." );
if( (dh = PerlDir_open( tmp )) == NULL ) {
g_dbe->set_error( con->pcon,
-1, "Unable to open directory '%s'", tmp );
return DBE_ERROR;
}
while( (de = PerlDir_read( dh )) != NULL ) {
if( de->d_name[0] == '.' )
continue;
len = strlen( de->d_name );
if( ! g_dbe->regexp_match( re_tb, de->d_name, len ) )
continue;
#ifdef CSV_DEBUG
_debug( "entry %s\n", de->d_name );
#endif
if( (tc % 8) == 0 )
Renew( tn, tc + 8, char * );
Newx( tn[tc], len + 1, char );
strcpy( tn[tc], de->d_name );
tc ++;
}
}
else {
Newx( tn, 1, char * );
tn[0] = (char *) table;
tc = 1;
}
if( column_len )
re_cl = g_dbe->regexp_from_pattern( column, column_len, TRUE );
pres = g_dbe->vres_create( con->pcon );
g_dbe->vres_add_column( pres, "TABLE_CAT", 9, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "TABLE_SCHEM", 11, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "TABLE_NAME", 10, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "COLUMN_NAME", 11, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "DATA_TYPE", 9, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "TYPE_NAME", 9, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "COLUMN_SIZE", 11, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "BUFFER_LENGTH", 13, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "DECIMAL_DIGITS", 14, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "NUM_PREC_RADIX", 14, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "NULLABLE", 8, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "REMARKS", 7, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "COLUMN_DEF", 10, DBE_TYPE_VARCHAR );
g_dbe->vres_add_column( pres, "SQL_DATA_TYPE", 13, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "SQL_DATETIME_SUB", 16, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "CHAR_OCTET_LENGTH", 17, DBE_TYPE_BIGINT );
g_dbe->vres_add_column( pres, "ORDINAL_POSITION", 16, DBE_TYPE_INTEGER );
g_dbe->vres_add_column( pres, "IS_NULLABLE", 11, DBE_TYPE_VARCHAR );
data[7].value = (char *) &bl;
data[10].value = (char *) &nullable, nullable = 1;
data[16].value = (char *) ⩝
SET_FIELD( data[17], "YES", 3 );
for( i = 0; i < tc; i ++ ) {
table_def = csv_open_table( &parse, tn[i], 0, NULL, 0, "r" );
if( table_def == NULL )
goto error;
SET_FIELD( data[2], table_def->name, table_def->name_length );
for( j = 0; j < (int) table_def->column_count; j ++ ) {
col = &table_def->columns[j];
if( re_cl != NULL ) {
if( ! g_dbe->regexp_match( re_cl, col->name, col->name_length ) )
continue;
}
SET_FIELD( data[3], col->name, col->name_length );
data[4].value = (char *) &col->type;
SET_FIELD( data[5], col->typename, col->typename_length );
data[6].value = (char *) &col->size;
switch( col->type ) {
case FIELD_TYPE_INTEGER:
bl = sizeof(int);
radix = 10;
data[8].value = NULL;
data[9].value = (char *) &radix;
data[15].value = NULL;
break;
case FIELD_TYPE_DOUBLE:
bl = sizeof(double);
radix = 2;
data[8].value = (char *) &dec, dec = 15;
data[9].value = (char *) &radix;
data[15].value = NULL;
break;
case FIELD_TYPE_CHAR:
bl = (int) col->size * 3 + 1;
data[8].value = NULL;
data[9].value = NULL;
data[15].value = (char *) &ocl, ocl = (int) col->size * 3;
break;
default:
bl = (int) col->size;
data[8].value = NULL;
data[9].value = NULL;
data[15].value = (char *) &ocl, ocl = (int) col->size;
break;
}
SET_FIELD( data[12], col->defval, col->defval_length );
data[13].value = data[4].value;
ord = j + 1;
if( g_dbe->vres_add_row( pres, data ) != DBE_OK )
goto error;
}
csv_table_def_free( table_def );
}
r = DBE_OK;
goto exit;
error:
r = DBE_ERROR;
exit:
if( sft ) {
for( tc --; tc >= 0; tc -- )
Safefree( tn[tc] );
}
Safefree( tn );
Safefree( tablename );
if( re_tb != NULL )
g_dbe->regexp_free( re_tb );
if( re_cl != NULL )
g_dbe->regexp_free( re_cl );
(*dbe_res) = pres;
return r;
}
/* global pointer to dbe structure */
const dbe_t *g_dbe = NULL;
/*
_g_drv_def ()
*/
/* driver definition structure */
const drv_def_t g_drv_def = {
DBE_VERSION,
DRV_NAME " " XS_VERSION " build " DRV_BUILD_STRING,
dbe_mem_free,
dbe_drv_free,
dbe_drv_connect,
dbe_con_free,
dbe_con_ping,
dbe_con_reconnect,
NULL, /* con_set_trace */
dbe_con_set_attr,
dbe_con_get_attr,
NULL, /* con_row_limit */
dbe_con_query,
NULL,
dbe_con_affected_rows,
NULL,
NULL,
NULL,
NULL,
dbe_con_prepare,
dbe_stmt_free,
dbe_stmt_param_count,
dbe_stmt_bind_param,
NULL, /* stmt_param_position */
dbe_stmt_execute,
dbe_res_free,
dbe_res_num_rows,
dbe_res_fetch_names,
dbe_res_fetch_row,
dbe_res_row_seek,
dbe_res_row_tell,
dbe_res_fetch_field,
dbe_res_field_seek,
dbe_res_field_tell,
NULL, /* lob_size */
NULL, /* lob_read */
NULL, /* lob_write */
NULL, /* lob_close */
dbe_con_quote,
dbe_con_quote_bin,
dbe_con_quote_id,
dbe_con_getinfo,
dbe_con_type_info,
NULL, /* con_data_sources */
dbe_con_tables,
dbe_con_columns
};