Commit b866d2e2 authored by Bruce Momjian's avatar Bruce Momjian

as attache of this mail is patch (to the main tree) with to_char's

family functions. Contain:

  conversion from a datetype to formatted text:

	to_char( datetime, 	text)
	to_char( timestamp,	text)
	to_char( int4,		text)
	to_char( int8,		text)
	to_char( float4,	text)
	to_char( float8,	text)
	to_char( numeric,	text)

  vice versa:

	to_date		( text, text)
	to_datetime	( text, text)
	to_timestamp	( text, text)
	to_number	( text, text)	   (convert to numeric)


  PostgreSQL to_char is very compatible with Oracle's to_char(), but not
total exactly (now). Small differentions are in number formating. It will
fix in next to_char() version.


! If will this patch aplly to the main tree, must be delete the current
  to_char version in contrib (directory "dateformat" and note in contrib's
  README), this patch not erase it (sorry Bruce).



The patch patching files:

	doc/src/sgml/func.sgml
                     ^^^^^^^^
   Hmm, I'm not sure if my English... :( Check it anyone (volunteer)?

   Thomas, it is right? SGML is not my primary lang  and compile
   the current PG docs tree is very happy job (hard variables setting in
   docs/sgml/Makefile --> HSTYLE= /home/users/t/thomas/....  :-)

   What add any definition to global configure.in and set Makefiles in docs
   tree via ./configure?

	src/backend/utils/adt/Makefile
	src/backend/utils/adt/formatting.c
	src/include/catalog/pg_proc.h
	src/include/utils/formatting.h
