Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
c6dd1e63
Commit
c6dd1e63
authored
Aug 11, 1998
by
Bruce Momjian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
This one cleans the cursor problems ecpg had so far. It is now able
to understand cursors with variables. Michael
parent
79c8d2e3
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
333 additions
and
231 deletions
+333
-231
src/interfaces/ecpg/ChangeLog
src/interfaces/ecpg/ChangeLog
+7
-0
src/interfaces/ecpg/TODO
src/interfaces/ecpg/TODO
+1
-3
src/interfaces/ecpg/include/ecpglib.h
src/interfaces/ecpg/include/ecpglib.h
+0
-3
src/interfaces/ecpg/lib/Makefile.in
src/interfaces/ecpg/lib/Makefile.in
+1
-1
src/interfaces/ecpg/lib/ecpglib.c
src/interfaces/ecpg/lib/ecpglib.c
+225
-201
src/interfaces/ecpg/preproc/ecpg.c
src/interfaces/ecpg/preproc/ecpg.c
+19
-0
src/interfaces/ecpg/preproc/extern.h
src/interfaces/ecpg/preproc/extern.h
+18
-0
src/interfaces/ecpg/preproc/preproc.y
src/interfaces/ecpg/preproc/preproc.y
+56
-17
src/interfaces/ecpg/test/Makefile
src/interfaces/ecpg/test/Makefile
+1
-1
src/interfaces/ecpg/test/test2.pgc
src/interfaces/ecpg/test/test2.pgc
+5
-5
No files found.
src/interfaces/ecpg/ChangeLog
View file @
c6dd1e63
...
...
@@ -270,3 +270,10 @@ Mon Aug 3 17:23:18 CEST 1998
- Fixed cursor handling
- Set version to 2.3.5
- Set library version to 2.4
Fri Aug 7 12:38:50 CEST 1998
- Fixed cursor handling once again
- Added support for variables in cursor
- Set version to 2.3.6
- Set library version to 2.5
src/interfaces/ecpg/TODO
View file @
c6dd1e63
What happens to a cursor declaration with variables?
The complete structure definition has to be listed inside the declare
section of the structure variable for ecpg to be able to understand it.
Variable type bool has to be
check
ed. I never used it so far.
Variable type bool has to be
test
ed. I never used it so far.
The error message for "no data" in an exec sql insert select from statement
has to be 100.
...
...
src/interfaces/ecpg/include/ecpglib.h
View file @
c6dd1e63
...
...
@@ -13,9 +13,6 @@ bool ECPGdisconnect(int, const char *);
void
ECPGlog
(
const
char
*
format
,...);
bool
ECPGdeclare
(
int
,
const
char
*
,
char
*
);
bool
ECPGopen
(
int
,
const
char
*
);
#ifdef LIBPQ_FE_H
bool
ECPGsetdb
(
PGconn
*
);
...
...
src/interfaces/ecpg/lib/Makefile.in
View file @
c6dd1e63
...
...
@@ -4,7 +4,7 @@ include $(SRCDIR)/Makefile.global
PQ_INCLUDE
=
-I
$(SRCDIR)
/interfaces/libpq
SO_MAJOR_VERSION
=
2
SO_MINOR_VERSION
=
4
SO_MINOR_VERSION
=
5
PORTNAME
=
@PORTNAME@
...
...
src/interfaces/ecpg/lib/ecpglib.c
View file @
c6dd1e63
...
...
@@ -33,6 +33,29 @@ static struct connection
struct
connection
*
next
;
}
*
all_connections
=
NULL
,
*
actual_connection
=
NULL
;
struct
variable
{
enum
ECPGttype
type
;
void
*
value
;
long
varcharsize
;
long
arrsize
;
long
offset
;
enum
ECPGttype
ind_type
;
void
*
ind_value
;
long
ind_varcharsize
;
long
ind_arrsize
;
long
ind_offset
;
struct
variable
*
next
;
};
struct
statement
{
int
lineno
;
char
*
command
;
struct
variable
*
inlist
;
struct
variable
*
outlist
;
};
static
int
simple_debug
=
0
;
static
FILE
*
debugstream
=
NULL
;
static
int
committed
=
true
;
...
...
@@ -116,27 +139,87 @@ ECPGfinish(struct connection *act)
ECPGlog
(
"ECPGfinish: called an extra time.
\n
"
);
}
bool
ECPGdo
(
int
lineno
,
char
*
query
,...)
/* create a list of variables */
static
bool
create_statement
(
int
lineno
,
struct
statement
**
stmt
,
char
*
query
,
va_list
ap
)
{
struct
variable
**
list
=
&
((
*
stmt
)
->
inlist
);
enum
ECPGttype
type
;
*
stmt
=
calloc
(
sizeof
(
struct
statement
),
1
);
if
(
!*
stmt
)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
return
false
;
}
(
*
stmt
)
->
command
=
query
;
(
*
stmt
)
->
lineno
=
lineno
;
list
=
&
((
*
stmt
)
->
inlist
);
type
=
va_arg
(
ap
,
enum
ECPGttype
);
while
(
type
!=
ECPGt_EORT
)
{
if
(
type
==
ECPGt_EOIT
)
{
list
=
&
((
*
stmt
)
->
outlist
);
}
else
{
struct
variable
*
var
,
*
ptr
;
var
=
malloc
(
sizeof
(
struct
variable
));
if
(
!
var
)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
return
false
;
}
var
->
type
=
type
;
var
->
value
=
va_arg
(
ap
,
void
*
);
var
->
varcharsize
=
va_arg
(
ap
,
long
);
var
->
arrsize
=
va_arg
(
ap
,
long
);
var
->
offset
=
va_arg
(
ap
,
long
);
var
->
ind_type
=
va_arg
(
ap
,
enum
ECPGttype
);
var
->
ind_value
=
va_arg
(
ap
,
void
*
);
var
->
ind_varcharsize
=
va_arg
(
ap
,
long
);
var
->
ind_arrsize
=
va_arg
(
ap
,
long
);
var
->
ind_offset
=
va_arg
(
ap
,
long
);
var
->
next
=
NULL
;
for
(
ptr
=
*
list
;
ptr
&&
ptr
->
next
;
ptr
=
ptr
->
next
);
if
(
ptr
==
NULL
)
*
list
=
var
;
else
ptr
->
next
=
var
;
}
type
=
va_arg
(
ap
,
enum
ECPGttype
);
}
return
(
true
);
}
static
bool
ECPGexecute
(
struct
statement
*
stmt
)
{
va_list
ap
;
bool
status
=
false
;
char
*
copiedquery
;
PGresult
*
results
;
PGnotify
*
notify
;
enum
ECPGttype
type
;
void
*
value
=
NULL
,
*
ind_value
;
long
varcharsize
,
ind_varcharsize
;
long
arrsize
,
ind_arrsize
;
long
offset
,
ind_offset
;
enum
ECPGttype
ind_type
;
struct
variable
*
var
;
memset
((
char
*
)
&
sqlca
,
0
,
sizeof
(
sqlca
));
va_start
(
ap
,
query
);
copiedquery
=
strdup
(
query
);
type
=
va_arg
(
ap
,
enum
ECPGttype
);
copiedquery
=
strdup
(
stmt
->
command
);
/*
* Now, if the type is one of the fill in types then we take the
...
...
@@ -144,7 +227,8 @@ ECPGdo(int lineno, char *query,...)
* Then if there are any more fill in types we fill in at the next and
* so on.
*/
while
(
type
!=
ECPGt_EOIT
)
var
=
stmt
->
inlist
;
while
(
var
)
{
char
*
newcopy
;
char
*
mallocedval
=
NULL
;
...
...
@@ -158,34 +242,24 @@ ECPGdo(int lineno, char *query,...)
* think).
*/
value
=
va_arg
(
ap
,
void
*
);
varcharsize
=
va_arg
(
ap
,
long
);
arrsize
=
va_arg
(
ap
,
long
);
offset
=
va_arg
(
ap
,
long
);
ind_type
=
va_arg
(
ap
,
enum
ECPGttype
);
ind_value
=
va_arg
(
ap
,
void
*
);
ind_varcharsize
=
va_arg
(
ap
,
long
);
ind_arrsize
=
va_arg
(
ap
,
long
);
ind_offset
=
va_arg
(
ap
,
long
);
buff
[
0
]
=
'\0'
;
/* check for null value and set input buffer accordingly */
switch
(
ind_type
)
switch
(
var
->
ind_type
)
{
case
ECPGt_short
:
case
ECPGt_unsigned_short
:
if
(
*
(
short
*
)
ind_value
<
0
)
if
(
*
(
short
*
)
var
->
ind_value
<
0
)
strcpy
(
buff
,
"null"
);
break
;
case
ECPGt_int
:
case
ECPGt_unsigned_int
:
if
(
*
(
int
*
)
ind_value
<
0
)
if
(
*
(
int
*
)
var
->
ind_value
<
0
)
strcpy
(
buff
,
"null"
);
break
;
case
ECPGt_long
:
case
ECPGt_unsigned_long
:
if
(
*
(
long
*
)
ind_value
<
0L
)
if
(
*
(
long
*
)
var
->
ind_value
<
0L
)
strcpy
(
buff
,
"null"
);
break
;
default:
...
...
@@ -194,42 +268,42 @@ ECPGdo(int lineno, char *query,...)
if
(
*
buff
==
'\0'
)
{
switch
(
type
)
switch
(
var
->
type
)
{
case
ECPGt_short
:
case
ECPGt_int
:
sprintf
(
buff
,
"%d"
,
*
(
int
*
)
value
);
sprintf
(
buff
,
"%d"
,
*
(
int
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_unsigned_short
:
case
ECPGt_unsigned_int
:
sprintf
(
buff
,
"%d"
,
*
(
unsigned
int
*
)
value
);
sprintf
(
buff
,
"%d"
,
*
(
unsigned
int
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_long
:
sprintf
(
buff
,
"%ld"
,
*
(
long
*
)
value
);
sprintf
(
buff
,
"%ld"
,
*
(
long
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_unsigned_long
:
sprintf
(
buff
,
"%ld"
,
*
(
unsigned
long
*
)
value
);
sprintf
(
buff
,
"%ld"
,
*
(
unsigned
long
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_float
:
sprintf
(
buff
,
"%.14g"
,
*
(
float
*
)
value
);
sprintf
(
buff
,
"%.14g"
,
*
(
float
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_double
:
sprintf
(
buff
,
"%.14g"
,
*
(
double
*
)
value
);
sprintf
(
buff
,
"%.14g"
,
*
(
double
*
)
va
r
->
va
lue
);
tobeinserted
=
buff
;
break
;
case
ECPGt_bool
:
sprintf
(
buff
,
"'%c'"
,
(
*
(
char
*
)
value
?
't'
:
'f'
));
sprintf
(
buff
,
"'%c'"
,
(
*
(
char
*
)
va
r
->
va
lue
?
't'
:
'f'
));
tobeinserted
=
buff
;
break
;
...
...
@@ -237,7 +311,7 @@ ECPGdo(int lineno, char *query,...)
case
ECPGt_unsigned_char
:
{
/* set slen to string length if type is char * */
int
slen
=
(
var
charsize
==
0
)
?
strlen
((
char
*
)
value
)
:
varcharsize
;
int
slen
=
(
var
->
varcharsize
==
0
)
?
strlen
((
char
*
)
var
->
value
)
:
var
->
varcharsize
;
char
*
tmp
;
newcopy
=
(
char
*
)
malloc
(
slen
+
1
);
...
...
@@ -245,11 +319,11 @@ ECPGdo(int lineno, char *query,...)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
strncpy
(
newcopy
,
(
char
*
)
value
,
slen
);
strncpy
(
newcopy
,
(
char
*
)
va
r
->
va
lue
,
slen
);
newcopy
[
slen
]
=
'\0'
;
mallocedval
=
(
char
*
)
malloc
(
2
*
strlen
(
newcopy
)
+
3
);
...
...
@@ -257,7 +331,7 @@ ECPGdo(int lineno, char *query,...)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -267,7 +341,7 @@ ECPGdo(int lineno, char *query,...)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -282,28 +356,28 @@ ECPGdo(int lineno, char *query,...)
case
ECPGt_varchar
:
{
struct
ECPGgeneric_varchar
*
var
=
(
struct
ECPGgeneric_varchar
*
)
value
;
struct
ECPGgeneric_varchar
*
var
iable
=
(
struct
ECPGgeneric_varchar
*
)
(
var
->
value
)
;
char
*
tmp
;
newcopy
=
(
char
*
)
malloc
(
var
->
len
+
1
);
newcopy
=
(
char
*
)
malloc
(
var
iable
->
len
+
1
);
if
(
!
newcopy
)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
strncpy
(
newcopy
,
var
->
arr
,
var
->
len
);
newcopy
[
var
->
len
]
=
'\0'
;
strncpy
(
newcopy
,
var
iable
->
arr
,
variable
->
len
);
newcopy
[
var
iable
->
len
]
=
'\0'
;
mallocedval
=
(
char
*
)
malloc
(
2
*
strlen
(
newcopy
)
+
3
);
if
(
!
mallocedval
)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -313,7 +387,7 @@ ECPGdo(int lineno, char *query,...)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -329,7 +403,7 @@ ECPGdo(int lineno, char *query,...)
default:
/* Not implemented yet */
register_error
(
ECPG_UNSUPPORTED
,
"Unsupported type %s on line %d."
,
ECPGtype_name
(
type
),
lineno
);
ECPGtype_name
(
var
->
type
),
stmt
->
lineno
);
return
false
;
break
;
}
...
...
@@ -348,7 +422,7 @@ ECPGdo(int lineno, char *query,...)
{
ECPGfinish
(
actual_connection
);
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -360,7 +434,7 @@ ECPGdo(int lineno, char *query,...)
* We have an argument but we dont have the matched up string
* in the string
*/
register_error
(
ECPG_TOO_MANY_ARGUMENTS
,
"Too many arguments line %d."
,
lineno
);
register_error
(
ECPG_TOO_MANY_ARGUMENTS
,
"Too many arguments line %d."
,
stmt
->
lineno
);
return
false
;
}
else
...
...
@@ -379,7 +453,7 @@ ECPGdo(int lineno, char *query,...)
/*
* Now everything is safely copied to the newcopy. Lets free the
* oldcopy and let the copiedquery get the value from the newcopy.
* oldcopy and let the copiedquery get the va
r->va
lue from the newcopy.
*/
if
(
mallocedval
!=
NULL
)
{
...
...
@@ -390,13 +464,13 @@ ECPGdo(int lineno, char *query,...)
free
(
copiedquery
);
copiedquery
=
newcopy
;
type
=
va_arg
(
ap
,
enum
ECPGttype
)
;
var
=
var
->
next
;
}
/* Check if there are unmatched things left. */
if
(
strstr
(
copiedquery
,
";;"
)
!=
NULL
)
{
register_error
(
ECPG_TOO_FEW_ARGUMENTS
,
"Too few arguments line %d."
,
lineno
);
register_error
(
ECPG_TOO_FEW_ARGUMENTS
,
"Too few arguments line %d."
,
stmt
->
lineno
);
return
false
;
}
...
...
@@ -406,27 +480,28 @@ ECPGdo(int lineno, char *query,...)
{
if
((
results
=
PQexec
(
actual_connection
->
connection
,
"begin transaction"
))
==
NULL
)
{
register_error
(
ECPG_TRANS
,
"Error starting transaction line %d."
,
lineno
);
register_error
(
ECPG_TRANS
,
"Error starting transaction line %d."
,
stmt
->
lineno
);
return
false
;
}
PQclear
(
results
);
committed
=
0
;
}
ECPGlog
(
"ECPG
do line %d: QUERY: %s
\n
"
,
lineno
,
copiedquery
);
ECPGlog
(
"ECPG
execute line %d: QUERY: %s
\n
"
,
stmt
->
lineno
,
copiedquery
);
results
=
PQexec
(
actual_connection
->
connection
,
copiedquery
);
free
(
copiedquery
);
if
(
results
==
NULL
)
{
ECPGlog
(
"ECPG
do line %d: error: %s"
,
lineno
,
ECPGlog
(
"ECPG
execute line %d: error: %s"
,
stmt
->
lineno
,
PQerrorMessage
(
actual_connection
->
connection
));
register_error
(
ECPG_PGSQL
,
"Postgres error: %s line %d."
,
PQerrorMessage
(
actual_connection
->
connection
),
lineno
);
PQerrorMessage
(
actual_connection
->
connection
),
stmt
->
lineno
);
}
else
{
sqlca
.
sqlerrd
[
2
]
=
0
;
var
=
stmt
->
outlist
;
switch
(
PQresultStatus
(
results
))
{
int
nfields
,
ntuples
,
act_tuple
,
act_field
;
...
...
@@ -445,9 +520,9 @@ ECPGdo(int lineno, char *query,...)
if
(
ntuples
<
1
)
{
ECPGlog
(
"ECPG
do
line %d: Incorrect number of matches: %d
\n
"
,
lineno
,
ntuples
);
register_error
(
ECPG_NOT_FOUND
,
"Data not found line %d."
,
lineno
);
ECPGlog
(
"ECPG
execute
line %d: Incorrect number of matches: %d
\n
"
,
stmt
->
lineno
,
ntuples
);
register_error
(
ECPG_NOT_FOUND
,
"Data not found line %d."
,
stmt
->
lineno
);
status
=
false
;
break
;
}
...
...
@@ -457,23 +532,19 @@ ECPGdo(int lineno, char *query,...)
char
*
pval
;
char
*
scan_length
;
type
=
va_arg
(
ap
,
enum
ECPGttype
);
value
=
va_arg
(
ap
,
void
*
);
varcharsize
=
va_arg
(
ap
,
long
);
arrsize
=
va_arg
(
ap
,
long
);
offset
=
va_arg
(
ap
,
long
);
ind_type
=
va_arg
(
ap
,
enum
ECPGttype
);
ind_value
=
va_arg
(
ap
,
void
*
);
ind_varcharsize
=
va_arg
(
ap
,
long
);
ind_arrsize
=
va_arg
(
ap
,
long
);
ind_offset
=
va_arg
(
ap
,
long
);
if
(
var
==
NULL
)
{
ECPGlog
(
"ECPGexecute line %d: Too few arguments.
\n
"
,
stmt
->
lineno
);
register_error
(
ECPG_TOO_FEW_ARGUMENTS
,
"Too few arguments line %d."
,
stmt
->
lineno
);
return
(
false
);
}
/* if we don't have enough space, we cannot read all tuples */
if
((
arrsize
>
0
&&
ntuples
>
arrsize
)
||
(
ind_arrsize
>
0
&&
ntuples
>
ind_arrsize
))
if
((
var
->
arrsize
>
0
&&
ntuples
>
var
->
arrsize
)
||
(
var
->
ind_arrsize
>
0
&&
ntuples
>
var
->
ind_arrsize
))
{
ECPGlog
(
"ECPG
do
line %d: Incorrect number of matches: %d don't fit into array of %d
\n
"
,
lineno
,
ntuples
,
arrsize
);
register_error
(
ECPG_TOO_MANY_MATCHES
,
"Too many matches line %d."
,
lineno
);
ECPGlog
(
"ECPG
execute
line %d: Incorrect number of matches: %d don't fit into array of %d
\n
"
,
stmt
->
lineno
,
ntuples
,
var
->
arrsize
);
register_error
(
ECPG_TOO_MANY_MATCHES
,
"Too many matches line %d."
,
stmt
->
lineno
);
status
=
false
;
break
;
}
...
...
@@ -481,31 +552,31 @@ ECPGdo(int lineno, char *query,...)
{
pval
=
PQgetvalue
(
results
,
act_tuple
,
act_field
);
ECPGlog
(
"ECPG
do line %d: RESULT: %s
\n
"
,
lineno
,
pval
?
pval
:
""
);
ECPGlog
(
"ECPG
execute line %d: RESULT: %s
\n
"
,
stmt
->
lineno
,
pval
?
pval
:
""
);
/* Now the pval is a pointer to the value. */
/* We will have to decode the value */
/* Now the pval is a pointer to the va
r->va
lue. */
/* We will have to decode the va
r->va
lue */
/* check for null value and set indicator accordingly */
switch
(
ind_type
)
/* check for null va
r->va
lue and set indicator accordingly */
switch
(
var
->
ind_type
)
{
case
ECPGt_short
:
case
ECPGt_unsigned_short
:
((
short
*
)
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
((
short
*
)
var
->
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
break
;
case
ECPGt_int
:
case
ECPGt_unsigned_int
:
((
int
*
)
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
((
int
*
)
var
->
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
break
;
case
ECPGt_long
:
case
ECPGt_unsigned_long
:
((
long
*
)
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
((
long
*
)
var
->
ind_value
)[
act_tuple
]
=
-
PQgetisnull
(
results
,
act_tuple
,
act_field
);
break
;
default:
break
;
}
switch
(
type
)
switch
(
var
->
type
)
{
long
res
;
unsigned
long
ures
;
...
...
@@ -520,7 +591,7 @@ ECPGdo(int lineno, char *query,...)
if
(
*
scan_length
!=
'\0'
)
/* Garbage left */
{
register_error
(
ECPG_INT_FORMAT
,
"Not correctly formatted int type: %s line %d."
,
pval
,
lineno
);
pval
,
stmt
->
lineno
);
status
=
false
;
res
=
0L
;
}
...
...
@@ -529,16 +600,16 @@ ECPGdo(int lineno, char *query,...)
res
=
0L
;
/* Again?! Yes */
switch
(
type
)
switch
(
var
->
type
)
{
case
ECPGt_short
:
((
short
*
)
value
)[
act_tuple
]
=
(
short
)
res
;
((
short
*
)
va
r
->
va
lue
)[
act_tuple
]
=
(
short
)
res
;
break
;
case
ECPGt_int
:
((
int
*
)
value
)[
act_tuple
]
=
(
int
)
res
;
((
int
*
)
va
r
->
va
lue
)[
act_tuple
]
=
(
int
)
res
;
break
;
case
ECPGt_long
:
((
long
*
)
value
)[
act_tuple
]
=
res
;
((
long
*
)
va
r
->
va
lue
)[
act_tuple
]
=
res
;
break
;
default:
/* Cannot happen */
...
...
@@ -555,7 +626,7 @@ ECPGdo(int lineno, char *query,...)
if
(
*
scan_length
!=
'\0'
)
/* Garbage left */
{
register_error
(
ECPG_UINT_FORMAT
,
"Not correctly formatted unsigned type: %s line %d."
,
pval
,
lineno
);
pval
,
stmt
->
lineno
);
status
=
false
;
ures
=
0L
;
}
...
...
@@ -564,16 +635,16 @@ ECPGdo(int lineno, char *query,...)
ures
=
0L
;
/* Again?! Yes */
switch
(
type
)
switch
(
var
->
type
)
{
case
ECPGt_unsigned_short
:
((
unsigned
short
*
)
value
)[
act_tuple
]
=
(
unsigned
short
)
ures
;
((
unsigned
short
*
)
va
r
->
va
lue
)[
act_tuple
]
=
(
unsigned
short
)
ures
;
break
;
case
ECPGt_unsigned_int
:
((
unsigned
int
*
)
value
)[
act_tuple
]
=
(
unsigned
int
)
ures
;
((
unsigned
int
*
)
va
r
->
va
lue
)[
act_tuple
]
=
(
unsigned
int
)
ures
;
break
;
case
ECPGt_unsigned_long
:
((
unsigned
long
*
)
value
)[
act_tuple
]
=
ures
;
((
unsigned
long
*
)
va
r
->
va
lue
)[
act_tuple
]
=
ures
;
break
;
default:
/* Cannot happen */
...
...
@@ -590,7 +661,7 @@ ECPGdo(int lineno, char *query,...)
if
(
*
scan_length
!=
'\0'
)
/* Garbage left */
{
register_error
(
ECPG_FLOAT_FORMAT
,
"Not correctly formatted floating point type: %s line %d."
,
pval
,
lineno
);
pval
,
stmt
->
lineno
);
status
=
false
;
dres
=
0
.
0
;
}
...
...
@@ -599,13 +670,13 @@ ECPGdo(int lineno, char *query,...)
dres
=
0
.
0
;
/* Again?! Yes */
switch
(
type
)
switch
(
var
->
type
)
{
case
ECPGt_float
:
((
float
*
)
value
)[
act_tuple
]
=
dres
;
((
float
*
)
va
r
->
va
lue
)[
act_tuple
]
=
dres
;
break
;
case
ECPGt_double
:
((
double
*
)
value
)[
act_tuple
]
=
dres
;
((
double
*
)
va
r
->
va
lue
)[
act_tuple
]
=
dres
;
break
;
default:
/* Cannot happen */
...
...
@@ -618,50 +689,50 @@ ECPGdo(int lineno, char *query,...)
{
if
(
pval
[
0
]
==
'f'
&&
pval
[
1
]
==
'\0'
)
{
((
char
*
)
value
)[
act_tuple
]
=
false
;
((
char
*
)
va
r
->
va
lue
)[
act_tuple
]
=
false
;
break
;
}
else
if
(
pval
[
0
]
==
't'
&&
pval
[
1
]
==
'\0'
)
{
((
char
*
)
value
)[
act_tuple
]
=
true
;
((
char
*
)
va
r
->
va
lue
)[
act_tuple
]
=
true
;
break
;
}
}
register_error
(
ECPG_CONVERT_BOOL
,
"Unable to convert %s to bool on line %d."
,
(
pval
?
pval
:
"NULL"
),
lineno
);
stmt
->
lineno
);
status
=
false
;
break
;
case
ECPGt_char
:
case
ECPGt_unsigned_char
:
{
if
(
varcharsize
==
0
)
if
(
var
->
var
charsize
==
0
)
{
/* char* */
strncpy
(((
char
**
)
value
)[
act_tuple
],
pval
,
strlen
(
pval
));
(((
char
**
)
value
)[
act_tuple
])[
strlen
(
pval
)]
=
'\0'
;
strncpy
(((
char
**
)
va
r
->
va
lue
)[
act_tuple
],
pval
,
strlen
(
pval
));
(((
char
**
)
va
r
->
va
lue
)[
act_tuple
])[
strlen
(
pval
)]
=
'\0'
;
}
else
{
strncpy
((
char
*
)
(
va
lue
+
offset
*
act_tuple
),
pval
,
varcharsize
);
if
(
varcharsize
<
strlen
(
pval
))
strncpy
((
char
*
)
(
va
r
->
value
+
var
->
offset
*
act_tuple
),
pval
,
var
->
varcharsize
);
if
(
var
->
var
charsize
<
strlen
(
pval
))
{
/* truncation */
switch
(
ind_type
)
switch
(
var
->
ind_type
)
{
case
ECPGt_short
:
case
ECPGt_unsigned_short
:
((
short
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
short
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
case
ECPGt_int
:
case
ECPGt_unsigned_int
:
((
int
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
int
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
case
ECPGt_long
:
case
ECPGt_unsigned_long
:
((
long
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
long
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
default:
break
;
...
...
@@ -674,62 +745,55 @@ ECPGdo(int lineno, char *query,...)
case
ECPGt_varchar
:
{
struct
ECPGgeneric_varchar
*
var
=
(
struct
ECPGgeneric_varchar
*
)
(
va
lue
+
offset
*
act_tuple
);
struct
ECPGgeneric_varchar
*
var
iable
=
(
struct
ECPGgeneric_varchar
*
)
(
va
r
->
value
+
var
->
offset
*
act_tuple
);
if
(
varcharsize
==
0
)
strncpy
(
var
->
arr
,
pval
,
strlen
(
pval
));
if
(
var
->
var
charsize
==
0
)
strncpy
(
var
iable
->
arr
,
pval
,
strlen
(
pval
));
else
strncpy
(
var
->
arr
,
pval
,
varcharsize
);
strncpy
(
var
iable
->
arr
,
pval
,
var
->
varcharsize
);
var
->
len
=
strlen
(
pval
);
if
(
var
charsize
>
0
&&
var
->
len
>
varcharsize
)
var
iable
->
len
=
strlen
(
pval
);
if
(
var
->
varcharsize
>
0
&&
variable
->
len
>
var
->
varcharsize
)
{
/* truncation */
switch
(
ind_type
)
switch
(
var
->
ind_type
)
{
case
ECPGt_short
:
case
ECPGt_unsigned_short
:
((
short
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
short
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
case
ECPGt_int
:
case
ECPGt_unsigned_int
:
((
int
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
int
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
case
ECPGt_long
:
case
ECPGt_unsigned_long
:
((
long
*
)
ind_value
)[
act_tuple
]
=
varcharsize
;
((
long
*
)
var
->
ind_value
)[
act_tuple
]
=
var
->
varcharsize
;
break
;
default:
break
;
}
sqlca
.
sqlwarn
[
0
]
=
sqlca
.
sqlwarn
[
1
]
=
'W'
;
var
->
len
=
varcharsize
;
var
iable
->
len
=
var
->
varcharsize
;
}
}
break
;
case
ECPGt_EORT
:
ECPGlog
(
"ECPGdo line %d: Too few arguments.
\n
"
,
lineno
);
register_error
(
ECPG_TOO_FEW_ARGUMENTS
,
"Too few arguments line %d."
,
lineno
);
status
=
false
;
break
;
default:
register_error
(
ECPG_UNSUPPORTED
,
"Unsupported type %s on line %d."
,
ECPGtype_name
(
type
),
lineno
);
ECPGtype_name
(
var
->
type
),
stmt
->
lineno
);
status
=
false
;
break
;
}
}
var
=
var
->
next
;
}
type
=
va_arg
(
ap
,
enum
ECPGttype
);
if
(
status
&&
type
!=
ECPGt_EORT
)
if
(
status
&&
var
!=
NULL
)
{
register_error
(
ECPG_TOO_MANY_ARGUMENTS
,
"Too many arguments line %d."
,
lineno
);
register_error
(
ECPG_TOO_MANY_ARGUMENTS
,
"Too many arguments line %d."
,
stmt
->
lineno
);
status
=
false
;
}
...
...
@@ -737,35 +801,34 @@ ECPGdo(int lineno, char *query,...)
break
;
case
PGRES_EMPTY_QUERY
:
/* do nothing */
register_error
(
ECPG_EMPTY
,
"Empty query line %d."
,
lineno
);
register_error
(
ECPG_EMPTY
,
"Empty query line %d."
,
stmt
->
lineno
);
break
;
case
PGRES_COMMAND_OK
:
status
=
true
;
sqlca
.
sqlerrd
[
2
]
=
atol
(
PQcmdTuples
(
results
));
ECPGlog
(
"TEST: %s
\n
"
,
PQcmdTuples
(
results
));
ECPGlog
(
"ECPGdo line %d Ok: %s
\n
"
,
lineno
,
PQcmdStatus
(
results
));
ECPGlog
(
"ECPGexecute line %d Ok: %s
\n
"
,
stmt
->
lineno
,
PQcmdStatus
(
results
));
break
;
case
PGRES_NONFATAL_ERROR
:
case
PGRES_FATAL_ERROR
:
case
PGRES_BAD_RESPONSE
:
ECPGlog
(
"ECPG
do
line %d: Error: %s"
,
lineno
,
PQerrorMessage
(
actual_connection
->
connection
));
ECPGlog
(
"ECPG
execute
line %d: Error: %s"
,
stmt
->
lineno
,
PQerrorMessage
(
actual_connection
->
connection
));
register_error
(
ECPG_PGSQL
,
"Error: %s line %d."
,
PQerrorMessage
(
actual_connection
->
connection
),
lineno
);
PQerrorMessage
(
actual_connection
->
connection
),
stmt
->
lineno
);
status
=
false
;
break
;
case
PGRES_COPY_OUT
:
ECPGlog
(
"ECPG
do line %d: Got PGRES_COPY_OUT ... tossing.
\n
"
,
lineno
);
ECPGlog
(
"ECPG
execute line %d: Got PGRES_COPY_OUT ... tossing.
\n
"
,
stmt
->
lineno
);
PQendcopy
(
results
->
conn
);
break
;
case
PGRES_COPY_IN
:
ECPGlog
(
"ECPG
do line %d: Got PGRES_COPY_IN ... tossing.
\n
"
,
lineno
);
ECPGlog
(
"ECPG
execute line %d: Got PGRES_COPY_IN ... tossing.
\n
"
,
stmt
->
lineno
);
PQendcopy
(
results
->
conn
);
break
;
default:
ECPGlog
(
"ECPG
do
line %d: Got something else, postgres error.
\n
"
,
lineno
);
register_error
(
ECPG_PGSQL
,
"Postgres error line %d."
,
lineno
);
ECPGlog
(
"ECPG
execute
line %d: Got something else, postgres error.
\n
"
,
stmt
->
lineno
);
register_error
(
ECPG_PGSQL
,
"Postgres error line %d."
,
stmt
->
lineno
);
status
=
false
;
break
;
}
...
...
@@ -775,8 +838,8 @@ ECPGdo(int lineno, char *query,...)
notify
=
PQnotifies
(
actual_connection
->
connection
);
if
(
notify
)
{
ECPGlog
(
"ECPG
do
line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received
\n
"
,
lineno
,
notify
->
relname
,
notify
->
be_pid
);
ECPGlog
(
"ECPG
execute
line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received
\n
"
,
stmt
->
lineno
,
notify
->
relname
,
notify
->
be_pid
);
free
(
notify
);
}
...
...
@@ -784,6 +847,20 @@ ECPGdo(int lineno, char *query,...)
return
status
;
}
bool
ECPGdo
(
int
lineno
,
char
*
query
,
...)
{
va_list
args
;
struct
statement
*
stmt
;
va_start
(
args
,
query
);
if
(
create_statement
(
lineno
,
&
stmt
,
query
,
args
)
==
false
)
return
(
false
);
va_end
(
args
);
return
(
ECPGexecute
(
stmt
));
}
bool
ECPGtrans
(
int
lineno
,
const
char
*
transaction
)
...
...
@@ -940,56 +1017,3 @@ sqlprint(void)
sqlca
.
sqlerrm
.
sqlerrmc
[
sqlca
.
sqlerrm
.
sqlerrml
]
=
'\0'
;
printf
(
"sql error %s
\n
"
,
sqlca
.
sqlerrm
.
sqlerrmc
);
}
/* keep a list of cursors */
struct
cursor
*
cur
=
NULL
;
bool
ECPGdeclare
(
int
lineno
,
const
char
*
name
,
char
*
command
)
{
struct
cursor
*
ptr
;
for
(
ptr
=
cur
;
ptr
!=
NULL
;
ptr
=
ptr
->
next
)
{
if
(
strcmp
(
name
,
ptr
->
name
)
==
0
)
{
/* re-definition */
free
(
ptr
->
command
);
ptr
->
command
=
command
;
break
;
}
}
if
(
ptr
==
NULL
)
{
struct
cursor
*
this
=
(
struct
cursor
*
)
malloc
(
sizeof
(
struct
cursor
));
if
(
!
this
)
{
ECPGlog
(
"out of memory
\n
"
);
register_error
(
ECPG_OUT_OF_MEMORY
,
"out of memory in line %d"
,
lineno
);
return
false
;
}
/* initial definition */
this
->
next
=
cur
;
this
->
name
=
name
;
this
->
command
=
command
;
cur
=
this
;
}
return
(
true
);
}
bool
ECPGopen
(
int
lineno
,
const
char
*
name
)
{
struct
cursor
*
ptr
;
for
(
ptr
=
cur
;
ptr
!=
NULL
;
ptr
=
ptr
->
next
)
{
if
(
strcmp
(
ptr
->
name
,
name
)
==
0
)
return
(
ECPGdo
(
lineno
,
ptr
->
command
,
ECPGt_EOIT
,
ECPGt_EORT
));
}
ECPGlog
(
"trying to open undeclared cursor %s
\n
"
,
name
);
register_error
(
ECPG_UNDECLARED_CURSOR
,
"trying to open undeclared cursor %s in line %d"
,
name
,
lineno
);
return
(
false
);
}
src/interfaces/ecpg/preproc/ecpg.c
View file @
c6dd1e63
...
...
@@ -23,6 +23,7 @@ extern char *optarg;
struct
_include_path
*
include_paths
;
static
int
no_auto_trans
=
0
;
struct
cursor
*
cur
=
NULL
;
static
void
usage
(
char
*
progname
)
...
...
@@ -138,6 +139,24 @@ main(int argc, char *const argv[])
{
struct
cursor
*
ptr
;
/* remove old cursor definitions if any are still there */
for
(
ptr
=
cur
;
ptr
!=
NULL
;
ptr
=
ptr
->
next
)
{
struct
arguments
*
l1
,
*
l2
;
free
(
ptr
->
command
);
free
(
ptr
->
name
);
for
(
l1
=
argsinsert
;
l1
;
l1
=
l2
)
{
l2
=
l1
->
next
;
free
(
l1
);
}
for
(
l1
=
argsresult
;
l1
;
l1
=
l2
)
{
l2
=
l1
->
next
;
free
(
l1
);
}
}
/* initialize lex */
lex_init
();
...
...
src/interfaces/ecpg/preproc/extern.h
View file @
c6dd1e63
...
...
@@ -16,6 +16,15 @@ struct _include_path { char * path;
extern
struct
_include_path
*
include_paths
;
struct
cursor
{
char
*
name
;
char
*
command
;
struct
arguments
*
argsinsert
;
struct
arguments
*
argsresult
;
struct
cursor
*
next
;
};
extern
struct
cursor
*
cur
;
/* This is a linked list of the variable names and types. */
struct
variable
{
...
...
@@ -28,6 +37,15 @@ struct variable
extern
struct
ECPGtype
ecpg_no_indicator
;
extern
struct
variable
no_indicator
;
struct
arguments
{
struct
variable
*
variable
;
struct
variable
*
indicator
;
struct
arguments
*
next
;
};
extern
struct
arguments
*
argsinsert
;
extern
struct
arguments
*
argsresult
;
/* functions */
extern
void
lex_init
(
void
);
...
...
src/interfaces/ecpg/preproc/preproc.y
View file @
c6dd1e63
...
...
@@ -245,14 +245,9 @@ remove_variables(int brace_level)
* These are of two kinds: input and output.
* I will make two lists for them.
*/
struct arguments {
struct variable * variable;
struct variable * indicator;
struct arguments * next;
};
st
atic st
ruct arguments * argsinsert = NULL;
st
atic st
ruct arguments * argsresult = NULL;
struct arguments * argsinsert = NULL;
struct arguments * argsresult = NULL;
static void
reset_variables(void)
...
...
@@ -279,7 +274,7 @@ add_variable(struct arguments ** list, struct variable * var, struct variable *
deletes the list as we go on.
*/
static void
dump_variables(struct arguments * list)
dump_variables(struct arguments * list
, int mode
)
{
if (list == NULL)
{
...
...
@@ -290,7 +285,7 @@ dump_variables(struct arguments * list)
end of the list:
*/
dump_variables(list->next);
dump_variables(list->next
, mode
);
/* Then the current element and its indicator */
ECPGdump_a_type(yyout, list->variable->name, list->variable->type,
...
...
@@ -298,6 +293,7 @@ dump_variables(struct arguments * list)
(list->indicator->type->typ != ECPGt_NO_INDICATOR) ? list->indicator->type : NULL, NULL, NULL);
/* Then release the list element. */
if (mode != 0)
free(list);
}
...
...
@@ -494,9 +490,9 @@ output_statement(char * stmt, int mode)
fputs("\", ", yyout);
/* dump variables to C file*/
dump_variables(argsinsert);
dump_variables(argsinsert
, 1
);
fputs("ECPGt_EOIT, ", yyout);
dump_variables(argsresult);
dump_variables(argsresult
, 1
);
fputs("ECPGt_EORT);", yyout);
whenever_action(mode);
free(stmt);
...
...
@@ -737,10 +733,9 @@ stmt: AddAttrStmt { output_statement($1, 0); }
| RenameStmt { output_statement($1, 0); }
| RevokeStmt { output_statement($1, 0); }
| OptimizableStmt {
if (strncmp($1, "
ECPGdeclare" , sizeof("ECPGdeclare
")-1) == 0)
if (strncmp($1, "
/* " , sizeof("/*
")-1) == 0)
{
fputs($1, yyout);
whenever_action(0);
free($1);
}
else
...
...
@@ -775,7 +770,27 @@ stmt: AddAttrStmt { output_statement($1, 0); }
whenever_action(0);
free($1);
}
| ECPGOpen { fprintf(yyout, "ECPGopen(__LINE__, %s);", $1);
| ECPGOpen {
struct cursor *ptr;
for (ptr = cur; ptr != NULL; ptr=ptr->next)
{
if (strcmp(ptr->name, $1) == 0)
break;
}
if (ptr == NULL)
{
sprintf(errortext, "trying to open undeclared cursor %s\n", $1);
yyerror(errortext);
}
fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command);
/* dump variables to C file*/
dump_variables(ptr->argsinsert, 0);
fputs("ECPGt_EOIT, ", yyout);
dump_variables(ptr->argsresult, 0);
fputs("ECPGt_EORT);", yyout);
whenever_action(0);
free($1);
}
...
...
@@ -2359,7 +2374,31 @@ CursorStmt: DECLARE name opt_binary CURSOR FOR
group_clause having_clause
union_clause sort_clause
{
$$ = make5_str(make1_str("ECPGdeclare(__LINE__, \""), $2, make1_str("\", \""), cat4_str(cat5_str(cat5_str(make1_str("declare"), strdup($2), $3, make1_str("cursor for select"), $7), $8, $9, $10, $11), $12, $13, $14), make1_str("\");"));
struct cursor *ptr, *this;
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
{
/* re-definition is a bug*/
sprintf(errortext, "cursor %s already defined", $2);
yyerror(errortext);
}
}
this = (struct cursor *) mm_alloc(sizeof(struct cursor));
/* initial definition */
this->next = cur;
this->name = $2;
this->command = cat4_str(cat5_str(cat5_str(make1_str("declare"), strdup($2), $3, make1_str("cursor for select"), $7), $8, $9, $10, $11), $12, $13, $14);
this->argsinsert = argsinsert;
this->argsresult = argsresult;
argsinsert = argsresult = NULL;
cur = this;
$$ = cat3_str(make1_str("/*"), strdup(this->command), make1_str("*/"));
}
;
...
...
@@ -4221,7 +4260,7 @@ execstring: cvariable |
* open is an open cursor, at the moment this has to be removed
*/
ECPGOpen: SQL_OPEN name open_opts {
$$ =
make3_str(make1_str("\""), $2, make1_str("\""))
;
$$ =
$2
;
};
open_opts: /* empty */ { $$ = make1_str(""); }
...
...
src/interfaces/ecpg/test/Makefile
View file @
c6dd1e63
all
:
test1 test2 perftest
LDFLAGS
=
-g
-I
../include
-I
../../libpq
-L
../lib
-lecpg
-L
../../libpq
-lpq
-lcrypt
--static
LDFLAGS
=
-g
-I
../include
-I
../../libpq
-L
../lib
-lecpg
-L
../../libpq
-lpq
-lcrypt
test1
:
test1.c
test1.c
:
test1.pgc
...
...
src/interfaces/ecpg/test/test2.pgc
View file @
c6dd1e63
...
...
@@ -19,6 +19,10 @@ exec sql begin declare section;
long ind_married;
char married[9];
exec sql end declare section;
exec sql declare cur cursor for
select name, born, age, married from meskes;
char msg[128], command[128];
FILE *dbgs;
...
...
@@ -41,10 +45,6 @@ exec sql end declare section;
strcpy(msg, "commit");
exec sql commit;
strcpy(msg, "declare");
exec sql declare cur cursor for
select name, born, age, married from meskes;
strcpy(msg, "open");
exec sql open cur;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment