mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-17 00:35:55 +01:00
553 lines
13 KiB
C
553 lines
13 KiB
C
/*
|
|
* Program type: API Interface
|
|
*
|
|
* Description:
|
|
* This program prompts for and executes unknown SQL statements.
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
#include <ctype.h>
|
|
#include <ibase.h>
|
|
#include "align.h"
|
|
#include "example.h"
|
|
|
|
#define MAXLEN 1024
|
|
|
|
process_statement (XSQLDA ** sqlda, char *query);
|
|
void print_column (XSQLVAR * var);
|
|
int get_statement (char * buf);
|
|
|
|
/* ibase.h contains PARAMVARY, with the handicap that vary_string is unsigned char*
|
|
instead of plain char*.
|
|
Maybe we should fix ibase.h for users but without affecting the engine */
|
|
/* typedef struct vary2 {
|
|
unsigned short vary_length;
|
|
char vary_string [1];
|
|
} VARY2;
|
|
*/
|
|
typedef PARAMVARY VARY2;
|
|
|
|
isc_db_handle db = NULL;
|
|
isc_tr_handle trans = NULL;
|
|
isc_stmt_handle stmt = NULL;
|
|
ISC_STATUS_ARRAY status;
|
|
int ret;
|
|
|
|
#ifndef ISC_INT64_FORMAT
|
|
|
|
/* Define a format string for printf. Printing of 64-bit integers
|
|
is not standard between platforms */
|
|
|
|
#if (defined(_MSC_VER) && defined(WIN32))
|
|
#define ISC_INT64_FORMAT "I64"
|
|
#else
|
|
#define ISC_INT64_FORMAT "ll"
|
|
#endif
|
|
#endif
|
|
|
|
|
|
int main (int argc, char** argv)
|
|
{
|
|
int query[MAXLEN];
|
|
XSQLDA * sqlda;
|
|
char db_name[128];
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("Enter the database name: ");
|
|
gets(db_name);
|
|
}
|
|
else
|
|
{
|
|
strcpy(db_name, argv[1]);
|
|
}
|
|
|
|
if (isc_attach_database(status, 0, db_name, &db, 0, NULL))
|
|
{
|
|
printf("Could not open database %s\n", db_name);
|
|
ERREXIT(status, 1);
|
|
}
|
|
|
|
/*
|
|
* Allocate enough space for 20 fields.
|
|
* If more fields get selected, re-allocate SQLDA later.
|
|
*/
|
|
sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH (20));
|
|
sqlda->sqln = 20;
|
|
sqlda->version = 1;
|
|
|
|
/* Allocate a global statement */
|
|
if (isc_dsql_allocate_statement(status, &db, &stmt))
|
|
{
|
|
free (sqlda);
|
|
ERREXIT(status,1)
|
|
}
|
|
|
|
/*
|
|
* Process SQL statements.
|
|
*/
|
|
ret = get_statement((char *) query);
|
|
/* Use break on error or exit */
|
|
while (ret != 1)
|
|
{
|
|
/* We must pass the address of sqlda, in case it
|
|
** gets re-allocated
|
|
*/
|
|
ret = process_statement(&sqlda,
|
|
(char *) query);
|
|
if (ret == 1)
|
|
break;
|
|
ret = get_statement((char *) query);
|
|
}
|
|
|
|
free (sqlda);
|
|
if (trans)
|
|
if (isc_commit_transaction(status, &trans))
|
|
{
|
|
ERREXIT(status,1);
|
|
}
|
|
|
|
if (isc_detach_database(status, &db))
|
|
{
|
|
ERREXIT(status,1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
** Function: process_statement
|
|
** Process submitted statement. On any fundamental error, return status 1,
|
|
** which will do an isc_print_status and exit the program.
|
|
** On user errors, found in parsing or executing go to status 2,
|
|
** which will print the error and continue.
|
|
*/
|
|
|
|
process_statement (XSQLDA **sqldap, char *query)
|
|
{
|
|
int buffer[MAXLEN];
|
|
XSQLDA *sqlda;
|
|
XSQLVAR *var;
|
|
short num_cols, i;
|
|
short length, alignment, type, offset;
|
|
int fetch_stat;
|
|
static char stmt_info[] = { isc_info_sql_stmt_type };
|
|
char info_buffer[20];
|
|
short l;
|
|
int statement_type;
|
|
|
|
sqlda = *sqldap;
|
|
|
|
/* Start a transaction if we are not in one */
|
|
if (!trans)
|
|
if (isc_start_transaction(status, &trans, 1, &db, 0, NULL))
|
|
{
|
|
ERREXIT(status, 1)
|
|
}
|
|
|
|
if (isc_dsql_prepare(status, &trans, &stmt, 0, query, SQL_DIALECT_V6, sqlda))
|
|
{
|
|
ERREXIT(status,2)
|
|
}
|
|
|
|
/* What is the statement type of this statement?
|
|
**
|
|
** stmt_info is a 1 byte info request. info_buffer is a buffer
|
|
** large enough to hold the returned info packet
|
|
** The info_buffer returned contains a isc_info_sql_stmt_type in the first byte,
|
|
** two bytes of length, and a statement_type token.
|
|
*/
|
|
if (!isc_dsql_sql_info(status, &stmt, sizeof (stmt_info), stmt_info,
|
|
sizeof (info_buffer), info_buffer))
|
|
{
|
|
l = (short) isc_vax_integer((char *) info_buffer + 1, 2);
|
|
statement_type = isc_vax_integer((char *) info_buffer + 3, l);
|
|
}
|
|
|
|
|
|
/*
|
|
* Execute a non-select statement.
|
|
*/
|
|
if (!sqlda->sqld)
|
|
{
|
|
if (isc_dsql_execute(status, &trans, &stmt, SQL_DIALECT_V6, NULL))
|
|
{
|
|
ERREXIT(status,2)
|
|
}
|
|
|
|
/* Commit DDL statements if that is what sql_info says */
|
|
|
|
if (trans && (statement_type == isc_info_sql_stmt_ddl))
|
|
{
|
|
printf ("\tCommitting...\n");
|
|
if (isc_commit_transaction(status, &trans))
|
|
{
|
|
ERREXIT(status, 2)
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Process select statements.
|
|
*/
|
|
|
|
num_cols = sqlda->sqld;
|
|
|
|
/* Need more room. */
|
|
if (sqlda->sqln < num_cols)
|
|
{
|
|
*sqldap = sqlda = (XSQLDA *) realloc(sqlda,
|
|
XSQLDA_LENGTH (num_cols));
|
|
sqlda->sqln = num_cols;
|
|
sqlda->version = 1;
|
|
|
|
if (isc_dsql_describe(status, &stmt, SQL_DIALECT_V6, sqlda))
|
|
{
|
|
ERREXIT(status,2)
|
|
}
|
|
|
|
num_cols = sqlda->sqld;
|
|
}
|
|
|
|
/*
|
|
* Set up SQLDA.
|
|
*/
|
|
for (var = sqlda->sqlvar, offset = 0, i = 0; i < num_cols; var++, i++)
|
|
{
|
|
length = alignment = var->sqllen;
|
|
type = var->sqltype & ~1;
|
|
|
|
if (type == SQL_TEXT)
|
|
alignment = 1;
|
|
else if (type == SQL_VARYING)
|
|
{
|
|
length += sizeof (short) + 1;
|
|
alignment = sizeof (short);
|
|
}
|
|
/* RISC machines are finicky about word alignment
|
|
** So the output buffer values must be placed on
|
|
** word boundaries where appropriate
|
|
*/
|
|
offset = FB_ALIGN(offset, alignment);
|
|
var->sqldata = (char *) buffer + offset;
|
|
offset += length;
|
|
offset = FB_ALIGN(offset, sizeof (short));
|
|
var->sqlind = (short*) ((char *) buffer + offset);
|
|
offset += sizeof (short);
|
|
}
|
|
|
|
if (isc_dsql_execute(status, &trans, &stmt, SQL_DIALECT_V6, NULL))
|
|
{
|
|
ERREXIT(status,2)
|
|
}
|
|
|
|
/*
|
|
* Print rows.
|
|
*/
|
|
|
|
while ((fetch_stat = isc_dsql_fetch(status, &stmt, SQL_DIALECT_V6, sqlda)) == 0)
|
|
{
|
|
for (i = 0; i < num_cols; i++)
|
|
{
|
|
print_column((XSQLVAR *) &sqlda->sqlvar[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Close cursor */
|
|
if (isc_dsql_free_statement(status, &stmt, DSQL_close))
|
|
{
|
|
ERREXIT (status,2);
|
|
}
|
|
|
|
if (fetch_stat != 100L)
|
|
{
|
|
ERREXIT(status,2)
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Print column's data.
|
|
*/
|
|
void print_column (XSQLVAR *var)
|
|
{
|
|
short dtype;
|
|
char data[MAXLEN], *p;
|
|
char blob_s[20], date_s[25];
|
|
VARY2 *vary2;
|
|
short len;
|
|
struct tm times;
|
|
ISC_QUAD bid;
|
|
|
|
dtype = var->sqltype & ~1;
|
|
p = data;
|
|
|
|
/* Null handling. If the column is nullable and null */
|
|
if ((var->sqltype & 1) && (*var->sqlind < 0))
|
|
{
|
|
switch (dtype)
|
|
{
|
|
case SQL_TEXT:
|
|
case SQL_VARYING:
|
|
len = var->sqllen;
|
|
break;
|
|
case SQL_SHORT:
|
|
len = 6;
|
|
if (var->sqlscale > 0) len += var->sqlscale;
|
|
break;
|
|
case SQL_LONG:
|
|
len = 11;
|
|
if (var->sqlscale > 0) len += var->sqlscale;
|
|
break;
|
|
case SQL_INT64:
|
|
len = 21;
|
|
if (var->sqlscale > 0) len += var->sqlscale;
|
|
break;
|
|
case SQL_FLOAT:
|
|
len = 15;
|
|
break;
|
|
case SQL_DOUBLE:
|
|
len = 24;
|
|
break;
|
|
case SQL_TIMESTAMP:
|
|
len = 24;
|
|
break;
|
|
case SQL_TYPE_DATE:
|
|
len = 10;
|
|
break;
|
|
case SQL_TYPE_TIME:
|
|
len = 13;
|
|
break;
|
|
case SQL_BLOB:
|
|
case SQL_ARRAY:
|
|
default:
|
|
len = 17;
|
|
break;
|
|
}
|
|
if ((dtype == SQL_TEXT) || (dtype == SQL_VARYING))
|
|
sprintf(p, "%-*s ", len, "NULL");
|
|
else
|
|
sprintf(p, "%*s ", len, "NULL");
|
|
}
|
|
else
|
|
{
|
|
switch (dtype)
|
|
{
|
|
case SQL_TEXT:
|
|
sprintf(p, "%.*s ", var->sqllen, var->sqldata);
|
|
break;
|
|
|
|
case SQL_VARYING:
|
|
vary2 = (VARY2*) var->sqldata;
|
|
vary2->vary_string[vary2->vary_length] = '\0';
|
|
sprintf(p, "%-*s ", var->sqllen, vary2->vary_string);
|
|
break;
|
|
|
|
case SQL_SHORT:
|
|
case SQL_LONG:
|
|
case SQL_INT64:
|
|
{
|
|
ISC_INT64 value;
|
|
short field_width;
|
|
short dscale;
|
|
switch (dtype)
|
|
{
|
|
case SQL_SHORT:
|
|
value = (ISC_INT64) *(short *) var->sqldata;
|
|
field_width = 6;
|
|
break;
|
|
case SQL_LONG:
|
|
value = (ISC_INT64) *(int *) var->sqldata;
|
|
field_width = 11;
|
|
break;
|
|
case SQL_INT64:
|
|
value = (ISC_INT64) *(ISC_INT64 *) var->sqldata;
|
|
field_width = 21;
|
|
break;
|
|
}
|
|
dscale = var->sqlscale;
|
|
if (dscale < 0)
|
|
{
|
|
ISC_INT64 tens;
|
|
short i;
|
|
|
|
tens = 1;
|
|
for (i = 0; i > dscale; i--)
|
|
tens *= 10;
|
|
|
|
if (value >= 0)
|
|
sprintf (p, "%*" ISC_INT64_FORMAT "d.%0*" ISC_INT64_FORMAT "d",
|
|
field_width - 1 + dscale,
|
|
(ISC_INT64) value / tens,
|
|
-dscale,
|
|
(ISC_INT64) value % tens);
|
|
else if ((value / tens) != 0)
|
|
sprintf (p, "%*" ISC_INT64_FORMAT "d.%0*" ISC_INT64_FORMAT "d",
|
|
field_width - 1 + dscale,
|
|
(ISC_INT64) (value / tens),
|
|
-dscale,
|
|
(ISC_INT64) -(value % tens));
|
|
else
|
|
sprintf (p, "%*s.%0*" ISC_INT64_FORMAT "d",
|
|
field_width - 1 + dscale,
|
|
"-0",
|
|
-dscale,
|
|
(ISC_INT64) -(value % tens));
|
|
}
|
|
else if (dscale)
|
|
sprintf (p, "%*" ISC_INT64_FORMAT "d%0*d",
|
|
field_width,
|
|
(ISC_INT64) value,
|
|
dscale, 0);
|
|
else
|
|
sprintf (p, "%*" ISC_INT64_FORMAT "d%",
|
|
field_width,
|
|
(ISC_INT64) value);
|
|
}
|
|
break;
|
|
|
|
case SQL_FLOAT:
|
|
sprintf(p, "%15g ", *(float *) (var->sqldata));
|
|
break;
|
|
|
|
case SQL_DOUBLE:
|
|
sprintf(p, "%24f ", *(double *) (var->sqldata));
|
|
break;
|
|
|
|
case SQL_TIMESTAMP:
|
|
isc_decode_timestamp((ISC_TIMESTAMP *)var->sqldata, ×);
|
|
sprintf(date_s, "%04d-%02d-%02d %02d:%02d:%02d.%04d",
|
|
times.tm_year + 1900,
|
|
times.tm_mon+1,
|
|
times.tm_mday,
|
|
times.tm_hour,
|
|
times.tm_min,
|
|
times.tm_sec,
|
|
((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
|
|
sprintf(p, "%*s ", 24, date_s);
|
|
break;
|
|
|
|
case SQL_TYPE_DATE:
|
|
isc_decode_sql_date((ISC_DATE *)var->sqldata, ×);
|
|
sprintf(date_s, "%04d-%02d-%02d",
|
|
times.tm_year + 1900,
|
|
times.tm_mon+1,
|
|
times.tm_mday);
|
|
sprintf(p, "%*s ", 10, date_s);
|
|
break;
|
|
|
|
case SQL_TYPE_TIME:
|
|
isc_decode_sql_time((ISC_TIME *)var->sqldata, ×);
|
|
sprintf(date_s, "%02d:%02d:%02d.%04d",
|
|
times.tm_hour,
|
|
times.tm_min,
|
|
times.tm_sec,
|
|
(*((ISC_TIME *)var->sqldata)) % 10000);
|
|
sprintf(p, "%*s ", 13, date_s);
|
|
break;
|
|
|
|
case SQL_BLOB:
|
|
case SQL_ARRAY:
|
|
/* Print the blob id on blobs or arrays */
|
|
bid = *(ISC_QUAD *) var->sqldata;
|
|
sprintf(blob_s, "%08x:%08x", bid.gds_quad_high, bid.gds_quad_low);
|
|
sprintf(p, "%17s ", blob_s);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*p)
|
|
{
|
|
putchar(*p++);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Prompt for and get input.
|
|
* Statements are terminated by a semicolon.
|
|
*/
|
|
int get_statement (char *buf)
|
|
{
|
|
short c;
|
|
char *p;
|
|
int cnt;
|
|
|
|
p = buf;
|
|
cnt = 0;
|
|
printf("SQL> ");
|
|
|
|
for (;;)
|
|
{
|
|
if ((c = getchar()) == EOF)
|
|
return 1;
|
|
|
|
if (c == '\n')
|
|
{
|
|
/* accept "quit" or "exit" to terminate application */
|
|
|
|
if (!strncmp(buf, "exit", 4))
|
|
return 1;
|
|
if (!strncmp(buf, "quit", 4))
|
|
return 1;
|
|
|
|
/* Search back through white space looking for ';'.*/
|
|
while (cnt && isspace(*(p - 1)))
|
|
{
|
|
p--;
|
|
cnt--;
|
|
}
|
|
if (*(p - 1) == ';')
|
|
{
|
|
*p++ = '\0';
|
|
|
|
return 0;
|
|
}
|
|
*p++ = ' ';
|
|
printf("CON> ");
|
|
}
|
|
else
|
|
{
|
|
*p++ = (char)c;
|
|
}
|
|
cnt++;
|
|
}
|
|
}
|
|
|