Karel Zak <zakkr@zf.jcu.cz>              http://home.zf.jcu.cz/~zakkr/
parent 90aaad06
...@@ -14,10 +14,6 @@ bit - ...@@ -14,10 +14,6 @@ bit -
Bit type Bit type
by Adriaan Joubert <a.joubert@albourne.com> by Adriaan Joubert <a.joubert@albourne.com>
dateformat -
Date Formatting to/from character strings
by Karel Zak - Zakkr <zakkr@zf.jcu.cz>
datetime - datetime -
Date & time functions Date & time functions
by Massimo Dal Zotto <dz@cs.unitn.it> by Massimo Dal Zotto <dz@cs.unitn.it>
......
#-------------------------------------------------------------------------
#
# Makefile --
#
# Makefile for TO-FROM_CHAR module.
#
#-------------------------------------------------------------------------
PGDIR = ../..
SRCDIR = $(PGDIR)/src
include $(SRCDIR)/Makefile.global
INCLUDE_OPT = -I ./ \
-I $(SRCDIR)/ \
-I $(SRCDIR)/include \
-I $(SRCDIR)/port/$(PORTNAME)
CFLAGS += $(INCLUDE_OPT) $(CFLAGS_SL)
MODNAME = to-from_char
SQLDEFS = $(MODNAME).sql
MODULE = $(MODNAME)$(DLSUFFIX)
MODDIR = $(LIBDIR)/modules
SQLDIR = $(LIBDIR)/sql
all: module sql
module: $(MODULE)
sql: $(SQLDEFS)
install: $(MODULE) $(SQLDEFS) $(MODDIR) $(SQLDIR)
cp -p $(MODULE) $(MODDIR)/
strip $(MODDIR)/$(MODULE)
cp -p $(SQLDEFS) $(SQLDIR)/
install-doc:
if [ -d "$(DOCDIR)" ]; then \
cp -p *.doc $(DOCDIR); \
else \
cp -p *.doc $(SQLDIR); \
fi
$(MODDIR):
mkdir -p $@
$(SQLDIR):
mkdir -p $@
%.sql: %.sql.in
sed "s|MODULE_PATHNAME|$(MODDIR)/$(MODULE)|" < $< > $@
.SUFFIXES: $(DLSUFFIX)
%$(DLSUFFIX): %.c
$(CC) $(CFLAGS) -shared -o $@ $<
depend dep:
$(CC) -MM $(INCLUDE_OPT) *.c >depend
clean:
rm -f *~ $(MODULE) $(MODNAME).sql
ifeq (depend,$(wildcard depend))
include depend
endif
PROGRAM = rand_datetime
OBJECTS = rand_datetime.o
CFLAGS = -Wall -fpic -O3
CC = gcc
RM = rm -f
LIBS =
INCLUDE =
COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE)
LINK = $(CC) $(CFLAGS) -o $@ $(LIBS)
all: $(PROGRAM)
$(PROGRAM): $(OBJECTS)
$(LINK) $(OBJECTS)
.c.o: $<
$(COMPILE) -c $<
clean:
$(RM) -f *~ $(OBJECTS) $(PROGRAM)
TO/FROM CHAR tests
~~~~~~~~~~~~~~~~~~
* rand_datetime
The program 'rand_datetime' output a random datetime strings
(with yaer range 0..9999), you can use this for datetime testing.
You can usage this (example) for table filling.
Usage:
./rand_datetime <randfile> <num> <prefix> <postfix>
Example:
./rand_datetime /dev/urandom 2 "INSERT INTO tab VALUES('" "'::datetime);"
INSERT INTO tab VALUES('Sat 27 Jul 13:08:57 19618'::datetime);
INSERT INTO tab VALUES('Wed 25 Aug 20:31:50 27450'::datetime);
* regress
psql < regress.sql (all answers, must be TRUE, for Posgres
datestyle)
--> TO_DATE() is simular as FROM_CHAR(), but convert full datetime
to date ==> needn't test (?).
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
char *month[] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",NULL
};
char *day[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat", NULL };
int num(FILE *f, int min, int max)
{
int x, y, one;
one = x = fgetc(f);
if (x < min)
x = min;
else if (x > max) {
while(x > max)
x /= 2;
return x;
}
do {
y = fgetc(f);
if ((x+y) > max)
return x;
x += y;
} while(--one > 0);
return x;
}
int main(int argc, char **argv)
{
FILE *f;
int count;
if (argc < 5) {
printf("\nUsage: %s <randfile> <num> <prefix> <postfix>\n", argv[0]);
printf("\n(C) Karel Zak - Zakkr 1999\n\n");
exit(1);
}
if ((f = fopen(argv[1], "r")) == NULL) {
perror(argv[1]);
exit(1);
}
count = atoi(argv[2]);
for(; count > 0; --count) {
fprintf(stdout, "%s%s %02d %s %02d:%02d:%02d %d%s\n",
argv[3],
day[ num(f, 0, 6) ],
num(f, 1, 28),
month[ num(f, 0, 11) ],
num(f, 0, 23),
num(f, 0, 59),
num(f, 0, 59),
num(f, 0, 9999),
argv[4]
);
}
exit(0);
}
\ No newline at end of file
---
--- Postgres DateStyle needs all tests which parsing 'now'::datetime string
---
SET DATESTYLE TO 'Postgres';
SELECT 'now'::datetime =
TO_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY')::datetime
as "Now vs. to_char";
SELECT 'now'::datetime =
FROM_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY')
as "Now vs. from_char";
SELECT FROM_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY') =
TO_CHAR('now'::datetime, 'Dy Mon DD HH24:MI:SS YYYY')::datetime
as "From_char vs. To_char";
SELECT 'now'::datetime =
FROM_CHAR(
TO_CHAR('now'::datetime, '"Time: "HH24-MI-SS" Date: "Dy DD Mon YYYY'),
'"Time: "HH24-MI-SS" Date: "Dy DD Mon YYYY'
)
as "High from/to char test";
SELECT TO_CHAR('now'::datetime, 'SSSS')::int =
TO_CHAR('now'::datetime, 'HH24')::int * 3600 +
TO_CHAR('now'::datetime, 'MI')::int * 60 +
TO_CHAR('now'::datetime, 'SS')::int
as "SSSS test";
SELECT TO_CHAR('now'::datetime, 'WW')::int =
(TO_CHAR('now'::datetime, 'DDD')::int -
TO_CHAR('now'::datetime, 'D')::int + 7) / 7
as "Week test";
SELECT TO_CHAR('now'::datetime, 'Q')::int =
TO_CHAR('now'::datetime, 'MM')::int / 3 + 1
as "Quartal test";
SELECT TO_CHAR('now'::datetime, 'DDD')::int =
(TO_CHAR('now'::datetime, 'WW')::int * 7) -
(7 - TO_CHAR('now'::datetime, 'D')::int) +
(7 - TO_CHAR(('01-Jan-'||
TO_CHAR('now'::datetime,'YYYY'))::datetime,'D')::int)
+1
as "Week and day test";
TO_CHAR(datetime, text)
-----------------------
(returns text)
TO_CHAR - the DateTime function for formating date and time outputs.
This routine is inspire with the Oracle to_char().
SELECT TO_CHAR('now'::datetime, 'HH:MI:SS YYYY');
-------------
11:57:11 1999
FROM_CHAR(text, text)
---------------------
(returns DateTime)
FROM_CHAR - the PostgreSQL extension routine which read non-datetime
string and convert it to DateTime. This func. is inspire with the
Oracle to_date() routine, but in Oracle this func. return date only
and not support all keywords (format pictures).
SELECT FROM_CHAR('11:57:11 1999', 'HH:MI:SS YYYY');
----------------------------
Fri 01 Jan 11:57:11 1999 CET
TO_DATE(text, text)
-------------------
(returns Date)
TO_DATE - the Date function which read non-datetime (non-date) string
and convert it to date. All for thos func. is just as from_char().
This func. is inspire with the Oracle to_date() routine.
SELECT TO_DATE('11:57:11 1999', 'HH:MI:SS YYYY');
----------
01-01-1999
----------------------------------
String format-KeyWords and options:
----------------------------------
* TO_CHAR (..., 'format picture')
* FROM_CHAR (..., 'format picture')
* TO_DATE (..., 'format picture')
(Note: In Oracle manual is format-keyword called 'format pictures'.)
All keywords has suffixes (prefix or postfix), example for 2 hours:
keyword: HH (hour) 'HH' --> '02'
prefix: FM (fill mode) 'FMHH' --> '2'
postfix: TH (ordinal number) 'HHth' --> '02nd'
'FMHHth' --> '2nd'
Suffixes:
--------
FM - fill mode
02 --> FMHH --> 2
January , --> FMMonth --> January,
TH - upper ordinal number
02 --> HHTH --> 02ND
th - lower ordinal number
02 --> HHth --> 02th
KeyWords (format pictures):
--------------------------
HH - hour of day (01-12)
HH12 - -- // --
HH24 - hour (00-24)
MI - minute (00-59)
SS - socond (00-59)
SSSS - seconds past midnight (0-86399)
Y,YYY - year with comma (full PgSQL datetime range) digits)
YYYY - year (4 and more (full PgSQL datetime range) digits)
YYY - last 3 digits of year
YY - last 2 digits of year
Y - last digit of year
MONTH - full month name (upper) (9-letters)
Month - full month name - first character is upper (9-letters)
month - full month name - all characters is upper (9-letters)
MON - abbreviated month name (3-letters)
Mon - abbreviated month name (3-letters) - first character is upper
mon - abbreviated month name (3-letters) - all characters is upper
MM - month (01-12)
DAY - full day name (upper) (9-letters)
Day - full day name - first character is upper (9-letters)
day - full day name - all characters is upper (9-letters)
DY - abbreviated day name (3-letters) (upper)
Dy - abbreviated day name (3-letters) - first character is upper
Dy - abbreviated day name (3-letters) - all character is upper
DDD - day of year (001-366)
DD - day of month (01-31)
D - day of week (1-7; SUN=1)
WW - week number of year
CC - century (2-digits)
Q - quarter
RM - roman numeral month (I=JAN; I-XII)
W - week of month
J - julian day (days since January 1, 4712 BC)
AC / BC:
-------
TO-FROM CHAR routines support BC and AC postfix for years.
You can combine BC and AC with TH.
OTHER:
-----
'\' - must be use as double \\
'\\HH\\MI\\SS' --> 11\45\56
'"' - string berween a quotation marks is skipen and not
is parsed. If you wand write '"' to output you must
use \\"
'"Month: "Month' --> Month: November
'\\"YYYY Month\\"' --> "1999 November "
text - the PostgreSQL TO-FROM CHAR support text without '"',
but " text " is fastly and you have guarantee,
that this text not will interprete as keyword.
WARNING:
-------
You DON'T OMIT differention between fill mode (FM prefix)
and standard input in FROM_CHAR (TO_DATE), because this
routines can't scan your input string and conver it to
Datetime. See:
WRONG: FROM_CHAR('August 1999', 'Month YYYY');
RIGHT: FROM_CHAR('August 1999', 'Month YYYY');
or FROM_CHAR('August 1999', 'FMMonth YYYY');
(! Month is 9-letters string if you not set fill-mode !)
---------------------------
TODO / Now is not supported:
---------------------------
- spelled-out SP suffix ( 22 --> Twenty-two )
- AM/PM
- not supported number to character converting
TO_CHAR(number, 'format')
-------------------------------------------------------------------------------
- secondary products :-) ------------------------------------------------------
-------------------------------------------------------------------------------
ORDINAL(int4, text)
-------------------
* Translate number to ordinal number and return this as text
* Examples:
template1=> select ordinal(21212, 'TH');
ordinal
-------
21212ND
template1=> select ordinal(21212, 'th');
ordinal
-------
21212nd
#ifndef TO_FROM_CHAR_H
#define TO_FROM_CHAR_H
/*------
* For postgres
*------
*/
extern text *to_char(DateTime *dt, text *format);
extern DateTime *from_char(text *date_str, text *format);
extern DateADT to_date(text *date_str, text *format);
extern text *ordinal(int4 num, text *type);
extern char *months_full[]; /* full months name */
extern char *rm_months[]; /* roman numeral of months */
#endif
\ No newline at end of file
-- to-from_char.sql datetime routines --
--
-- Copyright (c) 1999, Karel Zak "Zakkr" <zakkr@zf.jcu.cz>
--
-- This file is distributed under the GNU General Public License
-- either version 2, or (at your option) any later version.
-- Define the new functions
--
create function to_char(datetime, text) returns text
as 'MODULE_PATHNAME'
language 'c';
create function from_char(text, text) returns datetime
as 'MODULE_PATHNAME'
language 'c';
create function to_date(text, text) returns date
as 'MODULE_PATHNAME'
language 'c';
create function ordinal(int, text) returns text
as 'MODULE_PATHNAME'
language 'c';
-- end of file
\ No newline at end of file
...@@ -420,6 +420,547 @@ ...@@ -420,6 +420,547 @@
</para> </para>
</sect1> </sect1>
<sect1>
<title id="formatting-funcs"> Formatting Functions </title>
<note>
<title>Author</title>
<para>
Written by
<ulink url="mailto:zakkr@zf.jcu.cz">Karel Zak</ulink>
on 2000-01-24.
</para>
</note>
<para>
Formatting functions provide a powerful set of tools for converting
various datetypes (date/time, int, float, numeric) to formatted strings
and reverse convert from formatted strings to original datetypes.
</para>
<para>
<table tocentry="1">
<title>Formatting Functions</title>
<tgroup cols="4">
<thead>
<row>
<entry>Function</entry>
<entry>Returns</entry>
<entry>Description</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry> to_char(datetime, text) </entry>
<entry> text </entry>
<entry> convert datetime to string </entry>
<entry> to_char('now'::datetime, 'HH12:MI:SS') </entry>
</row>
<row>
<entry> to_char(timestamp, text) </entry>
<entry> text </entry>
<entry> convert timestamp to string </entry>
<entry> to_char( now(), 'HH12:MI:SS') </entry>
</row>
<row>
<entry> to_char(int, text) </entry>
<entry> text </entry>
<entry> convert int4/int8 to string </entry>
<entry> to_char(125, '999') </entry>
</row>
<row>
<entry> to_char(float, text) </entry>
<entry> text </entry>
<entry> convert float4/float8 to string </entry>
<entry> to_char(125.8, '999D9') </entry>
</row>
<row>
<entry> to_char(numeric, text) </entry>
<entry> text </entry>
<entry> convert numeric to string </entry>
<entry> to_char(-125.8, '999D99S') </entry>
</row>
<row>
<entry> to_datetime(text, text) </entry>
<entry> datetime </entry>
<entry> convert string to datetime </entry>
<entry> to_datetime('05 Dec 2000 13', 'DD Mon YYYY HH') </entry>
</row>
<row>
<entry> to_date(text, text) </entry>
<entry> date </entry>
<entry> convert string to date </entry>
<entry> to_date('05 Dec 2000', 'DD Mon YYYY') </entry>
</row>
<row>
<entry> to_timestamp(text, text) </entry>
<entry> date </entry>
<entry> convert string to timestamp </entry>
<entry> to_timestamp('05 Dec 2000', 'DD Mon YYYY') </entry>
</row>
<row>
<entry> to_number(text, text) </entry>
<entry> numeric </entry>
<entry> convert string to numeric </entry>
<entry> to_number('12,454.8-', '99G999D9S') </entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
For all formatting functions is second argument format-picture.
</para>
<para>
<table tocentry="1">
<title>Format-pictures for datetime to_char() version.</title>
<tgroup cols="2">
<thead>
<row>
<entry>Format-picture</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry> HH </entry>
<entry> hour of day (01-12) </entry>
</row>
<row>
<entry> HH12 </entry>
<entry> hour of day (01-12) </entry>
</row>
<row>
<entry> MI </entry>
<entry> minute (00-59) </entry>
</row>
<row>
<entry> SS </entry>
<entry> socond (00-59) </entry>
</row>
<row>
<entry> SSSS </entry>
<entry> seconds past midnight (0-86399) </entry>
</row>
<row>
<entry> Y,YYY </entry>
<entry> year (4 and more digits) with comma </entry>
</row>
<row>
<entry> YYYY </entry>
<entry> year (4 and more digits) </entry>
</row>
<row>
<entry> YYY </entry>
<entry> last 3 digits of year </entry>
</row>
<row>
<entry> YY </entry>
<entry> last 2 digits of year </entry>
</row>
<row>
<entry> Y </entry>
<entry> last digit of year </entry>
</row>
<row>
<entry> MONTH </entry>
<entry> full month name (9-letters) - all characters is upper </entry>
</row>
<row>
<entry> Month </entry>
<entry> full month name (9-letters) - first character is upper </entry>
</row>
<row>
<entry> month </entry>
<entry> full month name (9-letters) - all characters is lower </entry>
</row>
<row>
<entry> MON </entry>
<entry> abbreviated month name (3-letters) - all characters is upper </entry>
</row>
<row>
<entry> Mon </entry>
<entry> abbreviated month name (3-letters) - first character is upper </entry>
</row>
<row>
<entry> mon </entry>
<entry> abbreviated month name (3-letters) - all characters is lower </entry>
</row>
<row>
<entry> MM </entry>
<entry> month (01-12) </entry>
</row>
<row>
<entry> DAY </entry>
<entry> full day name (9-letters) - all characters is upper </entry>
</row>
<row>
<entry> Day </entry>
<entry> full day name (9-letters) - first character is upper </entry>
</row>
<row>
<entry> day </entry>
<entry> full day name (9-letters) - all characters is lower </entry>
</row>
<row>
<entry> DY </entry>
<entry> abbreviated day name (3-letters) - all characters is upper </entry>
</row>
<row>
<entry> Dy </entry>
<entry> abbreviated day name (3-letters) - first character is upper </entry>
</row>
<row>
<entry> dy </entry>
<entry> abbreviated day name (3-letters) - all characters is upper </entry>
</row>
<row>
<entry> DDD </entry>
<entry> day of year (001-366) </entry>
</row>
<row>
<entry> DD </entry>
<entry> day of month (01-31) </entry>
</row>
<row>
<entry> D </entry>
<entry> day of week (1-7; SUN=1) </entry>
</row>
<row>
<entry> W </entry>
<entry> week of month </entry>
</row>
<row>
<entry> WW </entry>
<entry> week number of year </entry>
</row>
<row>
<entry> CC </entry>
<entry> century (2-digits) </entry>
</row>
<row>
<entry> J </entry>
<entry> julian day (days since January 1, 4712 BC) </entry>
</row>
<row>
<entry> Q </entry>
<entry> quarter </entry>
</row>
<row>
<entry> RM </entry>
<entry> month in roman numeral (I-XII; I=JAN) </entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
All format-pictures allow use suffixes (postfix / prefix). The suffix is
always valid for near format-picture. The 'FX' is global prefix only.
</para>
<para>
<table tocentry="1">
<title>Suffixes for format-pictures for datetime to_char() version.</title>
<tgroup cols="3">
<thead>
<row>
<entry>Suffix</entry>
<entry>Description</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry> FM </entry>
<entry> fill mode - prefix </entry>
<entry> FMMonth </entry>
</row>
<row>
<entry> TH </entry>
<entry> upper ordinal number - postfix </entry>
<entry> DDTH </entry>
</row>
<row>
<entry> th </entry>
<entry> lower ordinal number - postfix </entry>
<entry> DDTH </entry>
</row>
<row>
<entry> FX </entry>
<entry> FX - (Fixed format) global format-picture switch.
the TO_DATETIME / TO_DATA skip blank space if this option is
not use. Must by used as first item in formt-picture. </entry>
<entry> FX Month DD Day </entry>
</row>
<row>
<entry> SP </entry>
<entry> spell mode (not implement now)</entry>
<entry> DDSP </entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
'\' - must be use as double \\, example '\\HH\\MI\\SS'
</para>
<para>
'"' - string between a quotation marks is skipen and not is parsed.
If you want write '"' to output you must use \\", exapmle '\\"YYYY Month\\"'.
</para>
<para>
text - the PostgreSQL's to_char() support text without '"', but string
between a quotation marks is fastly and you have guarantee, that a text
not will interpreted as a keyword (format-picture), exapmle '"Hello Year: "YYYY'.
</para>
<para>
<table tocentry="1">
<title>Format-pictures for number (int/float/numeric) to_char() version.</title>
<tgroup cols="2">
<thead>
<row>
<entry>Format-picture</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry> 9 </entry>
<entry> return value with the specified number of digits, and if digit is
not available use blank space </entry>
</row>
<row>
<entry> 0 </entry>
<entry> as 9, but instead blank space use zero </entry>
</row>
<row>
<entry> . (period) </entry>
<entry> decimal point </entry>
</row>
<row>
<entry> , (comma) </entry>
<entry> group (thousand) separator </entry>
</row>
<row>
<entry> PR </entry>
<entry> return negative value in angle brackets </entry>
</row>
<row>
<entry> S </entry>
<entry> return negatice value with minus sign (use locales) </entry>
</row>
<row>
<entry> L </entry>
<entry> currency symbol (use locales) </entry>
</row>
<row>
<entry> D </entry>
<entry> decimal point (use locales) </entry>
</row>
<row>
<entry> G </entry>
<entry> group separator (use locales) </entry>
</row>
<row>
<entry> MI </entry>
<entry> return minus sign on specified position (if number < 0) </entry>
</row>
<row>
<entry> PL </entry>
<entry> return plus sign on specified position (if number > 0) </entry>
</row>
<row>
<entry> RN </entry>
<entry> return number as roman number (number must be between 1 and 3999) </entry>
</row>
<row>
<entry> TH or th </entry>
<entry> convert number to ordinal number (not convert numbers under zero and decimal numbers) </entry>
</row>
<row>
<entry> V </entry>
<entry> arg1 * (10 ^ n); - return a value multiplied by 10^n (where 'n' is number of '9's after the 'V').
The to_char() not support use 'V' and decimal poin together, example "99.9V99". </entry>
</row>
<row>
<entry> EEEE </entry>
<entry> science numbers. Now not supported. </entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
The PostgreSQL to_char() not support absurd to_char(0.1, '99.99')
--> <ProgramListing> ' .10' </ProgramListing> format.
</para>
<para>
<table tocentry="1">
<title> The to_char() examples. </title>
<tgroup cols="2">
<thead>
<row>
<entry>Input</entry>
<entry>Output</entry>
</row>
</thead>
<tbody>
<row>
<entry> to_char(now(), 'Day, HH12:MI:SS') </entry>
<entry><ProgramListing> 'Tuesday , 05:39:18' </ProgramListing></entry>
</row>
<row>
<entry> to_char(now(), 'FMDay, HH12:MI:SS') </entry>
<entry><ProgramListing> 'Tuesday, 05:39:18' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 0.1, '99.99') </entry>
<entry><ProgramListing> ' 0.10' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 0.1, '0.9') </entry>
<entry><ProgramListing> ' 0.1' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 0.1, '090.9') </entry>
<entry><ProgramListing> ' 000.1' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, '999') </entry>
<entry><ProgramListing> ' 485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, '999') </entry>
<entry><ProgramListing> '-485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, '09999') </entry>
<entry><ProgramListing> ' 00485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'FM09999') </entry>
<entry><ProgramListing> '00485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'FM999') </entry>
<entry><ProgramListing> '485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, '9 9 9') </entry>
<entry><ProgramListing> ' 4 8 5' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 1485, '9,999') </entry>
<entry><ProgramListing> ' 1,485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 1485, '9G999') </entry>
<entry><ProgramListing> ' 1 485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 148.5, '999.999') </entry>
<entry><ProgramListing> ' 148.500' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 148.5, '999D999') </entry>
<entry><ProgramListing> ' 148,500' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 3148.5,'9G999D999') </entry>
<entry><ProgramListing> ' 3 148,500' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, '999S') </entry>
<entry><ProgramListing> '485-' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, '999MI') </entry>
<entry><ProgramListing> '485-' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, '999MI') </entry>
<entry><ProgramListing> '485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'PL999') </entry>
<entry><ProgramListing> '+485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'SG999') </entry>
<entry><ProgramListing> '+485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, 'SG999') </entry>
<entry><ProgramListing> '-485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, '9SG99') </entry>
<entry><ProgramListing> '4-85' </ProgramListing></entry>
</row>
<row>
<entry> to_char( -485, '999PR') </entry>
<entry><ProgramListing> '<485>' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'L999') </entry>
<entry><ProgramListing> 'DM 485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'RN') </entry>
<entry><ProgramListing> ' CDLXXXV' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, 'FMRN') </entry>
<entry><ProgramListing> 'CDLXXXV' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 5.2, 'FMRN') </entry>
<entry><ProgramListing> 'V' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 482, '999th') </entry>
<entry><ProgramListing> ' 482nd' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485, '"Good number:"999') </entry>
<entry><ProgramListing> 'Good number: 485' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 485.8, '"Pre-decimal:"999" Post-decimal:" .999') </entry>
<entry><ProgramListing> 'Pre-decimal: 485 Post-decimal: .800' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 12, '99V999') </entry>
<entry><ProgramListing> ' 12000' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 12.4, '99V999') </entry>
<entry><ProgramListing> ' 12400' </ProgramListing></entry>
</row>
<row>
<entry> to_char( 12.45, '99V9') </entry>
<entry><ProgramListing> ' 125' </ProgramListing></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</sect1>
<sect1> <sect1>
<title>Geometric Functions</title> <title>Geometric Functions</title>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for utils/adt # Makefile for utils/adt
# #
# IDENTIFICATION # IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.32 2000/01/19 02:58:56 petere Exp $ # $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.33 2000/01/25 23:53:51 momjian Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -31,7 +31,7 @@ OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \ ...@@ -31,7 +31,7 @@ OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \
regexp.o regproc.o ruleutils.o selfuncs.o sets.o \ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
tid.o timestamp.o varchar.o varlena.o version.o \ tid.o timestamp.o varchar.o varlena.o version.o \
network.o mac.o inet_net_ntop.o inet_net_pton.o \ network.o mac.o inet_net_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o
all: SUBSYS.o all: SUBSYS.o
......
/****************************************************************** /* -----------------------------------------------------------------------
* formatting.c
* *
* The PostgreSQL modul for DateTime formating, inspire with * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.1 2000/01/25 23:53:51 momjian Exp $
* Oracle TO_CHAR() / TO_DATE() routines.
* *
* Copyright (c) 1999, Karel Zak "Zakkr" <zakkr@zf.jcu.cz> * TO_CHAR(); TO_DATETIME(); TO_DATE(); TO_NUMBER();
* *
* This file is distributed under the GNU General Public License * The PostgreSQL routines for a DateTime/int/float/numeric formatting,
* either version 2, or (at your option) any later version. * inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
* *
* 1999 Karel Zak "Zakkr"
* *
* NOTE: *
* In this modul is _not_ used POSIX 'struct tm' type, but * Cache & Memory:
* PgSQL type, which has tm_mon based on one (_non_ zero) and * Routines use (itself) internal cache for format pictures. If
* year not based on 1900, but is used full year number. * new format arg is same as a last format string, routines not
* call the format-parser.
*
* The cache use static buffer and is persistent across transactions. If
* format-picture is bigger than cache buffer, parser is called always.
*
* NOTE for Number version:
* All in this version is implemented as keywords ( => not used
* suffixes), because a format picture is for *one* item (number)
* only. It not is as a datetime version, where each keyword (can)
* has suffix.
*
* NOTE for DateTime version:
* In this modul is *not* used POSIX 'struct tm' type, but
* PgSQL type, which has tm_mon based on one (*non* zero) and
* year *not* based on 1900, but is used full year number.
* Modul support AC / BC years. * Modul support AC / BC years.
* *
******************************************************************/ * Supported types for to_char():
*
* DateTime, Numeric, int4, int8, float4, float8
*
* Supported types for reverse conversion:
*
* Datetime - to_datetime()
* Date - to_date()
* Numeric - to_number()
*
* -----------------------------------------------------------------------
*/
/* /* ----------
* UnComment me for DEBUG
* ----------
*/
/***
#define DEBUG_TO_FROM_CHAR #define DEBUG_TO_FROM_CHAR
#define DEBUG_elog_output NOTICE #define DEBUG_elog_output NOTICE
*/ ***/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#include <locale.h>
#include <math.h>
#include "postgres.h" #include "postgres.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/pg_locale.h"
#include "utils/formatting.h"
#include "to-from_char.h" /* ----------
* Routines type
* ----------
*/
#define DCH_TYPE 1 /* DATE-TIME version */
#define NUM_TYPE 2 /* NUMBER version */
#define MAX_NODE_SIZ 16 /* maximal length of one node */ /* ----------
* KeyWord Index (ascii from position 32 (' ') to 126 (~))
* ----------
*/
#define KeyWord_INDEX_SIZE ('~' - ' ' + 1)
#define KeyWord_INDEX_FILTER(_c) ((_c) < ' ' || (_c) > '~' ? 0 : 1)
#ifdef DEBUG_TO_FROM_CHAR /* ----------
#define NOTICE_TM {\ * Maximal length of one node
elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\ * ----------
tm->tm_sec, tm->tm_year,\ */
tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\ #define DCH_MAX_ITEM_SIZ 9 /* some month name ? */
tm->tm_mday, tm->tm_isdst,tm->tm_mon);\ #define NUM_MAX_ITEM_SIZ 16 /* roman number */
}
#endif /* ----------
* Format picture cache limits
* ----------
*/
#define NUM_CACHE_SIZE 64
#define DCH_CACHE_SIZE 128
/* ----------
* More in float.c
* ----------
*/
#define MAXFLOATWIDTH 64
#define MAXDOUBLEWIDTH 128
/*------ /* ----------
* (External) defined in PgSQL dt.c (datetime utils) * External (defined in PgSQL dt.c (datetime utils))
*------ * ----------
*/ */
extern char *months[], /* month abbreviation */ extern char *months[], /* month abbreviation */
*days[]; /* full days */ *days[]; /* full days */
/*------ /* ----------
* Private definitions * Format parser structs
*------ * ----------
*/ */
static struct tm _tm, *tm = &_tm; typedef struct {
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
type; /* prefix / postfix */
} KeySuffix;
typedef struct {
char *name; /* keyword */
/* action for keyword */
int len, /* keyword length */
(*action)(),
id; /* keyword id */
} KeyWord;
typedef struct {
int type; /* node type */
KeyWord *key; /* if node type is KEYWORD */
int character, /* if node type is CHAR */
suffix; /* keyword suffix */
} FormatNode;
#define NODE_TYPE_END 1
#define NODE_TYPE_ACTION 2
#define NODE_TYPE_CHAR 3
#define SUFFTYPE_PREFIX 1
#define SUFFTYPE_POSTFIX 2
/* ----------
* Full months
* ----------
*/
static char *months_full[] = { static char *months_full[] = {
"January", "February", "March", "April", "May", "June", "July", "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December", NULL "August", "September", "October", "November", "December", NULL
}; };
/*------ /* ----------
* AC / DC * AC / DC
*------ * ----------
*/ */
#define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y) #define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y)
#define BC_STR " BC" #define BC_STR " BC"
/*------ /* ----------
* Months in roman-numeral * Months in roman-numeral
* (Must be conversely for seq_search (in FROM_CHAR), because * (Must be conversely for seq_search (in FROM_CHAR), because
* 'VIII' must be over 'V') * 'VIII' must be over 'V')
*------ * ----------
*/ */
static char *rm_months[] = { static char *rm_months[] = {
"XII", "XI", "X", "IX", "VIII", "VII", "XII", "XI", "X", "IX", "VIII", "VII",
"VI", "V", "IV", "III", "II", "I", NULL "VI", "V", "IV", "III", "II", "I", NULL
}; };
/*------ /* ----------
* Roman numbers
* ----------
*/
static char *rm1[] = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL };
static char *rm10[] = { "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL };
static char *rm100[] = { "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL };
/* ----------
* Ordinal postfixes * Ordinal postfixes
*------ * ----------
*/ */
static char *numTH[] = { "ST", "ND", "RD", "TH", NULL }; static char *numTH[] = { "ST", "ND", "RD", "TH", NULL };
static char *numth[] = { "st", "nd", "rd", "th", NULL }; static char *numth[] = { "st", "nd", "rd", "th", NULL };
/*------ /* ----------
* Flags: * Flags & Options:
*------ * ----------
*/ */
#define TO_CHAR 1 #define TO_CHAR 1
#define FROM_CHAR 2 #define FROM_CHAR 2
...@@ -107,64 +204,113 @@ static char *numth[] = { "st", "nd", "rd", "th", NULL }; ...@@ -107,64 +204,113 @@ static char *numth[] = { "st", "nd", "rd", "th", NULL };
#define TH_UPPER 1 #define TH_UPPER 1
#define TH_LOWER 2 #define TH_LOWER 2
/****************************************************************************
* Structs for format parsing
****************************************************************************/
/*------ #ifdef DEBUG_TO_FROM_CHAR
* Format parser structs #define NOTICE_TM {\
*------ elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
tm->tm_sec, tm->tm_year,\
tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\
tm->tm_mday, tm->tm_isdst,tm->tm_mon);\
}
#endif
/* ----------
* Flags for DCH version
* ----------
*/ */
typedef struct { static int DCH_global_flag = 0;
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
type; /* prefix / postfix */
} KeySuffix;
typedef struct { #define DCH_F_FX 0x01
char *name; /* keyword */
/* action for keyword */
int len, /* keyword length */
(*action)(),
id; /* keyword id */
} KeyWord;
typedef struct { #define IS_FX (DCH_global_flag & DCH_F_FX)
int type; /* node type */
KeyWord *key; /* if node type is KEYWORD */
int character, /* if node type is CHAR */
suffix; /* keyword suffix */
} FormatNode;
#define NODE_TYPE_END 0
#define NODE_TYPE_ACTION 1
#define NODE_TYPE_CHAR 2
#define NODE_LAST 3 /* internal option */
#define SUFFTYPE_PREFIX 1 /* ----------
#define SUFFTYPE_POSTFIX 2 * Number description struct
* ----------
*/
typedef struct {
int pre, /* (count) numbers before decimal */
pos, /* (count) numbers after decimal */
lsign, /* want locales sign */
flag, /* number parametrs */
pre_lsign_num, /* tmp value for lsign */
multi, /* multiplier for 'V' */
need_locale; /* needs it locale */
} NUMDesc;
/* ----------
* Flags for NUMBER version
* ----------
*/
#define NUM_F_DECIMAL 0x01
#define NUM_F_LDECIMAL 0x02
#define NUM_F_ZERO 0x04
#define NUM_F_BLANK 0x08
#define NUM_F_FILLMODE 0x10
#define NUM_F_LSIGN 0x20
#define NUM_F_BRACKET 0x40
#define NUM_F_MINUS 0x80
#define NUM_F_PLUS 0x100
#define NUM_F_ROMAN 0x200
#define NUM_F_MULTI 0x400
#define NUM_LSIGN_PRE -1
#define NUM_LSIGN_POST 1
#define NUM_LSIGN_NONE 0
/* ----------
* Tests
* ----------
*/
#define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
#define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
#define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
#define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
#define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
#define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
#define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
#define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
#define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
#define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
/* ----------
* Private global-modul definitions
* ----------
*/
static struct tm _tm, *tm = &_tm;
/* ----------
* Utils
* ----------
*/
#ifndef MIN
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b)) ? (a) : (b))
#endif
/***************************************************************************** /*****************************************************************************
* KeyWords definition & action * KeyWords definition & action
*****************************************************************************/ *****************************************************************************/
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node); static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node); static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
/*------ /* ----------
* Suffixes: * Suffixes:
*------ * ----------
*/ */
#define DCH_S_FM 0x01 #define DCH_S_FM 0x01
#define DCH_S_TH 0x02 #define DCH_S_TH 0x02
#define DCH_S_th 0x04 #define DCH_S_th 0x04
#define DCH_S_SP 0x08 #define DCH_S_SP 0x08
/*------ /* ----------
* Suffix tests * Suffix tests
*------ * ----------
*/ */
#define S_THth(_s) (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0) #define S_THth(_s) (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0)
#define S_TH(_s) ((_s & DCH_S_TH) ? 1 : 0) #define S_TH(_s) ((_s & DCH_S_TH) ? 1 : 0)
...@@ -174,11 +320,11 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node); ...@@ -174,11 +320,11 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
#define S_FM(_s) ((_s & DCH_S_FM) ? 1 : 0) #define S_FM(_s) ((_s & DCH_S_FM) ? 1 : 0)
#define S_SP(_s) ((_s & DCH_S_SP) ? 1 : 0) #define S_SP(_s) ((_s & DCH_S_SP) ? 1 : 0)
/*------ /* ----------
* Suffixes definition for TO / FROM CHAR * Suffixes definition for DATE-TIME TO/FROM CHAR
*------ * ----------
*/ */
static KeySuffix suff[] = { static KeySuffix DCH_suff[] = {
{ "FM", 2, DCH_S_FM, SUFFTYPE_PREFIX }, { "FM", 2, DCH_S_FM, SUFFTYPE_PREFIX },
{ "TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX }, { "TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX },
{ "th", 2, DCH_S_th, SUFFTYPE_POSTFIX }, { "th", 2, DCH_S_th, SUFFTYPE_POSTFIX },
...@@ -187,7 +333,8 @@ static KeySuffix suff[] = { ...@@ -187,7 +333,8 @@ static KeySuffix suff[] = {
{ NULL, 0, 0, 0 } { NULL, 0, 0, 0 }
}; };
/*------ /* ----------
* Format-pictures (KeyWord).
* *
* The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
* complicated -to-> easy: * complicated -to-> easy:
...@@ -195,20 +342,22 @@ static KeySuffix suff[] = { ...@@ -195,20 +342,22 @@ static KeySuffix suff[] = {
* (example: "DDD","DD","Day","D" ) * (example: "DDD","DD","Day","D" )
* *
* (this specific sort needs the algorithm for sequential search for strings, * (this specific sort needs the algorithm for sequential search for strings,
* which not has exact end; - How keyword is in "HH12blabla" ? - "HH" * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
* or "HH12"? You must first try "HH12", because "HH" is in string, but * or "HH12"? You must first try "HH12", because "HH" is in string, but
* it is not good:-) * it is not good.
* *
* (!) Position for the keyword is simular as position in the enum I_poz (!) * (!)
* Position for the keyword is simular as position in the enum DCH/NUM_poz
* (!)
* *
* For fast search is used the KWindex[256], in this index is DCH_ enums for * For fast search is used the 'int index[256-32]' (first 32 ascii chars is
* each ASCII position or -1 if char is not used in the KeyWord. Search example * skiped), in this index is DCH_ enums for each ASCII position or -1 if
* for string "MM": * char is not used in the KeyWord. Search example for string "MM":
* 1) see KWindex to KWindex[77] ('M'), * 1) see in index to index[77-32] (77 = 'M'),
* 2) take keywords position from KWindex[77] * 2) take keywords position from index[77-32]
* 3) run sequential search in keywords[] from position * 3) run sequential search in keywords[] from position
* *
*------ * ----------
*/ */
typedef enum { typedef enum {
...@@ -220,6 +369,7 @@ typedef enum { ...@@ -220,6 +369,7 @@ typedef enum {
DCH_Day, DCH_Day,
DCH_Dy, DCH_Dy,
DCH_D, DCH_D,
DCH_FX, /* global suffix */
DCH_HH24, DCH_HH24,
DCH_HH12, DCH_HH12,
DCH_HH, DCH_HH,
...@@ -247,10 +397,41 @@ typedef enum { ...@@ -247,10 +397,41 @@ typedef enum {
DCH_mon, DCH_mon,
/* last */ /* last */
_DCH_last_ _DCH_last_
} I_poz; } DCH_poz;
typedef enum {
NUM_COMMA,
NUM_DEC,
NUM_0,
NUM_9,
NUM_B,
NUM_C,
NUM_D,
NUM_E,
NUM_FM,
NUM_G,
NUM_L,
NUM_MI,
NUM_PL,
NUM_PR,
NUM_RN,
NUM_SG,
NUM_SP,
NUM_S,
NUM_TH,
NUM_V,
NUM_rn,
NUM_th,
/* last */
_NUM_last_
} NUM_poz;
static KeyWord keywords[] = { /* ----------
/* keyword, len, func. I_poz is in KWindex */ * KeyWords for DATE-TIME version
* ----------
*/
static KeyWord DCH_keywords[] = {
/* keyword, len, func. type is in Index */
{ "CC", 2, dch_date, DCH_CC }, /*C*/ { "CC", 2, dch_date, DCH_CC }, /*C*/
{ "DAY", 3, dch_date, DCH_DAY }, /*D*/ { "DAY", 3, dch_date, DCH_DAY }, /*D*/
...@@ -260,6 +441,7 @@ static KeyWord keywords[] = { ...@@ -260,6 +441,7 @@ static KeyWord keywords[] = {
{ "Day", 3, dch_date, DCH_Day }, { "Day", 3, dch_date, DCH_Day },
{ "Dy", 2, dch_date, DCH_Dy }, { "Dy", 2, dch_date, DCH_Dy },
{ "D", 1, dch_date, DCH_D }, { "D", 1, dch_date, DCH_D },
{ "FX", 2, dch_global, DCH_FX }, /*F*/
{ "HH24", 4, dch_time, DCH_HH24 }, /*H*/ { "HH24", 4, dch_time, DCH_HH24 }, /*H*/
{ "HH12", 4, dch_time, DCH_HH12 }, { "HH12", 4, dch_time, DCH_HH12 },
{ "HH", 2, dch_time, DCH_HH }, { "HH", 2, dch_time, DCH_HH },
...@@ -285,54 +467,173 @@ static KeyWord keywords[] = { ...@@ -285,54 +467,173 @@ static KeyWord keywords[] = {
{ "dy", 2, dch_date, DCH_dy }, { "dy", 2, dch_date, DCH_dy },
{ "month", 5, dch_date, DCH_month }, /*m*/ { "month", 5, dch_date, DCH_month }, /*m*/
{ "mon", 3, dch_date, DCH_mon }, { "mon", 3, dch_date, DCH_mon },
/* last */ /* last */
{ NULL, 0, NULL, 0 }}; { NULL, 0, NULL, 0 }};
/* ----------
* KeyWords for NUMBER version
* ----------
*/
static KeyWord NUM_keywords[] = {
/* keyword, len, func. type is in Index */
{ ",", 1, NULL, NUM_COMMA }, /*,*/
{ ".", 1, NULL, NUM_DEC }, /*.*/
{ "0", 1, NULL, NUM_0 }, /*0*/
{ "9", 1, NULL, NUM_9 }, /*9*/
{ "B", 1, NULL, NUM_B }, /*B*/
{ "C", 1, NULL, NUM_C }, /*C*/
{ "D", 1, NULL, NUM_D }, /*D*/
{ "E", 1, NULL, NUM_E }, /*E*/
{ "FM", 2, NULL, NUM_FM }, /*F*/
{ "G", 1, NULL, NUM_G }, /*G*/
{ "L", 1, NULL, NUM_L }, /*L*/
{ "MI", 2, NULL, NUM_MI }, /*M*/
{ "PL", 2, NULL, NUM_PL }, /*P*/
{ "PR", 2, NULL, NUM_PR },
{ "RN", 2, NULL, NUM_RN }, /*R*/
{ "SG", 2, NULL, NUM_SG }, /*S*/
{ "SP", 2, NULL, NUM_SP },
{ "S", 1, NULL, NUM_S },
{ "TH", 2, NULL, NUM_TH }, /*T*/
{ "V", 1, NULL, NUM_V }, /*V*/
{ "rn", 2, NULL, NUM_rn }, /*r*/
{ "th", 2, NULL, NUM_th }, /*t*/
/* last */
{ NULL, 0, NULL, 0 }};
static int KWindex[256] = {
/* ----------
* KeyWords index for DATE-TIME version
* ----------
*/
static int DCH_index[256 - 32] = {
/* /*
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
*/ */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*---- first 0..31 chars is skiped ----*/
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, DCH_CC, DCH_DAY,-1, -1, -1, -1, -1, -1, -1, -1, DCH_CC, DCH_DAY,-1,
-1, -1, DCH_HH24,-1, DCH_J, -1, -1, DCH_MI, -1, -1, DCH_FX, -1, DCH_HH24,-1, DCH_J, -1, -1, DCH_MI, -1, -1,
-1, DCH_Q, DCH_RM, DCH_SSSS,-1, -1, -1, DCH_WW, -1, DCH_Y_YYY, -1, DCH_Q, DCH_RM, DCH_SSSS,-1, -1, -1, DCH_WW, -1, DCH_Y_YYY,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
DCH_day,-1, -1, -1, -1, -1, -1, -1, -1, DCH_month, DCH_day,-1, -1, -1, -1, -1, -1, -1, -1, DCH_month,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1
/*---- chars over 126 are skiped ----*/
};
/* ----------
* KeyWords index for NUMBER version
* ----------
*/
static int NUM_index[256 - 32] = {
/*
0 1 2 3 4 5 6 7 8 9
*/
/*---- first 0..31 chars are skiped ----*/
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, NUM_COMMA,-1, NUM_DEC,-1, NUM_0, -1,
-1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
-1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
NUM_PL,-1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, NUM_rn, -1, NUM_th, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1 -1, -1, -1, -1, -1, -1
/*---- chars over 126 are skiped ----*/
}; };
/* ----------
* Number processor struct
* ----------
*/
typedef struct NUMProc
{
int type; /* FROM_CHAR (TO_NUMBER) or TO_CHAR */
NUMDesc *Num; /* number description */
int sign, /* '-' or '+' */
sign_wrote, /* is sign wrote */
count_num, /* number of non-write digits */
in_number, /* is inside number */
pre_number; /* space before number */
char *number, /* string with number */
*number_p, /* pointer to current number pozition */
*inout, /* in / out buffer */
*inout_p, /* pointer to current inout pozition */
*L_negative_sign, /* Locale */
*L_positive_sign,
*decimal,
*L_thousands_sep,
*L_currency_symbol;
} NUMProc;
/* ----------
* Functions
* ----------
*/
static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index);
static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
static void parse_format(FormatNode *node, char *str, KeyWord *kw,
KeySuffix *suf, int *index, int ver, NUMDesc *Num);
static char *DCH_action(FormatNode *node, char *inout, int flag);
#ifdef DEBUG_TO_FROM_CHAR
static void dump_index(KeyWord *k, int *index);
static void dump_node(FormatNode *node, int max);
#endif
/*------ static char *get_th(char *num, int type);
* Fast sequential search, use index for selection data which static char *str_numth(char *dest, char *num, int type);
static int int4len(int4 num);
static char *str_toupper(char *buff);
static char *str_tolower(char *buff);
static int is_acdc(char *str, int *len);
static int seq_search(char *name, char **array, int type, int max, int *len);
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
static char *fill_str(char *str, int c, int max);
static FormatNode *NUM_cache( int len, char *CacheStr, FormatNode *CacheFormat,
NUMDesc *CacheNum, NUMDesc *Num, char *pars_str, int *flag);
static char *int_to_roman(int number);
static void NUM_prepare_locale(NUMProc *Np);
static int NUM_numpart(NUMProc *Np, int id);
static char *NUM_processor (FormatNode *node, NUMDesc *Num, char *inout, char *number,
int plen, int sign, int type);
/* ----------
* Fast sequential search, use index for data selection which
* go to seq. cycle (it is very fast for non-wanted strings) * go to seq. cycle (it is very fast for non-wanted strings)
* (can't be used binary search in format parsing) * (can't be used binary search in format parsing)
*------ * ----------
*/ */
static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index) static KeyWord *
index_seq_search(char *str, KeyWord *kw, int *index)
{ {
int poz; int poz;
if ( (poz = *(index + *str)) > -1) { if (! KeyWord_INDEX_FILTER(*str))
return (KeyWord *) NULL;
if ( (poz = *(index + (*str - ' '))) > -1) {
KeyWord *k = kw+poz; KeyWord *k = kw+poz;
...@@ -347,7 +648,8 @@ static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index) ...@@ -347,7 +648,8 @@ static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index)
return (KeyWord *) NULL; return (KeyWord *) NULL;
} }
static KeySuffix *suff_search(char *str, KeySuffix *suf, int type) static KeySuffix *
suff_search(char *str, KeySuffix *suf, int type)
{ {
KeySuffix *s; KeySuffix *s;
...@@ -361,63 +663,196 @@ static KeySuffix *suff_search(char *str, KeySuffix *suf, int type) ...@@ -361,63 +663,196 @@ static KeySuffix *suff_search(char *str, KeySuffix *suf, int type)
return (KeySuffix *) NULL; return (KeySuffix *) NULL;
} }
/*------ /* ----------
* Prepare NUMDesc (number description struct) via FormatNode struct
* ----------
*/
static void
NUMDesc_prepare(NUMDesc *num, FormatNode *n)
{
if (n->type != NODE_TYPE_ACTION)
return;
switch(n->key->id) {
case NUM_9:
if (IS_MULTI(num)) {
++num->multi;
break;
}
if (IS_DECIMAL(num))
++num->pos;
else
++num->pre;
break;
case NUM_0:
if (num->pre == 0)
num->flag |= NUM_F_ZERO;
if (! IS_DECIMAL(num))
++num->pre;
else
++num->pos;
break;
case NUM_B:
if (num->pre == 0 && num->pos == 0 && (! IS_ZERO(num)))
num->flag |= NUM_F_BLANK;
break;
case NUM_D:
num->flag |= NUM_F_LDECIMAL;
num->need_locale = TRUE;
case NUM_DEC:
if (IS_DECIMAL(num))
elog(ERROR, "to_char/number(): not unique decimal poit.");
if (IS_MULTI(num))
elog(ERROR, "to_char/number(): can't use 'V' and decimal poin together.");
num->flag |= NUM_F_DECIMAL;
break;
case NUM_FM:
num->flag |= NUM_F_FILLMODE;
break;
case NUM_S:
if (! IS_DECIMAL(num)) {
num->lsign = NUM_LSIGN_PRE;
num->pre_lsign_num = num->pre;
num->need_locale = TRUE;
} else if (num->lsign == NUM_LSIGN_NONE) {
num->lsign = NUM_LSIGN_POST;
num->need_locale = TRUE;
}
break;
case NUM_MI:
num->flag |= NUM_F_MINUS;
break;
case NUM_PL:
num->flag |= NUM_F_PLUS;
break;
case NUM_SG:
num->flag |= NUM_F_MINUS;
num->flag |= NUM_F_PLUS;
break;
case NUM_PR:
num->flag |= NUM_F_BRACKET;
break;
case NUM_rn:
case NUM_RN:
num->flag |= NUM_F_ROMAN;
break;
case NUM_L:
case NUM_G:
num->need_locale = TRUE;
break;
case NUM_V:
if (IS_DECIMAL(num))
elog(ERROR, "to_char/number(): can't use 'V' and decimal poin together.");
num->flag |= NUM_F_MULTI;
break;
}
return;
}
/* ----------
* Format parser, search small keywords and keyword's suffixes, and make * Format parser, search small keywords and keyword's suffixes, and make
* format-node tree. * format-node tree.
*------ *
* for DATE-TIME & NUMBER version
* ----------
*/ */
#undef FUNC_NAME static void
#define FUNC_NAME "parse_format" parse_format(FormatNode *node, char *str, KeyWord *kw,
KeySuffix *suf, int *index, int ver, NUMDesc *Num)
static void parse_format(FormatNode *node, char *str, KeyWord *kw,
KeySuffix *suf, int *index)
{ {
KeySuffix *s; KeySuffix *s;
FormatNode *n; FormatNode *n;
int node_set=0, int node_set=0,
suffix, suffix,
last=0; last=0;
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "to-from_char(): run parser.");
#endif
n = node; n = node;
while(*str) { while(*str) {
suffix=0; suffix=0;
/* prefix */ /* ----------
if ((s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL) { * Prefix
* ----------
*/
if (ver==DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL) {
suffix |= s->id; suffix |= s->id;
if (s->len) if (s->len)
str += s->len; str += s->len;
} }
/* keyword */ /* ----------
* Keyword
* ----------
*/
if (*str && (n->key = index_seq_search(str, kw, index)) != NULL) { if (*str && (n->key = index_seq_search(str, kw, index)) != NULL) {
n->type = NODE_TYPE_ACTION; n->type = NODE_TYPE_ACTION;
n->suffix = 0; n->suffix = 0;
node_set= 1; node_set= 1;
if (n->key->len) if (n->key->len)
str += n->key->len; str += n->key->len;
/* postfix */ /* ----------
if (*str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL) { * NUM version: Prepare global NUMDesc struct
* ----------
*/
if (ver==NUM_TYPE)
NUMDesc_prepare(Num, n);
/* ----------
* Postfix
* ----------
*/
if (ver==DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL) {
suffix |= s->id; suffix |= s->id;
if (s->len) if (s->len)
str += s->len; str += s->len;
} }
} else if (*str) { } else if (*str) {
/* special characters '\' and '"' */
/* ----------
* Special characters '\' and '"'
* ----------
*/
if (*str == '"' && last != '\\') { if (*str == '"' && last != '\\') {
int x = 0;
while(*(++str)) { while(*(++str)) {
if (*str == '"') { if (*str == '"' && x != '\\') {
str++; str++;
break; break;
} else if (*str == '\\' && x != '\\') {
x = '\\';
continue;
} }
n->type = NODE_TYPE_CHAR; n->type = NODE_TYPE_CHAR;
n->character = *str; n->character = *str;
n->key = (KeyWord *) NULL; n->key = (KeyWord *) NULL;
n->suffix = 0; n->suffix = 0;
++n; ++n;
x = *str;
} }
node_set = 0; node_set = 0;
suffix = 0; suffix = 0;
...@@ -435,6 +870,7 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw, ...@@ -435,6 +870,7 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw,
last = 0; last = 0;
str++; str++;
} }
} }
/* end */ /* end */
...@@ -442,9 +878,11 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw, ...@@ -442,9 +878,11 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw,
if (n->type == NODE_TYPE_ACTION) if (n->type == NODE_TYPE_ACTION)
n->suffix = suffix; n->suffix = suffix;
++n; ++n;
n->suffix = 0; n->suffix = 0;
node_set = 0; node_set = 0;
} }
} }
n->type = NODE_TYPE_END; n->type = NODE_TYPE_END;
...@@ -452,45 +890,61 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw, ...@@ -452,45 +890,61 @@ static void parse_format(FormatNode *node, char *str, KeyWord *kw,
return; return;
} }
/*------ /* ----------
* Call keyword's function for each of (action) node in format-node tree * Call keyword's function for each of (action) node in format-node tree
*------ * ----------
*/ */
static char *node_action(FormatNode *node, char *inout, int flag) static char *
DCH_action(FormatNode *node, char *inout, int flag)
{ {
FormatNode *n; FormatNode *n;
char *s; char *s;
for(n=node, s=inout; n->type != NODE_TYPE_END; n++, s++) {
/* ----------
* Zeroing global flags
* ----------
*/
DCH_global_flag = 0;
for(n=node, s=inout; n->type != NODE_TYPE_END; n++) {
if (n->type == NODE_TYPE_ACTION) { if (n->type == NODE_TYPE_ACTION) {
int len; int len;
/* /* ----------
* Call node action function * Call node action function
* ----------
*/ */
len = n->key->action(n->key->id, s, n->suffix, flag, n); len = n->key->action(n->key->id, s, n->suffix, flag, n);
if (len > 0) if (len > 0)
s += len; s += len;
else
continue;
} else { } else {
/* /* ----------
* Remove to output char from input in TO_CHAR * Remove to output char from input in TO_CHAR
* ----------
*/ */
if (flag == TO_CHAR) if (flag == TO_CHAR)
*s = n->character; *s = n->character;
else { else {
/* /* ----------
* Skip blank space in FROM_CHAR's input * Skip blank space in FROM_CHAR's input
* ----------
*/ */
if (isspace(n->character)) { if (isspace(n->character) && IS_FX == 0) {
while(*s != '\0' && isspace(*(s+1))) while(*s != '\0' && isspace(*(s+1)))
++s; ++s;
} }
} }
} }
++s; /* ! */
} }
if (flag == TO_CHAR) if (flag == TO_CHAR)
...@@ -498,76 +952,109 @@ static char *node_action(FormatNode *node, char *inout, int flag) ...@@ -498,76 +952,109 @@ static char *node_action(FormatNode *node, char *inout, int flag)
return inout; return inout;
} }
/* ----------
* DEBUG: Dump the FormatNode Tree (debug)
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
#define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
#define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
static void
dump_node(FormatNode *node, int max)
{
FormatNode *n;
int a;
elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
for(a=0, n=node; a<=max; n++, a++) {
if (n->type == NODE_TYPE_ACTION)
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION\t(%s,%s)",
a, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
else if (n->type == NODE_TYPE_CHAR)
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
else if (n->type == NODE_TYPE_END) {
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
return;
} else
elog(DEBUG_elog_output, "%d:\t UnKnown NODE !!!", a);
}
}
#endif
/***************************************************************************** /*****************************************************************************
* Private utils * Private utils
*****************************************************************************/ *****************************************************************************/
/*------ /* ----------
* Return ST/ND/RD/TH for simple (1..9) numbers * Return ST/ND/RD/TH for simple (1..9) numbers
* type --> 0 upper, 1 lower * type --> 0 upper, 1 lower
*------ * ----------
*/ */
static char *get_th(int num, int type) static char *
get_th(char *num, int type)
{ {
switch(num) { int len = strlen(num),
case 1: last;
last = *(num + (len-1));
if (!isdigit((unsigned char) last))
elog(ERROR, "get_th: '%s' is not number.", num);
/* 11 || 12 */
if (len == 2 && (last=='1' || last=='2') && *num == '1')
last=0;
switch(last) {
case '1':
if (type==TH_UPPER) return numTH[0]; if (type==TH_UPPER) return numTH[0];
return numth[0]; return numth[0];
case 2: case '2':
if (type==TH_UPPER) return numTH[1]; if (type==TH_UPPER) return numTH[1];
return numth[1]; return numth[1];
case 3: case '3':
if (type==TH_UPPER) return numTH[2]; if (type==TH_UPPER) return numTH[2];
return numth[2]; return numth[2];
} default:
if (type==TH_UPPER) return numTH[3]; if (type==TH_UPPER) return numTH[3];
return numth[3]; return numth[3];
}
return NULL;
} }
/*------ /* ----------
* Convert string-number to ordinal string-number * Convert string-number to ordinal string-number
* type --> 0 upper, 1 lower * type --> 0 upper, 1 lower
*------ * ----------
*/ */
#undef FUNC_NAME static char *
#define FUNC_NAME "str_numth" str_numth(char *dest, char *num, int type)
static char *str_numth(char *dest, char *src, int type)
{ {
int len = strlen(src), sprintf(dest, "%s%s", num, get_th(num, type));
num=0, f_num=0;
num = *(src+(len-1));
if (num < 48 || num > 57)
elog(ERROR, "%s: in '%s' is not number.", FUNC_NAME, src);
num -= 48;
if (num==1 || num==2) { /* 11 || 12 */
f_num = atoi(src);
if (abs(f_num)==11 || abs(f_num)==12)
num=0;
}
sprintf(dest, "%s%s", src, get_th(num, type));
return dest; return dest;
} }
/*------ /* ----------
* Return length of integer writed in string * Return length of integer writed in string
*------- * ----------
*/ */
static int int4len(int4 num) static int
int4len(int4 num)
{ {
char b[16]; char b[16];
return sprintf(b, "%d", num);
sprintf(b, "%d", num);
return strlen(b);
} }
/*------ /* ----------
* Convert string to upper-string * Convert string to upper-string
*------ * ----------
*/ */
static char *str_toupper(char *buff) static char *
str_toupper(char *buff)
{ {
char *p_buff=buff; char *p_buff=buff;
...@@ -578,17 +1065,34 @@ static char *str_toupper(char *buff) ...@@ -578,17 +1065,34 @@ static char *str_toupper(char *buff)
return buff; return buff;
} }
/*------ /* ----------
* Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC) * Convert string to lower-string
*------ * ----------
*/ */
static int is_acdc(char *str, int *len) static char *
str_tolower(char *buff)
{ {
char *p; char *p_buff=buff;
for(p=str; *p != '\0'; p++) { while (*p_buff) {
if (isspace(*p)) *p_buff = tolower((unsigned char) *p_buff);
continue; ++p_buff;
}
return buff;
}
/* ----------
* Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC)
* ----------
*/
static int
is_acdc(char *str, int *len)
{
char *p;
for(p=str; *p != '\0'; p++) {
if (isspace(*p))
continue;
if (*(p+1)) { if (*(p+1)) {
if (toupper(*p)=='B' && toupper(*(++p))=='C') { if (toupper(*p)=='B' && toupper(*(++p))=='C') {
...@@ -604,12 +1108,12 @@ static int is_acdc(char *str, int *len) ...@@ -604,12 +1108,12 @@ static int is_acdc(char *str, int *len)
return 0; return 0;
} }
/* ----------
/*------
* Sequential search with to upper/lower conversion * Sequential search with to upper/lower conversion
*------ * ----------
*/ */
static int seq_search(char *name, char **array, int type, int max, int *len) static int
seq_search(char *name, char **array, int type, int max, int *len)
{ {
char *p, *n, **a; char *p, *n, **a;
int last, i; int last, i;
...@@ -659,9 +1163,8 @@ static int seq_search(char *name, char **array, int type, int max, int *len) ...@@ -659,9 +1163,8 @@ static int seq_search(char *name, char **array, int type, int max, int *len)
} }
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p, *a, name); /* elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p, *a, name);*/
#endif #endif
if (*n != *p) if (*n != *p)
break; break;
} }
...@@ -672,44 +1175,68 @@ static int seq_search(char *name, char **array, int type, int max, int *len) ...@@ -672,44 +1175,68 @@ static int seq_search(char *name, char **array, int type, int max, int *len)
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
/*------- /* -----------
* Call for debug and for KWindex checking; (Show ASCII char and defined * DEBUG: Call for debug and for index checking; (Show ASCII char
* keyword for each used position * and defined keyword for each used position
*------- * ----------
*/ */
static void dump_KWindex() static void
dump_index(KeyWord *k, int *index)
{ {
int i; int i, count=0, free_i=0;
for(i=0; i<255; i++) { elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
if (KWindex[i] != -1)
elog(NOTICE, "%c: %s, ", i, keywords[ KWindex[i] ].name); for(i=0; i < KeyWord_INDEX_SIZE; i++) {
if (index[i] != -1) {
elog(DEBUG_elog_output, "\t%c: %s, ", i+32, k[ index[i] ].name);
count++;
} else {
free_i++;
elog(DEBUG_elog_output, "\t(%d) %c %d", i, i+32, index[i]);
} }
}
elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
count, free_i);
} }
#endif #endif
/***************************************************************************** /* ----------
* Master routines * Skip TM / th in FROM_CHAR
*****************************************************************************/ * ----------
/*
* Spip TM / th in FROM_CHAR
*/ */
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0) #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
/*------
* Master of TIME for TO_CHAR - write (inout) formated string /* ----------
* FROM_CHAR - scan (inout) string by course of FormatNode * Global format opton for DCH version
*------ * ----------
*/ */
#undef FUNC_NAME static int
#define FUNC_NAME "dch_time" dch_global(int arg, char *inout, int suf, int flag, FormatNode *node)
{
switch(arg) {
case DCH_FX:
DCH_global_flag |= DCH_F_FX;
break;
}
return 0;
}
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node) /* ----------
* Master function of TIME for:
* TO_CHAR - write (inout) formated string
* FROM_CHAR - scan (inout) string by course of FormatNode
* ----------
*/
static int
dch_time(int arg, char *inout, int suf, int flag, FormatNode *node)
{ {
char *p_inout = inout; char *p_inout = inout;
switch(arg) { switch(arg) {
case DCH_HH: case DCH_HH:
case DCH_HH12: case DCH_HH12:
if (flag == TO_CHAR) { if (flag == TO_CHAR) {
...@@ -787,38 +1314,37 @@ static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -787,38 +1314,37 @@ static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node)
str_numth(p_inout, inout, S_TH_TYPE(suf)); str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout)-1; return strlen(p_inout)-1;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: SSSS is not supported", FUNC_NAME); elog(ERROR, "to_datatime: SSSS is not supported");
} }
return 0; return 0;
} }
#define CHECK_SEQ_SEARCH(_l, _s) { \ #define CHECK_SEQ_SEARCH(_l, _s) { \
if (_l <= 0) { \ if (_l <= 0) { \
elog(ERROR, "%s: bad value for %s", FUNC_NAME, _s); \ elog(ERROR, "to_datatime: bad value for %s", _s); \
} \ } \
} }
/*------ /* ----------
* Master of DATE for TO_CHAR - write (inout) formated string * Master of DATE for:
* TO_CHAR - write (inout) formated string
* FROM_CHAR - scan (inout) string by course of FormatNode * FROM_CHAR - scan (inout) string by course of FormatNode
*------ * ----------
*/ */
#undef FUNC_NAME static int
#define FUNC_NAME "dch_date" dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
{ {
char buff[MAX_NODE_SIZ], char buff[ DCH_CACHE_SIZE ],
*p_inout; *p_inout;
int i, len; int i, len;
p_inout = inout; p_inout = inout;
/*------ /* ----------
* In the FROM-char is not difference between "January" or "JANUARY" * In the FROM-char is not difference between "January" or "JANUARY"
* or "january", all is before search convert to one-upper. * or "january", all is before search convert to "first-upper".
* This convention is used for MONTH, MON, DAY, DY * This convention is used for MONTH, MON, DAY, DY
*------ * ----------
*/ */
if (flag == FROM_CHAR) { if (flag == FROM_CHAR) {
if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month) { if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month) {
...@@ -977,7 +1503,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -977,7 +1503,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
return strlen(p_inout)-1; return strlen(p_inout)-1;
else return 1; else return 1;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: WW is not supported", FUNC_NAME); elog(ERROR, "to_datatime: WW is not supported");
case DCH_Q: case DCH_Q:
if (flag == TO_CHAR) { if (flag == TO_CHAR) {
sprintf(inout, "%d", (tm->tm_mon-1)/3+1); sprintf(inout, "%d", (tm->tm_mon-1)/3+1);
...@@ -987,7 +1513,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -987,7 +1513,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
return 2; return 2;
return 0; return 0;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: Q is not supported", FUNC_NAME); elog(ERROR, "to_datatime: Q is not supported");
case DCH_CC: case DCH_CC:
if (flag == TO_CHAR) { if (flag == TO_CHAR) {
i = tm->tm_year/100 +1; i = tm->tm_year/100 +1;
...@@ -999,7 +1525,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -999,7 +1525,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
str_numth(p_inout, inout, S_TH_TYPE(suf)); str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout)-1; return strlen(p_inout)-1;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: CC is not supported", FUNC_NAME); elog(ERROR, "to_datatime: CC is not supported");
case DCH_Y_YYY: case DCH_Y_YYY:
if (flag == TO_CHAR) { if (flag == TO_CHAR) {
i= YEAR_ABS(tm->tm_year) / 1000; i= YEAR_ABS(tm->tm_year) / 1000;
...@@ -1117,7 +1643,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -1117,7 +1643,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
if (S_THth(suf)) return 2; if (S_THth(suf)) return 2;
return 0; return 0;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: W is not supported", FUNC_NAME); elog(ERROR, "to_datatime: W is not supported");
case DCH_J: case DCH_J:
if (flag == TO_CHAR) { if (flag == TO_CHAR) {
sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
...@@ -1125,7 +1651,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -1125,7 +1651,7 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
str_numth(p_inout, inout, S_TH_TYPE(suf)); str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout)-1; return strlen(p_inout)-1;
} else if (flag == FROM_CHAR) } else if (flag == FROM_CHAR)
elog(ERROR, "%s: J is not supported", FUNC_NAME); elog(ERROR, "to_datatime: J is not supported");
} }
return 0; return 0;
} }
...@@ -1134,31 +1660,20 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node) ...@@ -1134,31 +1660,20 @@ static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
* Public routines * Public routines
***************************************************************************/ ***************************************************************************/
/* -------------------
/********************************************************************* * DATETIME to_char()
* * -------------------
* to_char */
* text *
* Syntax: datetime_to_char(DateTime *dt, text *fmt)
*
* text *to_char(DateTime *dt, text *fmt)
*
* Purpose:
*
* Returns string, with date and/or time, formated at
* argument 'fmt'
*
*********************************************************************/
#undef FUNC_NAME
#define FUNC_NAME "to_char"
text
*to_char(DateTime *dt, text *fmt)
{ {
FormatNode *tree; static FormatNode CacheFormat[ DCH_CACHE_SIZE +1];
static char CacheStr[ DCH_CACHE_SIZE +1];
text *result; text *result;
char *pars_str; FormatNode *format;
int flag=0;
char *str;
double fsec; double fsec;
char *tzn; char *tzn;
int len=0, tz; int len=0, tz;
...@@ -1184,57 +1699,114 @@ text ...@@ -1184,57 +1699,114 @@ text
datetime2tm(SetDateTime(*dt), &tz, tm, &fsec, &tzn); datetime2tm(SetDateTime(*dt), &tz, tm, &fsec, &tzn);
} else { } else {
if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) != 0) if (datetime2tm(*dt, &tz, tm, &fsec, &tzn) != 0)
elog(ERROR, "s%: Unable to convert datetime to tm", FUNC_NAME); elog(ERROR, "to_char: Unable to convert datetime to tm");
} }
/* In dt.c is j2day as static :-(((
tm->tm_wday = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
must j2day convert itself...
*/
tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7; tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7;
tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1,1) +1; tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1,1) +1;
tree = (FormatNode *) palloc(len * sizeof(FormatNode) +1); /* ----------
result = (text *) palloc( len * MAX_NODE_SIZ + VARHDRSZ); * Convert VARDATA() to string
(tree + len)->type = NODE_LAST; * ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, VARDATA(fmt), len);
*(str + len) = '\0';
pars_str = VARDATA(fmt); /* ----------
pars_str[ len ] = '\0'; * Allocate result
* ----------
*/
result = (text *) palloc( (len * DCH_MAX_ITEM_SIZ) + 1 + VARHDRSZ);
parse_format( tree, pars_str, keywords, suff, KWindex); /* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if ( len > DCH_CACHE_SIZE ) {
node_action(tree, VARDATA(result), TO_CHAR); format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; flag = 1;
parse_format(format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
} else {
/* ----------
* Use cache buffers
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "DCH_TO_CHAR() Len: %d", len);
elog(DEBUG_elog_output, "DCH_TO_CHAR() Cache - str: >%s<", CacheStr);
elog(DEBUG_elog_output, "DCH_TO_CHAR() Arg.str: >%s<", str);
#endif
flag = 0;
if (strcmp(CacheStr, str) != 0) {
/* ----------
* Can't use the cache, must run parser and save a original
* format-picture string to the cache.
* ----------
*/
strncpy(CacheStr, str, DCH_CACHE_SIZE);
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(CacheFormat, len); */
/* dump_index(DCH_keywords, DCH_index); */
#endif
parse_format(CacheFormat, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */
}
format = CacheFormat;
}
DCH_action(format, VARDATA(result), TO_CHAR);
if (flag)
pfree(format);
pfree(str);
VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ;
return result; return result;
} }
/********************************************************************* /* -------------------
* * TIMESTAMP to_char()
* from_char * -------------------
* */
* Syntax: text *
* timestamp_to_char(time_t dt, text *fmt)
* DateTime *from_char(text *date_str, text *fmt) {
* return datetime_to_char( timestamp_datetime(dt), fmt);
* Purpose: }
/* ---------------------
* TO_DATETIME()
* *
* Make DateTime from date_str which is formated at argument 'fmt' * Make DateTime from date_str which is formated at argument 'fmt'
* ( from_char is reverse to_char() ) * ( to_datetime is reverse to_char() )
* * ---------------------
*********************************************************************/ */
DateTime *
#undef FUNC_NAME to_datetime(text *date_str, text *fmt)
#define FUNC_NAME "from_char"
DateTime
*from_char(text *date_str, text *fmt)
{ {
FormatNode *tree; static FormatNode CacheFormat[ DCH_CACHE_SIZE +1];
static char CacheStr[ DCH_CACHE_SIZE +1];
FormatNode *format;
int flag=0;
DateTime *result; DateTime *result;
char *pars_str; char *str;
int len=0, int len=0,
fsec=0, fsec=0,
tz=0; tz=0;
...@@ -1253,14 +1825,75 @@ DateTime ...@@ -1253,14 +1825,75 @@ DateTime
len = VARSIZE(fmt) - VARHDRSZ; len = VARSIZE(fmt) - VARHDRSZ;
if (len) { if (len) {
tree = (FormatNode *) palloc((len+1) * sizeof(FormatNode));
(tree + len)->type = NODE_LAST;
pars_str = VARDATA(fmt); /* ----------
pars_str[ len ] = '\0'; * Convert VARDATA() to string
parse_format( tree, pars_str, keywords, suff, KWindex); * ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, VARDATA(fmt), len);
*(str + len) = '\0';
/* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if ( len > DCH_CACHE_SIZE ) {
format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
flag = 1;
parse_format(format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
} else {
/* ----------
* Use cache buffers
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "DCH_TO_CHAR() Len: %d", len);
elog(DEBUG_elog_output, "DCH_TO_CHAR() Cache - str: >%s<", CacheStr);
elog(DEBUG_elog_output, "DCH_TO_CHAR() Arg.str: >%s<", str);
#endif
flag = 0;
if (strcmp(CacheStr, str) != 0) {
/* ----------
* Can't use the cache, must run parser and save a original
* format-picture string to the cache.
* ----------
*/
strncpy(CacheStr, str, DCH_CACHE_SIZE);
parse_format(CacheFormat, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */
}
format = CacheFormat;
}
/* ----------
* Call action for each node in FormatNode tree
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(format, len); */
#endif
VARDATA(date_str)[ VARSIZE(date_str) - VARHDRSZ ] = '\0'; VARDATA(date_str)[ VARSIZE(date_str) - VARHDRSZ ] = '\0';
node_action(tree, VARDATA(date_str), FROM_CHAR); DCH_action(format, VARDATA(date_str), FROM_CHAR);
pfree(str);
if (flag)
pfree(format);
} }
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
...@@ -1274,7 +1907,7 @@ DateTime ...@@ -1274,7 +1907,7 @@ DateTime
tm->tm_mon -= 1; tm->tm_mon -= 1;
#ifdef DEBUG_TO_FROM_CHAR #ifdef DEBUG_TO_FROM_CHAR
elog(NOTICE, "TO-FROM_CHAR: Call mktime()"); elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()");
NOTICE_TM; NOTICE_TM;
#endif #endif
mktime(tm); mktime(tm);
...@@ -1306,77 +1939,1219 @@ DateTime ...@@ -1306,77 +1939,1219 @@ DateTime
NOTICE_TM; NOTICE_TM;
#endif #endif
if (tm2datetime(tm, fsec, &tz, result) != 0) if (tm2datetime(tm, fsec, &tz, result) != 0)
elog(ERROR, "%s: can't convert 'tm' to datetime.", FUNC_NAME); elog(ERROR, "to_datatime: can't convert 'tm' to datetime.");
return result; return result;
} }
/********************************************************************* /* ----------
* * TO_DATE
* to_date
*
* Syntax:
*
* DateADT *to_date(text *date_str, text *fmt)
*
* Purpose:
*
* Make Date from date_str which is formated at argument 'fmt' * Make Date from date_str which is formated at argument 'fmt'
* * ----------
*/
DateADT
to_date(text *date_str, text *fmt)
{
return datetime_date( to_datetime(date_str, fmt) );
}
/* ----------
* TO_TIMESTAMP
* Make timestamp from date_str which is formated at argument 'fmt'
* ----------
*/
time_t
to_timestamp(text *date_str, text *fmt)
{
return datetime_timestamp( to_datetime(date_str, fmt) );
}
/**********************************************************************
* the NUMBER version part
*********************************************************************/ *********************************************************************/
#undef FUNC_NAME
#define FUNC_NAME "to_date"
DateADT static char *
to_date(text *date_str, text *fmt) fill_str(char *str, int c, int max)
{ {
return datetime_date( from_char(date_str, fmt) ); memset(str, c, max);
*(str+max+1) = '\0';
return str;
} }
/******************************************************************** /* ----------
* * Cache routine for NUM to_char version
* ordinal * ----------
* */
* Syntax: static FormatNode *
* NUM_cache( int len, char *CacheStr, FormatNode *CacheFormat,
* text *ordinal(int4 num, text type) NUMDesc *CacheNum, NUMDesc *Num, char *pars_str, int *flag)
* {
* Purpose: FormatNode *format;
char *str;
/* ----------
* Convert VARDATA() to string
* ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, pars_str, len);
*(str + len) = '\0';
/* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if ( len > NUM_CACHE_SIZE ) {
format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
*flag = 1;
Num->flag = 0;
Num->lsign = 0;
Num->pre = 0;
Num->pos = 0;
Num->pre_lsign_num = 0;
parse_format(format, str, NUM_keywords,
NULL, NUM_index, NUM_TYPE, Num);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
pfree(str);
return format;
} else {
/* ----------
* Use cache buffer
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "NUM_TO_CHAR() Len: %d", len);
elog(DEBUG_elog_output, "NUM_TO_CHAR() Cache - str: >%s<", CacheStr);
elog(DEBUG_elog_output, "NUM_TO_CHAR() Arg.str: >%s<", str);
#endif
*flag = 0;
if (strcmp(CacheStr, str) != 0) {
/* ----------
* Can't use the cache, must run parser and save a original
* format-picture string to the cache.
* ----------
*/
strncpy(CacheStr, str, NUM_CACHE_SIZE);
/* ----------
* Set zeros to CacheNum struct
* ----------
*/
CacheNum->flag = 0;
CacheNum->lsign = 0;
CacheNum->pre = 0;
CacheNum->pos = 0;
CacheNum->pre_lsign_num = 0;
CacheNum->need_locale = 0;
CacheNum->multi = 0;
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(CacheFormat, len); */
/* dump_index(NUM_keywords, NUM_index); */
#endif
parse_format(CacheFormat, str, NUM_keywords,
NULL, NUM_index, NUM_TYPE, CacheNum);
(CacheFormat + len)->type = NODE_TYPE_END; /* Paranoa? */
}
/* ----------
* Copy cache to used struct
* ----------
*/
Num->flag = CacheNum->flag;
Num->lsign = CacheNum->lsign;
Num->pre = CacheNum->pre;
Num->pos = CacheNum->pos;
Num->pre_lsign_num = CacheNum->pre_lsign_num;
Num->need_locale = CacheNum->need_locale;
Num->multi = CacheNum->multi;
pfree(str);
return CacheFormat;
}
}
static char *
int_to_roman(int number)
{
int len = 0,
num = 0,
set = 0;
char *p = NULL,
*result,
numstr[5];
result = (char *) palloc( 16 );
*result = '\0';
if (number > 3999 || number < 1) {
fill_str(result, '#', 15);
return result;
}
len = sprintf(numstr, "%d", number);
for(p=numstr; *p!='\0'; p++, --len) {
num = *p - 49; /* 48 ascii + 1 */
if (num < 0)
continue;
if (num == -1 && set==0)
continue;
set = 1;
if (len > 3) {
while(num-- != -1)
strcat(result, "M");
} else {
if (len==3)
strcat(result, rm100[num]);
else if (len==2)
strcat(result, rm10[num]);
else if (len==1)
strcat(result, rm1[num]);
}
}
return result;
}
/* ----------
* Locale
* ----------
*/
static void
NUM_prepare_locale(NUMProc *Np)
{
#ifdef USE_LOCALE
if (Np->Num->need_locale) {
struct lconv *lconv;
/* ----------
* Get locales
* ----------
*/
lconv = PGLC_localeconv();
/* ----------
* Positive / Negative number sign
* ----------
*/
if (lconv->negative_sign && *lconv->negative_sign)
Np->L_negative_sign = lconv->negative_sign;
else
Np->L_negative_sign = "-";
if (lconv->positive_sign && *lconv->positive_sign)
Np->L_positive_sign = lconv->positive_sign;
else
Np->L_positive_sign = "+";
/* ----------
* Number thousands separator
* ----------
*/
if (lconv->thousands_sep && *lconv->thousands_sep)
Np->L_thousands_sep = lconv->thousands_sep;
else
Np->L_thousands_sep = ",";
/* ----------
* Number decimal point
* ----------
*/
if (lconv->decimal_point && *lconv->decimal_point)
Np->decimal = lconv->decimal_point;
else
Np->decimal = ".";
/* ----------
* Currency symbol
* ----------
*/
if (lconv->currency_symbol && *lconv->currency_symbol)
Np->L_currency_symbol = lconv->currency_symbol;
else
Np->L_currency_symbol = " ";
if (!IS_LDECIMAL(Np->Num))
Np->decimal = ".";
} else {
#endif
/* ----------
* Default values
* ----------
*/
Np->L_negative_sign = "-";
Np->L_positive_sign = "+";
Np->decimal = ".";
Np->L_thousands_sep = ",";
Np->L_currency_symbol = " ";
#ifdef USE_LOCALE
}
#endif
}
/* ----------
* SET SIGN macro - set 'S' or 'PR' befor number
* ----------
*/
#define SET_SIGN(_np) { \
if ((_np)->sign_wrote==0) { \
if (IS_BRACKET((_np)->Num)) { \
\
*(_np)->inout_p = '<'; \
++(_np)->inout_p; \
(_np)->sign_wrote = 1; \
\
} else if ((_np)->Num->lsign==NUM_LSIGN_PRE && (! IS_BRACKET((_np)->Num)) && \
(! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \
\
if ((_np)->sign=='-') \
strcpy((_np)->inout_p, (_np)->L_negative_sign); \
else \
strcpy((_np)->inout_p, (_np)->L_positive_sign); \
(_np)->inout_p += strlen((_np)->inout_p); \
(_np)->sign_wrote = 1; \
\
} else { \
if ((_np)->sign=='-' && (_np)->Num->lsign==NUM_LSIGN_NONE \
&& (! IS_BRACKET((_np)->Num)) && \
(! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \
\
*(_np)->inout_p = '-'; \
++(_np)->inout_p; \
(_np)->sign_wrote=1; \
\
} else if ((! IS_FILLMODE((_np)->Num)) && (_np)->Num->lsign==NUM_LSIGN_NONE && \
(! IS_BRACKET((_np)->Num)) && (! IS_MINUS((_np)->Num)) && (! IS_PLUS((_np)->Num))) { \
\
*(_np)->inout_p = ' '; \
++(_np)->inout_p; \
(_np)->sign_wrote=1; \
} \
} \
} \
}
/* ----------
* Create number
* return FALSE if any work over current NUM_[0|9|D|DEC] must be skip
* in NUM_processor()
* ----------
*/
static int
NUM_numpart(NUMProc *Np, int id)
{
if (IS_ROMAN(Np->Num))
return FALSE;
/* Note: in this elog() output not set '\0' in inout
elog(DEBUG_elog_output, "sign_w: %d, count: %d, plen %d, sn: %s, inout: '%s'",
Np->sign_wrote, Np->count_num, Np->pre_number, Np->number_p, Np->inout);
*/
/* ----------
* Number extraction for TO_NUMBER()
* ----------
*/
if (Np->type == FROM_CHAR) {
if (id == NUM_9 || id == NUM_0) {
if (!*(Np->number+1)) {
if (*Np->inout_p == '-' ||
(IS_BRACKET(Np->Num) &&
*Np->inout_p == '<' )) {
*Np->number = '-';
Np->inout_p++;
} else if (*Np->inout_p == '+') {
*Np->number = '+';
Np->inout_p++;
} else if (*Np->inout_p == ' ' && (! IS_FILLMODE(Np->Num))) {
Np->inout_p++;
}
}
if (isdigit((unsigned char) *Np->inout_p)) {
*Np->number_p = *Np->inout_p;
Np->number_p++;
}
} else {
if (id == NUM_DEC) {
*Np->number_p = '.';
Np->number_p++;
} else if (id == NUM_D) {
int x = strlen(Np->decimal);
if (!strncmp(Np->inout_p, Np->decimal, x)) {
Np->inout_p += x-1;
*Np->number_p = '.';
Np->number_p++;
}
}
}
return TRUE;
}
/* ----------
* Add blank space (pre number)
* ----------
*/
if ((! IS_ZERO(Np->Num)) && (! IS_FILLMODE(Np->Num)) && Np->pre_number > 0) {
*Np->inout_p = ' ';
--Np->pre_number;
--Np->count_num;
return TRUE;
}
/* ----------
* Add zero (pre number)
* ----------
*/
if (IS_ZERO(Np->Num) && Np->pre_number > 0) {
SET_SIGN(Np);
*Np->inout_p='0';
--Np->pre_number;
--Np->count_num;
return TRUE;
}
if (IS_FILLMODE(Np->Num) && Np->pre_number > 0) {
--Np->pre_number;
return FALSE;
}
/* ----------
* Add sign or '<' or ' '
* ----------
*/
if (Np->number_p==Np->number && Np->sign_wrote==0) {
SET_SIGN(Np);
}
/* ----------
* Add decimal point
* ----------
*/
if (*Np->number_p=='.') {
strcpy(Np->inout_p, Np->decimal);
Np->inout_p += strlen(Np->inout_p);
if (*Np->number_p)
++Np->number_p;
return FALSE;
}
if (*Np->number_p) {
/* ----------
* Copy number string to inout string
* ----------
*/
*Np->inout_p = *Np->number_p;
++Np->number_p;
Np->in_number = 1;
/* ----------
* Number end (set post 'S' or '>'
* ----------
*/
if ((--Np->count_num)==0) {
if (IS_BRACKET(Np->Num)) {
++Np->inout_p;
*Np->inout_p = '>';
Np->sign_wrote =1;
}
if (Np->Num->lsign==NUM_LSIGN_POST && Np->sign_wrote==0) {
++Np->inout_p;
if (Np->sign=='-')
strcpy(Np->inout_p, Np->L_negative_sign);
else
strcpy(Np->inout_p, Np->L_positive_sign);
Np->inout_p += strlen(Np->inout_p)-1;
Np->sign_wrote = 1;
}
}
} else
return FALSE;
return TRUE;
}
/* ----------
* Master function for NUMBER version.
* type (variant):
* TO_CHAR
* -> create formatted string by course of FormatNode
* *
* Add to number 'th' suffix and return this as text. * FROM_CHAR (TO_NUMBER)
* -> extract number string from formatted string (reverse TO_CHAR)
* *
********************************************************************/ * - ! this version use non-string 'inout' VARDATA(),
* and 'plen' is used for VARSIZE()
* - value 'sign' is not used
* - number is creating in Np->number and first position (*Np->number)
* in this buffer is reserved for +/- sign
* ----------
*/
static char *
NUM_processor (FormatNode *node, NUMDesc *Num, char *inout, char *number,
int plen, int sign, int type)
{
FormatNode *n;
NUMProc _Np, *Np = &_Np;
Np->Num = Num;
Np->type = type;
Np->number = number;
if (type == TO_CHAR) {
Np->pre_number = plen;
Np->sign = sign;
} else if (type == FROM_CHAR) {
Np->pre_number = 0;
*Np->number = ' '; /* sign space */
*(Np->number+1) = '\0';
Np->sign = 0;
}
Np->inout = inout;
Np->sign_wrote = Np->count_num = Np->in_number = 0;
/* ----------
* Roman corection
* ----------
*/
if (IS_ROMAN(Np->Num)) {
if (Np->type==FROM_CHAR)
elog(ERROR, "to_number: RN is not supported");
Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->pos =
Np->Num->pre = Np->pre_number = Np->sign = 0;
if (IS_FILLMODE(Np->Num)){
Np->Num->flag = 0;
Np->Num->flag |= NUM_F_FILLMODE;
} else {
Np->Num->flag = 0;
}
Np->Num->flag |= NUM_F_ROMAN;
}
/* ----------
* Check Num struct
* ----------
*/
if (Np->sign=='+')
Np->Num->flag &= ~NUM_F_BRACKET;
if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
Np->Num->lsign = NUM_LSIGN_POST;
/* set counter (number of all digits) */
Np->count_num = Np->Num->pos + Np->Num->pre;
if (Np->type==TO_CHAR && IS_FILLMODE(Np->Num) && (! IS_ZERO(Np->Num)))
Np->count_num -= Np->pre_number;
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "NUM: '%s', PRE: %d, POS: %d, PLEN: %d, LSIGN: %d, DECIMAL: %d, MULTI: %d",
Np->number, Np->Num->pre, Np->Num->pos,
Np->pre_number, Np->Num->lsign,IS_DECIMAL(Np->Num),
Np->Num->multi);
#endif
/* ----------
* Locale
* ----------
*/
NUM_prepare_locale(Np);
/* need for DEBUG: memset(Np->inout, '\0', Np->count_num );*/
/* ----------
* Processor direct cycle
* ----------
*/
if (Np->type == FROM_CHAR)
Np->number_p=Np->number+1; /* first char is space for sign */
else if (Np->type == TO_CHAR)
Np->number_p=Np->number;
for(n=node, Np->inout_p=Np->inout; n->type != NODE_TYPE_END; n++) {
if (Np->type == FROM_CHAR) {
/* ----------
* Check non-string inout end
* ----------
*/
if (Np->inout_p == Np->inout + plen)
break;
}
/* ----------
* Format pictures actions
* ----------
*/
if (n->type == NODE_TYPE_ACTION) {
/* ----------
* Create/reading digit/zero/blank/sing
* ----------
*/
switch( n->key->id ) {
case NUM_9:
case NUM_0:
case NUM_DEC:
case NUM_D:
if (NUM_numpart(Np, n->key->id))
break;
else
continue;
case NUM_COMMA:
if (Np->type == TO_CHAR) {
if (!Np->in_number) {
if (IS_FILLMODE(Np->Num))
continue;
else
*Np->inout_p= ' ';
} else
*Np->inout_p = ',';
} else if (Np->type == FROM_CHAR) {
if (!Np->in_number) {
if (IS_FILLMODE(Np->Num))
continue;
}
}
break;
case NUM_G:
if (Np->type == TO_CHAR) {
if (!Np->in_number) {
if (IS_FILLMODE(Np->Num))
continue;
else {
int x = strlen(Np->L_thousands_sep);
memset(Np->inout_p, ' ', x);
Np->inout_p += x-1;
}
} else {
strcpy(Np->inout_p, Np->L_thousands_sep);
Np->inout_p += strlen(Np->inout_p)-1;
}
} else if (Np->type == FROM_CHAR) {
if (!Np->in_number) {
if (IS_FILLMODE(Np->Num))
continue;
}
Np->inout_p += strlen(Np->L_thousands_sep)-1;
}
break;
case NUM_L:
if (Np->type == TO_CHAR) {
strcpy(Np->inout_p, Np->L_currency_symbol);
Np->inout_p += strlen(Np->inout_p)-1;
} else if (Np->type == FROM_CHAR) {
Np->inout_p += strlen(Np->L_currency_symbol)-1;
}
break;
case NUM_MI:
if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) {
if (Np->type == TO_CHAR)
continue;
else
break;
}
if (Np->type == TO_CHAR) {
if (Np->sign=='-')
*Np->inout_p = '-';
else
*Np->inout_p = ' ';
} else if (Np->type == FROM_CHAR) {
if (*Np->inout_p == '-')
*Np->number = '-';
}
Np->sign_wrote=1;
break;
case NUM_PL:
if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) {
if (Np->type == TO_CHAR)
continue;
else
break;
}
if (Np->type == TO_CHAR) {
if (Np->sign=='+')
*Np->inout_p = '+';
else
*Np->inout_p = ' ';
} else if (Np->type == FROM_CHAR) {
if (*Np->inout_p == '+')
*Np->number = '+';
}
Np->sign_wrote=1;
break;
case NUM_SG:
if (Np->sign_wrote==1 || IS_BRACKET(Np->Num)) {
if (Np->type == TO_CHAR)
continue;
else
break;
}
if (Np->type == TO_CHAR)
*Np->inout_p = Np->sign;
else if (Np->type == FROM_CHAR) {
if (*Np->inout_p == '-')
*Np->number = '-';
else if (*Np->inout_p == '+')
*Np->number = '+';
}
Np->sign_wrote=1;
break;
case NUM_RN:
if (Np->type == FROM_CHAR)
elog(ERROR, "TO_NUMBER: internal error #1");
if (IS_FILLMODE(Np->Num)) {
strcpy(Np->inout_p, Np->number_p);
Np->inout_p += strlen(Np->inout_p) - 1;
} else
Np->inout_p += sprintf(Np->inout_p, "%15s", Np->number_p) -1;
break;
case NUM_rn:
if (Np->type == FROM_CHAR)
elog(ERROR, "TO_NUMBER: internal error #2");
if (IS_FILLMODE(Np->Num)) {
strcpy(Np->inout_p, str_tolower(Np->number_p));
Np->inout_p += strlen(Np->inout_p) - 1;
} else
Np->inout_p += sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p)) -1;
break;
case NUM_th:
if (IS_ROMAN(Np->Num) || *Np->number=='#' ||
Np->sign=='-' || IS_DECIMAL(Np->Num))
continue;
if (Np->type == TO_CHAR)
strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
Np->inout_p += 1;
break;
case NUM_TH:
if (IS_ROMAN(Np->Num) || *Np->number=='#' ||
Np->sign=='-' || IS_DECIMAL(Np->Num))
continue;
if (Np->type == TO_CHAR)
strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
Np->inout_p += 1;
break;
case NUM_S:
/* ----------
* 'S' for TO_CHAR is in NUM_numpart()
* ----------
*/
if (Np->type == FROM_CHAR && Np->sign_wrote==FALSE) {
int x = strlen(Np->L_negative_sign);
if (!strncmp(Np->inout_p, Np->L_negative_sign, x)) {
Np->inout_p += x-1;
*Np->number = '-';
break;
}
x = strlen(Np->L_positive_sign);
if (!strncmp(Np->inout_p, Np->L_positive_sign, x)) {
Np->inout_p += x-1;
*Np->number = '+';
break;
}
} else
continue;
break;
default:
continue;
break;
}
} else {
/* ----------
* Remove to output char from input in TO_CHAR
* ----------
*/
if (Np->type == TO_CHAR)
*Np->inout_p = n->character;
}
Np->inout_p++;
}
if (Np->type == TO_CHAR) {
*Np->inout_p = '\0';
return Np->inout;
} else if (Np->type == FROM_CHAR) {
*Np->number_p = '\0';
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
#endif
return Np->number;
} else
return NULL;
}
/* ----------
* MACRO: Start part of NUM - for all NUM's to_char variants
* (sorry, but I hate copy same code - macro is better..)
* ----------
*/
#define NUM_TOCHAR_prepare { \
if (!PointerIsValid(fmt)) \
return NULL; \
\
len = VARSIZE(fmt) - VARHDRSZ; \
\
if (!len) \
return textin(""); \
\
result = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
format = NUM_cache(len, CacheStr, CacheFormat, &CacheNum, &Num, \
VARDATA(fmt), &flag); \
}
#undef FUNC_NAME /* ----------
#define FUNC_NAME "ordinal" * MACRO: Finish part of NUM
* ----------
*/
#define NUM_TOCHAR_finish { \
\
NUM_processor(format, &Num, VARDATA(result), \
numstr, plen, sign, TO_CHAR); \
pfree(orgnum); \
\
if (flag) \
pfree(format); \
\
VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; \
}
text /* -------------------
*ordinal(int4 num, text *typ) * NUMERIC to_number()
* -------------------
*/
Numeric
numeric_to_number(text *value, text *fmt)
{ {
text *result; static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
int ttt=0; static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
if (!PointerIsValid(typ)) NUMDesc Num;
FormatNode *format;
char *numstr;
int flag=0;
int len=0;
int scale, precision;
if ((!PointerIsValid(value)) || (!PointerIsValid(fmt)))
return NULL; return NULL;
VARDATA(typ)[ VARSIZE(typ) - VARHDRSZ ] = '\0'; len = VARSIZE(fmt) - VARHDRSZ;
if (!len)
return numeric_in(NULL, 0, 0);
format = NUM_cache(len, CacheStr, CacheFormat, &CacheNum, &Num,
VARDATA(fmt), &flag);
numstr = (char *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1);
NUM_processor(format, &Num, VARDATA(value), numstr,
VARSIZE(value) - VARHDRSZ, 0, FROM_CHAR);
if (!strcmp("TH", VARDATA(typ))) scale = Num.pos;
ttt = TH_UPPER; precision = MAX(0, Num.pre) + scale;
else if (!strcmp("th", VARDATA(typ)))
ttt = TH_LOWER; return numeric_in(numstr, 0, ((precision << 16) | scale) + VARHDRSZ);
}
/* ------------------
* NUMERIC to_char()
* ------------------
*/
text *
numeric_to_char(Numeric value, text *fmt)
{
static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
NUMDesc Num;
FormatNode *format;
text *result;
int flag=0;
int len=0, plen=0, sign=0;
char *numstr, *orgnum, *p;
NUM_TOCHAR_prepare;
/* ----------
* On DateType depend part (numeric)
* ----------
*/
if (IS_ROMAN(&Num)) {
numstr = orgnum = int_to_roman( numeric_int4( numeric_round(value, 0)));
} else {
Numeric val = value;
if (IS_MULTI(&Num)) {
val = numeric_mul(value,
numeric_power(int4_numeric(10), int4_numeric(Num.multi)));
Num.pre += Num.multi;
}
orgnum = numeric_out( numeric_round(val, Num.pos) );
if (*orgnum == '-') { /* < 0 */
sign = '-';
numstr = orgnum+1;
} else {
sign = '+';
numstr = orgnum;
}
if ((p = strchr(numstr, '.')))
len = p - numstr;
else else
elog(ERROR, "%s: bad type '%s' (allowed: 'TH' or 'th')", len = strlen(numstr);
FUNC_NAME, VARDATA(typ));
result = (text *) palloc(16); /* ! not int8 ! */ if (Num.pre > len)
plen = Num.pre - len;
sprintf(VARDATA(result), "%d", (int) num); else if (len > Num.pre) {
str_numth(VARDATA(result), VARDATA(result), ttt); fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.pos);
}
}
/*dump_node(format, VARSIZE(fmt) - VARHDRSZ);*/
NUM_TOCHAR_finish;
return result;
}
VARSIZE(result) = strlen(VARDATA(result)) + VARHDRSZ; /* ---------------
* INT4 to_char()
* ---------------
*/
text *
int4_to_char(int32 value, text *fmt)
{
static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
NUMDesc Num;
FormatNode *format;
text *result;
int flag=0;
int len=0, plen=0, sign=0;
char *numstr, *orgnum;
NUM_TOCHAR_prepare;
/* ----------
* On DateType depend part (int32)
* ----------
*/
if (IS_ROMAN(&Num)) {
numstr = orgnum = int_to_roman( value );
} else {
if (IS_MULTI(&Num)) {
orgnum = int4out(int4mul(value, (int32) pow( (double)10, (double) Num.multi)));
Num.pre += Num.multi;
} else
orgnum = int4out(value);
len = strlen(orgnum);
if (*orgnum == '-') { /* < 0 */
sign = '-';
--len;
} else
sign = '+';
if (Num.pos) {
int i;
numstr = palloc( len + 1 + Num.pos );
strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
*(numstr + len) = '.';
for(i=len+1; i<=Num.pos+len+1; i++)
*(numstr+i) = '0';
*(numstr + Num.pos + len + 1) = '\0';
pfree(orgnum);
orgnum = numstr;
} else
numstr = orgnum + (*orgnum == '-' ? 1 : 0);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre) {
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.pos);
}
}
/*dump_node(format, len);*/
NUM_TOCHAR_finish;
return result;
}
/* ---------------
* INT8 to_char()
* ---------------
*/
text *
int8_to_char(int64 *value, text *fmt)
{
static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
NUMDesc Num;
FormatNode *format;
text *result;
int flag=0;
int len=0, plen=0, sign=0;
char *numstr, *orgnum;
NUM_TOCHAR_prepare;
/* ----------
* On DateType depend part (int32)
* ----------
*/
if (IS_ROMAN(&Num)) {
numstr = orgnum = int_to_roman( int84( value ));
} else {
if (IS_MULTI(&Num)) {
double multi = pow( (double)10, (double) Num.multi);
orgnum = int8out( int8mul(value, dtoi8( (float64) &multi )));
Num.pre += Num.multi;
} else
orgnum = int8out(value);
len = strlen(orgnum);
if (*orgnum == '-') { /* < 0 */
sign = '-';
--len;
} else
sign = '+';
if (Num.pos) {
int i;
numstr = palloc( len + 1 + Num.pos );
strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
*(numstr + len) = '.';
for(i=len+1; i<=Num.pos+len+1; i++)
*(numstr+i) = '0';
*(numstr + Num.pos + len + 1) = '\0';
pfree(orgnum);
orgnum = numstr;
} else
numstr = orgnum + (*orgnum == '-' ? 1 : 0);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre) {
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.pos);
}
}
NUM_TOCHAR_finish;
return result;
}
/* -----------------
* FLOAT4 to_char()
* -----------------
*/
text *
float4_to_char(float32 value, text *fmt)
{
static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
NUMDesc Num;
FormatNode *format;
text *result;
int flag=0;
int len=0, plen=0, sign=0;
char *numstr, *orgnum, *p;
NUM_TOCHAR_prepare;
if (IS_ROMAN(&Num)) {
numstr = orgnum = int_to_roman( (int) rint( *value ));
} else {
float32 val = value;
if (IS_MULTI(&Num)) {
float multi = pow( (double) 10, (double) Num.multi);
val = float4mul(value, (float32) &multi);
Num.pre += Num.multi;
}
orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
len = sprintf(orgnum, "%.0f", fabs(*val));
if (Num.pre > len)
plen = Num.pre - len;
if (len >= FLT_DIG)
Num.pos = 0;
else if (Num.pos + len > FLT_DIG)
Num.pos = FLT_DIG - len;
sprintf(orgnum, "%.*f", Num.pos, *val);
if (*orgnum == '-') { /* < 0 */
sign = '-';
numstr = orgnum+1;
} else {
sign = '+';
numstr = orgnum;
}
if ((p = strchr(numstr, '.')))
len = p - numstr;
else
len = strlen(numstr);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre) {
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.pos);
}
}
NUM_TOCHAR_finish;
return result;
}
/* -----------------
* FLOAT8 to_char()
* -----------------
*/
text *
float8_to_char(float64 value, text *fmt)
{
static FormatNode CacheFormat[ NUM_CACHE_SIZE +1];
static char CacheStr[ NUM_CACHE_SIZE +1];
static NUMDesc CacheNum;
NUMDesc Num;
FormatNode *format;
text *result;
int flag=0;
int len=0, plen=0, sign=0;
char *numstr, *orgnum, *p;
NUM_TOCHAR_prepare;
if (IS_ROMAN(&Num)) {
numstr = orgnum = int_to_roman( (int) rint( *value ));
} else {
float64 val = value;
if (IS_MULTI(&Num)) {
double multi = pow( (double) 10, (double) Num.multi);
val = float8mul(value, (float64) &multi);
Num.pre += Num.multi;
}
orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
len = sprintf(orgnum, "%.0f", fabs(*val));
if (Num.pre > len)
plen = Num.pre - len;
if (len >= DBL_DIG)
Num.pos = 0;
else if (Num.pos + len > DBL_DIG)
Num.pos = DBL_DIG - len;
sprintf(orgnum, "%.*f", Num.pos, *val);
if (*orgnum == '-') { /* < 0 */
sign = '-';
numstr = orgnum+1;
} else {
sign = '+';
numstr = orgnum;
}
if ((p = strchr(numstr, '.')))
len = p - numstr;
else
len = strlen(numstr);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre) {
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.pos);
}
}
NUM_TOCHAR_finish;
return result; return result;
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_proc.h,v 1.116 2000/01/24 07:16:52 tgl Exp $ * $Id: pg_proc.h,v 1.117 2000/01/25 23:53:52 momjian Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -2343,6 +2343,30 @@ DESCR("larger of two numbers"); ...@@ -2343,6 +2343,30 @@ DESCR("larger of two numbers");
DATA(insert OID = 1769 ( numeric_cmp PGUID 11 f t t 2 f 23 "1700 1700" 100 0 0 100 numeric_cmp - )); DATA(insert OID = 1769 ( numeric_cmp PGUID 11 f t t 2 f 23 "1700 1700" 100 0 0 100 numeric_cmp - ));
DESCR("compare two numbers"); DESCR("compare two numbers");
/* formatting */
DATA(insert OID = 1770 ( to_char PGUID 11 f t f 2 f 25 "1184 25" 100 0 0 100 datetime_to_char - ));
DESCR("convert / formatting datetime to text");
DATA(insert OID = 1771 ( to_char PGUID 11 f t f 2 f 25 "1296 25" 100 0 0 100 timestamp_to_char - ));
DESCR("convert / formatting timestamp to text");
DATA(insert OID = 1772 ( to_char PGUID 11 f t f 2 f 25 "1700 25" 100 0 0 100 numeric_to_char - ));
DESCR("convert / formatting numeric to text");
DATA(insert OID = 1773 ( to_char PGUID 11 f t f 2 f 25 "23 25" 100 0 0 100 int4_to_char - ));
DESCR("convert / formatting int4 to text");
DATA(insert OID = 1774 ( to_char PGUID 11 f t f 2 f 25 "20 25" 100 0 0 100 int8_to_char - ));
DESCR("convert / formatting int8 to text");
DATA(insert OID = 1775 ( to_char PGUID 11 f t f 2 f 25 "700 25" 100 0 0 100 float4_to_char - ));
DESCR("convert / formatting float4 to text");
DATA(insert OID = 1776 ( to_char PGUID 11 f t f 2 f 25 "701 25" 100 0 0 100 float8_to_char - ));
DESCR("convert / formatting float8 to text");
DATA(insert OID = 1777 ( to_number PGUID 11 f t f 2 f 1700 "25 25" 100 0 0 100 numeric_to_number - ));
DESCR("convert text to numeric");
DATA(insert OID = 1778 ( to_datetime PGUID 11 f t f 2 f 1184 "25 25" 100 0 0 100 to_datetime - ));
DESCR("convert text to datetime");
DATA(insert OID = 1779 ( to_timestamp PGUID 11 f t f 2 f 1296 "25 25" 100 0 0 100 to_timestamp - ));
DESCR("convert text to datetime");
DATA(insert OID = 1780 ( to_date PGUID 11 f t f 2 f 1082 "25 25" 100 0 0 100 to_date - ));
DESCR("convert text to date");
/* /*
* prototypes for functions pg_proc.c * prototypes for functions pg_proc.c
......
/* -----------------------------------------------------------------------
* formatting.h
*
* $Id: formatting.h,v 1.1 2000/01/25 23:53:56 momjian Exp $
*
*
* The PostgreSQL routines for a DateTime/int/float/numeric formatting,
* inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
*
* 1999 Karel Zak "Zakkr"
*
* -----------------------------------------------------------------------
*/
#ifndef _FORMATTING_H_
#define _FORMATTING_H_
extern text *datetime_to_char(DateTime *dt, text *fmt);
extern text *timestamp_to_char(time_t dt, text *fmt);
extern DateTime *to_datetime(text *date_str, text *fmt);
extern time_t to_timestamp(text *date_str, text *fmt);
extern DateADT to_date(text *date_str, text *fmt);
extern Numeric numeric_to_number(text *value, text *fmt);
extern text *numeric_to_char(Numeric value, text *fmt);
extern text *int4_to_char(int32 value, text *fmt);
extern text *int8_to_char(int64 *value, text *fmt);
extern text *float4_to_char(float32 value, text *fmt);
extern text *float8_to_char(float64 value, text *fmt);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment