diff --git a/ChangeLog b/ChangeLog index ff34efd70e..39763d0688 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32206,7 +32206,8 @@ * firebird2/src/jrd/: GlobalRWLock.cpp (1.3), cch.cpp (1.160): - Small NBAK performance optimization from Rom an Simakov + Small NBAK performance optimization from Rom +an Simakov 2007-05-04 16:48 asfernandes diff --git a/autogen.sh b/autogen.sh index f9032095f9..a7a5f2a2a9 100755 --- a/autogen.sh +++ b/autogen.sh @@ -80,6 +80,7 @@ fi # If NOCONFIGURE is set, skip the call to configure if test "x$NOCONFIGURE" = "x"; then + conf_flags="$conf_flags --enable-binreloc" echo Running $SRCDIR/configure $conf_flags "$@" ... rm -f config.cache config.log chmod a+x $SRCDIR/configure diff --git a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj index d9d9516f54..a9c94a2c07 100644 --- a/builds/mac_os_x/CS/CS.pbproj/project.pbxproj +++ b/builds/mac_os_x/CS/CS.pbproj/project.pbxproj @@ -4286,11 +4286,6 @@ path = fun_proto.h; refType = 4; }; - F616C5E30200B0CF01EF0ADE = { - isa = PBXFileReference; - path = functions.cpp; - refType = 4; - }; F616C5E40200B0CF01EF0ADE = { isa = PBXFileReference; path = gds.bas; @@ -7498,11 +7493,6 @@ path = example.mak; refType = 4; }; - F616C9260200B0D001EF0ADE = { - isa = PBXFileReference; - path = functions.c; - refType = 4; - }; F616C9270200B0D001EF0ADE = { isa = PBXFileReference; path = indexoff.sql; diff --git a/builds/posix/Makefile.in.embed.fbudf b/builds/posix/Makefile.in.embed.fbudf index 51af961741..fa19f532b2 100644 --- a/builds/posix/Makefile.in.embed.fbudf +++ b/builds/posix/Makefile.in.embed.fbudf @@ -66,7 +66,7 @@ all: lib_fbudf lib_fbudf: $(LIBIBUTIL_SO) $(UDF)/fbudf.$(SHRLIB_EXT) $(UDF)/fbudf.$(SHRLIB_EXT): $(FBUDF_Objects) $(FBUTIL_Objects) - $(call LINK_UDF,fbudf) -o $@ $^ $(LINK_UDF_LIBS) -lfbembed + $(call LINK_UDF,fbudf) $(LINK_EMPTY_SYMBOLS) -o $@ $^ $(LINK_UDF_LIBS) -lfbembed include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.embed.gbak b/builds/posix/Makefile.in.embed.gbak index 705f96ba6a..a9e245d8ff 100644 --- a/builds/posix/Makefile.in.embed.gbak +++ b/builds/posix/Makefile.in.embed.gbak @@ -62,14 +62,14 @@ all: gbak gsplit gbak : $(LIBFBEMBED_SO) $(GBAK) $(GBAK): $(GBAK_Objects) $(CLUMPLETS_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) gsplit : $(LIBFBEMBED_SO) $(GSPLIT) $(GSPLIT): $(GSPLIT_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.embed.gdef b/builds/posix/Makefile.in.embed.gdef index 4a29ea316f..322e0b466f 100644 --- a/builds/posix/Makefile.in.embed.gdef +++ b/builds/posix/Makefile.in.embed.gdef @@ -63,7 +63,7 @@ all: gdef gdef: $(GDEF) $(GDEF): $(GDEF_Objects) $(CLUMPLETS_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) diff --git a/builds/posix/Makefile.in.embed.gfix b/builds/posix/Makefile.in.embed.gfix index 0a379c8e3b..6937f1b2c4 100644 --- a/builds/posix/Makefile.in.embed.gfix +++ b/builds/posix/Makefile.in.embed.gfix @@ -59,7 +59,7 @@ Dependencies = $(AllObjects:.o=.d) gfix : $(LIBFBEMBED_SO) $(GFIX) $(GFIX): $(AllObjects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.embed.gpre b/builds/posix/Makefile.in.embed.gpre index c2407f28cc..af396b9105 100644 --- a/builds/posix/Makefile.in.embed.gpre +++ b/builds/posix/Makefile.in.embed.gpre @@ -58,7 +58,7 @@ Dependencies = $(AllObjects:.o=.d) gpre : $(LIBFBEMBED_SO) $(GPRE) $(GPRE): $(GPRE_Objects) $(CLUMPLETS_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.embed.isql b/builds/posix/Makefile.in.embed.isql index f0a8100d91..82f74f76ca 100644 --- a/builds/posix/Makefile.in.embed.isql +++ b/builds/posix/Makefile.in.embed.isql @@ -59,7 +59,7 @@ Dependencies = $(AllObjects:.o=.d) isql : $(LIBFBEMBED_SO) $(ISQL) $(ISQL): $(ISQL_Objects) $(CLUMPLETS_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LIBEDITLINE) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LIBEDITLINE) $(LINK_LIBS) include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.embed.qli b/builds/posix/Makefile.in.embed.qli index 83757b2390..d277a06cf3 100644 --- a/builds/posix/Makefile.in.embed.qli +++ b/builds/posix/Makefile.in.embed.qli @@ -61,7 +61,7 @@ all: qli qli : create_yachts $(LIBFBEMBED_SO) $(QLI) $(QLI): $(QLI_Objects) $(CLUMPLETS_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) # EKU: At this point yachts.lnk is a link to empty.fdb, but gpre will fail # with it. metadata.fdb is what is needed here. diff --git a/builds/posix/Makefile.in.embed.util b/builds/posix/Makefile.in.embed.util index 977cf68203..d108164f0e 100644 --- a/builds/posix/Makefile.in.embed.util +++ b/builds/posix/Makefile.in.embed.util @@ -154,68 +154,68 @@ $(CREATE_DB): $(CREATEDB_Objects) $(COMMON_Objects) $(LIBFBSTATIC_A) nbackup: $(LIBFBEMBED_SO) $(NBACKUP) $(NBACKUP): $(NBACKUP_Objects) $(CLUMPLETS_Objects) $(COMMON_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) fb_lock_print: $(LIBFBEMBED_SO) $(LOCKPRINT) $(LOCKPRINT): $(LOCKPRINT_Objects) $(COMMON_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) gstat : $(LIBFBEMBED_SO) $(GSTAT) $(GSTAT): $(GSTAT_Objects) $(CLUMPLETS_Objects) $(COMMON_Objects) $(FBCONFIG_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) gds_drop: $(GDS_DROP) $(GDS_DROP): $(DROP_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) fbsvcmgr: $(FBSVCMGR) $(FBSVCMGR): $(FBSVCMGR_Objects) $(LIBFBEMBED_SO) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) fbtracemgr: $(FBTRACEMGR) $(FBTRACEMGR): $(FBTRACEMGR_Objects) $(LIBFBEMBED_SO) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) gds_relay: $(GDS_RELAY) $(GDS_RELAY): $(RELAY_Objects) $(LIBFBEMBED_SO) - $(EXE_LINK) $(LINK_OPTS) $(RELAY_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $(RELAY_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) gsec: $(GSEC) $(GSEC): $(GSEC_Objects) $(CLUMPLETS_Objects) $(COMMON_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) fbguard: $(LIBFBCLIENT_SO) $(FBGUARD) $(FBGUARD): $(FBGUARD_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ -L$(LIB) -lfbclient $(LIB_GUI) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ -L$(LIB) -lfbclient $(LIB_GUI) $(LINK_LIBS) ibmgr_bin: $(IBMGR_BIN) $(IBMGR_BIN): $(IBMGR_Objects) $(LIBFBEMBED_SO) - $(EXE_LINK) $(LINK_OPTS) $(IBMGR_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $(IBMGR_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) # This one needs a bit of work. rebuild: $(GDS_REBUILD) $(GDS_REBUILD): $(REBUILD_Objects) $(LIBFBEMBED_SO) - $(EXE_LINK) $(LINK_OPTS) $(REBUILD_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $(REBUILD_Objects) -o $@ $(FBEMBED_LINK) $(LINK_LIBS) include $(ROOT)/gen/make.shared.targets diff --git a/builds/posix/Makefile.in.examples b/builds/posix/Makefile.in.examples index 6c0c230d8f..ac0d0cc7e4 100644 --- a/builds/posix/Makefile.in.examples +++ b/builds/posix/Makefile.in.examples @@ -79,12 +79,12 @@ INTL_Sources = $(addprefix $(EXAMPLES_DEST)/, $(INTL_Files)) EMPLOYEE_DB= $(EXAMPLES_DEST)/employee.fdb INTLEMP_DB= $(EXAMPLES_DEST)/intlemp.fdb -.PHONY: all examples +.PHONY: all examples plugins_examples -all: examples +all: examples plugins_examples # examples: $(EMPLOYEE_DB) $(INTLEMP_DB) $(FIREBIRD)/examples/README -examples: $(EMPLOYEE_DB) $(FIREBIRD)/examples/README +examples: $(EMPLOYEE_DB) $(FIREBIRD)/examples/README plugins_examples $(FIREBIRD)/examples/README: $(CP) $(ROOT)/examples/readme $(FIREBIRD)/examples/README @@ -165,3 +165,6 @@ $(EXAMPLES_DEST)/%.e: $(EXAMPLES_SRC)/empbuild/%.e $(EXAMPLES_DEST)/%.h: $(EXAMPLES_SRC)/common/%.h $(CP) $^ $@ + +plugins_examples: # plugins examples + $(MAKE) -f $(GEN_ROOT)/examples/Makefile.plugins_examples diff --git a/builds/posix/Makefile.in.fbserver b/builds/posix/Makefile.in.fbserver index 43697786b5..c0586d01eb 100644 --- a/builds/posix/Makefile.in.fbserver +++ b/builds/posix/Makefile.in.fbserver @@ -49,7 +49,7 @@ SERVER_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(SERVER_Source fbserver : $(FB_SUPER_SERVER) $(FB_SUPER_SERVER): $(SERVER_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ -L$(LIB) $(LIB_GUI) $(LINK_LIBS) $(ICU_LIBS) + $(EXE_LINK) $(LINK_EXEC_EXPORT) $(LINK_FIREBIRD_SYMBOLS) $(LINK_OPTS) $^ -o $@ -L$(LIB) $(LIB_GUI) $(LINK_LIBS) $(ICU_LIBS) AllObjects = $(SERVER_Objects) diff --git a/builds/posix/Makefile.in.firebird b/builds/posix/Makefile.in.firebird index e4b9e7cad1..8624b1629d 100644 --- a/builds/posix/Makefile.in.firebird +++ b/builds/posix/Makefile.in.firebird @@ -298,10 +298,10 @@ security2.fdb: gdef isql # build the security database # database earlier, then perhaps we could build the jrdlib in one step and # include the security bits and pieces. -.PHONY: ref_databases msgs msgs_intl generated_headers intl extlib includes +.PHONY: ref_databases msgs msgs_intl generated_headers intl extlib plugins includes basic_targets: ref_databases msgs msgs_intl generated_headers \ - intl extlib includes examples_cp + intl extlib plugins includes examples_cp # hack to make code regeneration work generated_headers : @@ -319,6 +319,9 @@ intl: # international lang components $(FIREBIRD)/intl/ extlib: # external programs than can be called $(MAKE) -f $(GEN_ROOT)/Makefile.extlib +plugins: # plugins + $(MAKE) -f $(GEN_ROOT)/Makefile.plugins + # distribution header include files includes: include_generic diff --git a/builds/posix/Makefile.in.plugins b/builds/posix/Makefile.in.plugins new file mode 100644 index 0000000000..765eb10668 --- /dev/null +++ b/builds/posix/Makefile.in.plugins @@ -0,0 +1,72 @@ +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# You may obtain a copy of the Licence at +# http://www.gnu.org/licences/lgpl.html +# +# As a special exception this file can also be included in modules +# with other source code as long as that source code has been +# released under an Open Source Initiative certificed licence. +# More information about OSI certification can be found at: +# http://www.opensource.org +# +# This module is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public Licence for more details. +# +# This module was created by members of the firebird development +# team. All individual contributions remain the Copyright (C) of +# those individuals and all rights are reserved. Contributors to +# this file are either listed below or can be obtained from a CVS +# history command. +# +# Created by: Mark O'Donohue +# +# Contributor(s): +# Adriano dos Santos Fernandes +# +ROOT=.. +ObjModuleType=std + +include $(ROOT)/gen/make.defaults +include $(ROOT)/gen/make.platform +include $(ROOT)/gen/make.rules +include $(ROOT)/gen/make.shared.variables + +@SET_MAKE@ + +PLUGINS= $(FIREBIRD)/plugins + + +UDRENG_Files = UdrEngine.cpp +UDRENG_Sources = $(addprefix plugins/udr_engine/, $(UDRENG_Files)) jrd/mod_loader.cpp +UDRENG_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(UDRENG_Sources)))) + + +AllObjects = $(UDRENG_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) +Dependencies = $(AllObjects:.o=.d) + + +.PHONY: all udr_engine + +all: udr_engine $(PLUGINS)/udr_engine.conf + + +udr_engine: $(PLUGINS)/udr_engine.$(SHRLIB_EXT) + +$(PLUGINS)/udr_engine.$(SHRLIB_EXT): $(UDRENG_Objects) $(FBCOMMON_Objects) $(FBCLASSES_Objects) $(OS_SPECIFIC_Objects) +ifeq ($(PLATFORM),DARWIN) + $(LIB_LINK) $(LIB_BUNDLE_OPTIONS) -o $@ $^ @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ +else + $(LIB_LINK) $(LINK_UDRENG_SYMBOLS) $(LIB_LINK_OPTIONS) $(LIB_LINK_SONAME)udr_engine.$(SHRLIB_EXT) \ +$(LIB_PATH_OPTS) -o $@ $^ $(THR_LIBS) $(SO_LINK_LIBS) +endif + +$(PLUGINS)/udr_engine.conf: $(ROOT)/src/plugins/udr_engine/udr_engine.conf + cp $^ $@ + +include $(ROOT)/gen/make.shared.targets + +-include $(Dependencies) diff --git a/builds/posix/Makefile.in.plugins_examples b/builds/posix/Makefile.in.plugins_examples new file mode 100644 index 0000000000..7df8560906 --- /dev/null +++ b/builds/posix/Makefile.in.plugins_examples @@ -0,0 +1,71 @@ +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# You may obtain a copy of the Licence at +# http://www.gnu.org/licences/lgpl.html +# +# As a special exception this file can also be included in modules +# with other source code as long as that source code has been +# released under an Open Source Initiative certificed licence. +# More information about OSI certification can be found at: +# http://www.opensource.org +# +# This module is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public Licence for more details. +# +# This module was created by members of the firebird development +# team. All individual contributions remain the Copyright (C) of +# those individuals and all rights are reserved. Contributors to +# this file are either listed below or can be obtained from a CVS +# history command. +# +# Created by: Mark O'Donohue +# +# Contributor(s): +# Adriano dos Santos Fernandes +# +ROOT=../.. +ObjModuleType=std + +CXXFLAGS+= -I$(ROOT)/gen/firebird/include + +include $(ROOT)/gen/make.defaults +include $(ROOT)/gen/make.platform +include $(ROOT)/gen/make.rules +include $(ROOT)/gen/make.shared.variables + +@SET_MAKE@ + +PLUGINS= $(FIREBIRD)/plugins + + +UDR_Files = UdrCppExample.cpp +UDR_Sources = $(addprefix ../examples/udr/, $(UDR_Files)) +UDR_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(UDR_Sources)))) + + +AllObjects = $(UDR_Objects) +Dependencies = $(AllObjects:.o=.d) + + +.PHONY: all udrcpp_example + +all: udrcpp_example + + +udrcpp_example: $(PLUGINS)/udr/udrcpp_example.$(SHRLIB_EXT) + +$(PLUGINS)/udr/udrcpp_example.$(SHRLIB_EXT): $(UDR_Objects) +ifeq ($(PLATFORM),DARWIN) + $(LIB_LINK) $(LIB_BUNDLE_OPTIONS) -o $@ $^ @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ +else + $(LIB_LINK) $(LIB_LINK_OPTIONS) $(LIB_LINK_SONAME)udrcpp_example.$(SHRLIB_EXT) \ +$(LIB_PATH_OPTS) -o $@ $^ $(THR_LIBS) $(PLUGINS)/udr_engine.so +endif + +include $(ROOT)/gen/make.shared.targets + +-include $(Dependencies) diff --git a/builds/posix/Makefile.in.smp_server b/builds/posix/Makefile.in.smp_server index d313459710..744db436da 100644 --- a/builds/posix/Makefile.in.smp_server +++ b/builds/posix/Makefile.in.smp_server @@ -60,7 +60,7 @@ $(OS_SPECIFIC_Objects) fb_smp_server: $(FB_DAEMON) $(FB_DAEMON): $(SERVER_Objects) - $(EXE_LINK) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) + $(EXE_LINK) $(LINK_EMPTY_SYMBOLS) $(LINK_OPTS) $^ -o $@ $(FBEMBED_LINK) $(LINK_LIBS) AllObjects = $(SERVER_Objects) diff --git a/builds/posix/empty.vers b/builds/posix/empty.vers new file mode 100644 index 0000000000..ac6db4b002 --- /dev/null +++ b/builds/posix/empty.vers @@ -0,0 +1,26 @@ +# Version script to hide all symbols from executables. +# +# The contents of this file are subject to the Initial +# Developer's Public License Version 1.0 (the "License"); +# you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. +# +# Software distributed under the License is distributed AS IS, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. +# See the License for the specific language governing rights +# and limitations under the License. +# +# The Original Code was created by Adriano dos Santos Fernandes +# for the Firebird Open Source RDBMS project. +# +# Copyright (c) 2009 Adriano dos Santos Fernandes +# and all contributors signed below. +# +# All Rights Reserved. +# Contributor(s): ______________________________________. + +{ +local: + *; +}; diff --git a/builds/posix/make.defaults b/builds/posix/make.defaults index d8c537b4da..73d5c6141d 100755 --- a/builds/posix/make.defaults +++ b/builds/posix/make.defaults @@ -252,12 +252,15 @@ LINK_FIREBIRD_CLIENT_SYMBOLS = $(LINK_FIREBIRD_SYMBOLS) LINK_TRACE_SYMBOLS = $(LIB_LINK_MAPFILE)$(ROOT)/builds/posix/fbtrace.vers LINK_FBINTL_SYMBOLS = $(LIB_LINK_MAPFILE)$(ROOT)/builds/posix/fbintl.vers LINK_IBUTIL_SYMBOLS = $(LIB_LINK_MAPFILE)$(ROOT)/builds/posix/ib_util.vers +LINK_UDRENG_SYMBOLS = $(LIB_LINK_MAPFILE)$(ROOT)/builds/posix/udr_engine.vers +LINK_EMPTY_SYMBOLS = $(LIB_LINK_MAPFILE)$(ROOT)/builds/posix/empty.vers +LINK_EXEC_EXPORT=-rdynamic LIB_PLATFORM_RPATH = -Wl,-rpath,$(1) ifeq ($(strip @BINRELOC_CFLAGS@),) LIB_LINK_RPATH = $(call LIB_PLATFORM_RPATH,$(if $(subst intl,,$(1)),@FB_LIBDIR@,@FB_INTLDIR@)) else -LIB_LINK_RPATH = $(call LIB_PLATFORM_RPATH,'$$$$ORIGIN/../$(1)') +LIB_LINK_RPATH = $(call LIB_PLATFORM_RPATH,'$$ORIGIN/../$(1)') endif LIB_PATH_OPTS = $(call LIB_LINK_RPATH,lib) $(call LIB_LINK_RPATH,intl) LIB_LINK_SONAME= -Wl,-soname,$(1) diff --git a/builds/posix/make.shared.targets b/builds/posix/make.shared.targets index ff5c2a2b1c..b4ea460f3b 100644 --- a/builds/posix/make.shared.targets +++ b/builds/posix/make.shared.targets @@ -75,6 +75,10 @@ help.fdb: # Explicit dependencies on generated header $(OBJ)/jrd/par.o: $(SRC_ROOT)/include/gen/blrtable.h +# Explicit dependence on generated header (parser) +$(SRC_ROOT)/dsql/keywords.cpp: $(SRC_ROOT)/dsql/dsql.tab.h +$(OBJ)/dsql/Parser.h: $(SRC_ROOT)/dsql/dsql.tab.h + $(OBJ)/dsql/StmtNodes.o: $(SRC_ROOT)/include/gen/blrtable.h # Special cases for building cpp from epp @@ -84,6 +88,9 @@ $(OBJ)/dsql/metd.cpp: $(SRC_ROOT)/dsql/metd.epp $(OBJ)/dsql/DdlNodes.cpp: $(SRC_ROOT)/dsql/DdlNodes.epp $(GPRE_CURRENT) $(JRD_GPRE_FLAGS) $< $@ +$(OBJ)/dsql/PackageNodes.cpp: $(SRC_ROOT)/dsql/PackageNodes.epp + $(GPRE_CURRENT) $(JRD_GPRE_FLAGS) $< $@ + # Rebuild blrtable.h $(SRC_ROOT)/include/gen/blrtable.h: $(BLRTABLE) $(BLRTABLE) > $(SRC_ROOT)/include/gen/blrtable.h diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index 4f7c212695..b7ac74177d 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -25,20 +25,21 @@ JRD_ClientSources = $(addprefix jrd/, $(JRD_ClientFiles)) common/cvt.cpp JRD_ServerFiles= blob_filter.cpp cvt.cpp dpm.epp dyn.epp dyn_def.epp \ dyn_del.epp dyn_mod.epp dyn_util.epp fun.epp \ grant.epp ini.epp met.epp pcmet.epp scl.epp \ - CharSet.cpp Collation.cpp DatabaseSnapshot.cpp VirtualTable.cpp RecordBuffer.cpp \ + CharSet.cpp Collation.cpp DatabaseSnapshot.cpp VirtualTable.cpp WindowRsb.cpp RecordBuffer.cpp \ blb.cpp btn.cpp btr.cpp builtin.cpp \ GlobalRWLock.cpp cch.cpp cmp.cpp cvt2.cpp \ DataTypeUtil.cpp dfw.cpp UserManagement.cpp divorce.cpp \ - err.cpp event.cpp evl.cpp exe.cpp ext.cpp \ - execute_statement.cpp filters.cpp flu.cpp functions.cpp \ + err.cpp event.cpp ErrorImpl.cpp evl.cpp exe.cpp ext.cpp \ + execute_statement.cpp ExtEngineManager.cpp filters.cpp flu.cpp \ idx.cpp inf.cpp intl.cpp intl_builtin.cpp IntlManager.cpp \ IntlUtil.cpp isc_sync.cpp \ jrd.cpp Database.cpp lck.cpp \ mov.cpp nav.cpp opt.cpp Optimizer.cpp pag.cpp par.cpp \ - ods.cpp pwd.cpp PreparedStatement.cpp RandomGenerator.cpp \ + ods.cpp PluginManager.cpp pwd.cpp PreparedStatement.cpp RandomGenerator.cpp \ Relation.cpp ResultSet.cpp rlck.cpp rpb_chain.cpp rse.cpp \ sdw.cpp shut.cpp sort.cpp sqz.cpp \ - svc.cpp SysFunction.cpp TempSpace.cpp tpc.cpp tra.cpp validation.cpp vio.cpp \ + svc.cpp SysFunction.cpp TempSpace.cpp tpc.cpp tra.cpp validation.cpp \ + ValueImpl.cpp ValuesImpl.cpp vio.cpp \ nodebug.cpp nbak.cpp sha.cpp $(Physical_IO_Module) TextType.cpp \ unicode_util.cpp RuntimeStatistics.cpp DebugInterface.cpp \ extds/ExtDS.cpp extds/InternalDS.cpp extds/IscDS.cpp \ @@ -71,7 +72,7 @@ DSQL_ClientFiles = array.epp blob.epp \ DSQL_ServerFiles= metd.epp \ ddl.cpp dsql.cpp errd.cpp gen.cpp hsh.cpp make.cpp \ movd.cpp parse.cpp Parser.cpp pass1.cpp misc_func.cpp \ - DdlNodes.epp StmtNodes.cpp + DdlNodes.epp PackageNodes.epp StmtNodes.cpp DSQL_Files = $(DSQL_ClientFiles) $(DSQL_ServerFiles) diff --git a/builds/posix/udr_engine.hpux.vers b/builds/posix/udr_engine.hpux.vers new file mode 100644 index 0000000000..36b79d294d --- /dev/null +++ b/builds/posix/udr_engine.hpux.vers @@ -0,0 +1,33 @@ +# +# Version script to hide private symbols from Firebird libraries +# GNU and Solaris linkers should understand it +# +# The contents of this file are subject to the Initial +# Developer's Public License Version 1.0 (the "License"); +# you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. +# +# Software distributed under the License is distributed AS IS, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. +# See the License for the specific language governing rights +# and limitations under the License. +# +# The Original Code was created by Nickolay Samofatov +# for the Firebird Open Source RDBMS project. +# +# Copyright (c) 2004 Nickolay Samofatov +# and all contributors signed below. +# +# All Rights Reserved. +# Contributor(s): ______________________________________. +# Adriano dos Santos Fernandes +# +# + + ++e firebirdPlugin ++e fbUdrRegFunction ++e fbUdrRegProcedure ++e fbUdrRegTrigger ++e fbUdrGetFunction diff --git a/builds/posix/udr_engine.vers b/builds/posix/udr_engine.vers new file mode 100644 index 0000000000..0dd9d12d19 --- /dev/null +++ b/builds/posix/udr_engine.vers @@ -0,0 +1,39 @@ +# +# Version script to hide private symbols from Firebird libraries +# GNU and Solaris linkers should understand it +# +# The contents of this file are subject to the Initial +# Developer's Public License Version 1.0 (the "License"); +# you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. +# +# Software distributed under the License is distributed AS IS, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. +# See the License for the specific language governing rights +# and limitations under the License. +# +# The Original Code was created by Nickolay Samofatov +# for the Firebird Open Source RDBMS project. +# +# Copyright (c) 2004 Nickolay Samofatov +# and all contributors signed below. +# +# All Rights Reserved. +# Contributor(s): ______________________________________. +# Adriano dos Santos Fernandes +# +# + + +{ +global: + firebirdPlugin; + fbUdrRegFunction; + fbUdrRegProcedure; + fbUdrRegTrigger; + fbUdrGetFunction; + +local: + *; +}; diff --git a/builds/win32/defs/udr_engine.def b/builds/win32/defs/udr_engine.def new file mode 100644 index 0000000000..00a073358e --- /dev/null +++ b/builds/win32/defs/udr_engine.def @@ -0,0 +1,26 @@ +; The contents of this file are subject to the Initial +; Developer's Public License Version 1.0 (the "License"); +; you may not use this file except in compliance with the +; License. You may obtain a copy of the License at +; http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. +; +; Software distributed under the License is distributed AS IS, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. +; See the License for the specific language governing rights +; and limitations under the License. +; +; The Original Code was created by Adriano dos Santos Fernandes +; for the Firebird Open Source RDBMS project. +; +; Copyright (c) 2008 Adriano dos Santos Fernandes +; and all contributors signed below. +; +; All Rights Reserved. +; Contributor(s): ______________________________________. + +EXPORTS + firebirdPlugin + fbUdrRegFunction + fbUdrRegProcedure + fbUdrRegTrigger + fbUdrGetFunction diff --git a/builds/win32/make_all.bat b/builds/win32/make_all.bat index 674a2067c6..caee7b764c 100644 --- a/builds/win32/make_all.bat +++ b/builds/win32/make_all.bat @@ -56,6 +56,7 @@ for %%v in ( icuuc30 icudt30 icuin30 ) do ( @copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\firebird\udf\* %FB_OUTPUT_DIR%\udf >nul @copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\firebird\system32\* %FB_OUTPUT_DIR%\system32 >nul @copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\firebird\plugins\fbtrace.dll %FB_OUTPUT_DIR%\plugins\fbtrace.dll >nul +@copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\firebird\plugins\udr_engine.dll %FB_OUTPUT_DIR%\plugins\udr_engine.dll >nul @copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\fbclient\fbclient.lib %FB_OUTPUT_DIR%\lib\fbclient_ms.lib >nul @copy %FB_ROOT_PATH%\temp\%FB_OBJ_DIR%\ib_util\ib_util.lib %FB_OUTPUT_DIR%\lib\ib_util_ms.lib >nul diff --git a/builds/win32/msvc8/Firebird2.sln b/builds/win32/msvc8/Firebird2.sln index 22458d997e..ac5fdf8285 100644 --- a/builds/win32/msvc8/Firebird2.sln +++ b/builds/win32/msvc8/Firebird2.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +# Visual C++ Express 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "alice", "alice.vcproj", "{0D616380-1A5A-4230-A80B-021360E4E669}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "burp", "burp.vcproj", "{D1507562-A363-4685-96AF-B036F5E5E47F}" @@ -20,25 +20,25 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine_embed", "engine_embe EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fb_inet_server", "fb_inet_server.vcproj", "{664D4A04-36E0-48EF-8BCA-D5C331EFAA24}" ProjectSection(ProjectDependencies) = postProject - {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} - {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} - {F5746066-8613-4811-B27C-0ED70FF9F0FF} = {F5746066-8613-4811-B27C-0ED70FF9F0FF} - {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} - {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} - {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} - {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} = {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} - {3C4993E4-946C-4029-97B8-1A111F32F4FC} = {3C4993E4-946C-4029-97B8-1A111F32F4FC} {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} = {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} - {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} - {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} - {F5746066-8613-4811-B27C-0ED70FF9F0FF} = {F5746066-8613-4811-B27C-0ED70FF9F0FF} - {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} - {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} - {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} + {3C4993E4-946C-4029-97B8-1A111F32F4FC} = {3C4993E4-946C-4029-97B8-1A111F32F4FC} {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} = {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} + {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} = {E029E4E2-0C3F-4F7D-BC4C-D9C20F40EB4E} + {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} + {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} + {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} + {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} + {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} + {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} + {F5746066-8613-4811-B27C-0ED70FF9F0FF} = {F5746066-8613-4811-B27C-0ED70FF9F0FF} + {F5746066-8613-4811-B27C-0ED70FF9F0FF} = {F5746066-8613-4811-B27C-0ED70FF9F0FF} + {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} + {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} {53F75437-15B8-4A5C-86BF-E238CC68FCBC} = {53F75437-15B8-4A5C-86BF-E238CC68FCBC} + {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} + {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} + {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} + {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fb_lock_print", "fb_lock_print.vcproj", "{E8397148-0E9C-449B-9F45-7FB377A08242}" @@ -53,89 +53,83 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbclient", "fbclient.vcproj EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbembed", "fbembed.vcproj", "{C5A60E3D-7815-4127-B856-96277BEC1D11}" ProjectSection(ProjectDependencies) = postProject - {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} = {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} - {F55ACA54-70DF-4343-8E16-FA97C757CCF6} = {F55ACA54-70DF-4343-8E16-FA97C757CCF6} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} - {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} - {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} = {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} - {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} = {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} - {F55ACA54-70DF-4343-8E16-FA97C757CCF6} = {F55ACA54-70DF-4343-8E16-FA97C757CCF6} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} + {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} + {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} + {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} {F5746066-8613-4811-B27C-0ED70FF9F0FF} = {F5746066-8613-4811-B27C-0ED70FF9F0FF} - {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} = {488199DD-D9F3-41C8-AED6-8AFFFB294CFF} - {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} + {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} + {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} + {F55ACA54-70DF-4343-8E16-FA97C757CCF6} = {F55ACA54-70DF-4343-8E16-FA97C757CCF6} + {F55ACA54-70DF-4343-8E16-FA97C757CCF6} = {F55ACA54-70DF-4343-8E16-FA97C757CCF6} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} + {604E1144-1A22-43AF-9A3E-08650EE4EE90} = {604E1144-1A22-43AF-9A3E-08650EE4EE90} {53F75437-15B8-4A5C-86BF-E238CC68FCBC} = {53F75437-15B8-4A5C-86BF-E238CC68FCBC} + {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} = {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} + {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} = {B0833E27-BCB2-4D0D-B6D2-F4621224CFD8} + {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} = {213C6F21-D83F-48C7-BBB5-B35AB1B706B1} + {520DF501-5775-44FD-BDC6-37753A17696A} = {520DF501-5775-44FD-BDC6-37753A17696A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbguard", "fbguard.vcproj", "{BBD83ED3-8A48-4FE8-B4B7-CB27730986B2}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbserver", "fbserver.vcproj", "{23EC8DAA-6718-4EF3-979F-89F611C7D504}" ProjectSection(ProjectDependencies) = postProject - {520DF501-5775-44FD-BDC6-37753A17696A} = {520DF501-5775-44FD-BDC6-37753A17696A} - {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} - {4BCC693D-1745-45ED-8302-E5E2F979549A} = {4BCC693D-1745-45ED-8302-E5E2F979549A} - {604E1144-1A22-43AF-9A3E-08650EE4EE90} = {604E1144-1A22-43AF-9A3E-08650EE4EE90} - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} - {F8798A49-9D20-451E-A7BD-FEB5237103B5} = {F8798A49-9D20-451E-A7BD-FEB5237103B5} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} - {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} - {4BCC693D-1745-45ED-8302-E5E2F979549A} = {4BCC693D-1745-45ED-8302-E5E2F979549A} - {604E1144-1A22-43AF-9A3E-08650EE4EE90} = {604E1144-1A22-43AF-9A3E-08650EE4EE90} - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} - {F8798A49-9D20-451E-A7BD-FEB5237103B5} = {F8798A49-9D20-451E-A7BD-FEB5237103B5} - {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} {0D616380-1A5A-4230-A80B-021360E4E669} = {0D616380-1A5A-4230-A80B-021360E4E669} - {E83187C1-AAC2-445D-B8B2-883EFC10C39A} = {E83187C1-AAC2-445D-B8B2-883EFC10C39A} - {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} = {EABA0FF3-1C4D-4FAB-8418-31C9061F3F0D} - {520DF501-5775-44FD-BDC6-37753A17696A} = {520DF501-5775-44FD-BDC6-37753A17696A} + {D1507562-A363-4685-96AF-B036F5E5E47F} = {D1507562-A363-4685-96AF-B036F5E5E47F} + {F8798A49-9D20-451E-A7BD-FEB5237103B5} = {F8798A49-9D20-451E-A7BD-FEB5237103B5} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} + {604E1144-1A22-43AF-9A3E-08650EE4EE90} = {604E1144-1A22-43AF-9A3E-08650EE4EE90} + {4BCC693D-1745-45ED-8302-E5E2F979549A} = {4BCC693D-1745-45ED-8302-E5E2F979549A} {53F75437-15B8-4A5C-86BF-E238CC68FCBC} = {53F75437-15B8-4A5C-86BF-E238CC68FCBC} + {E9AAC310-465E-4384-8BCC-674F297F777C} = {E9AAC310-465E-4384-8BCC-674F297F777C} + {520DF501-5775-44FD-BDC6-37753A17696A} = {520DF501-5775-44FD-BDC6-37753A17696A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbudf", "fbudf.vcproj", "{9DC67B05-AC3E-49A3-B0CC-83B25D757445}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gbak", "gbak.vcproj", "{B732F5D2-B5D9-417F-B156-D790F466CB8E}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gdef", "gdef.vcproj", "{E8B8E0CE-F47F-48BD-8911-C11805A711D9}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfix", "gfix.vcproj", "{44A9E4AD-B932-4620-B319-431A153BB341}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpre", "gpre.vcproj", "{D84F0839-28A4-40B2-B5F4-F5E1E7F48FD0}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsec", "gsec.vcproj", "{7043CC61-DEC1-4C6B-86B9-0E911D1094C9}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsplit", "gsplit.vcproj", "{B7F22B7F-9937-4874-9A8B-6AB4E36E74A5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gstat", "gstat.vcproj", "{7E862973-37C4-4202-80E7-490ED4DEDA14}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ib_udf", "ib_udf.vcproj", "{0D4A2D8E-6461-479E-9399-F7929174E050}" @@ -159,8 +153,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "intl", "intl.vcproj", "{DFF EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "isql", "isql.vcproj", "{DEE75AD5-F165-40E1-80B2-400E27725D5C}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lock", "lock.vcproj", "{604E1144-1A22-43AF-9A3E-08650EE4EE90}" @@ -169,8 +163,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lock_classic", "lock_classi EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qli", "qli.vcproj", "{EBB8361B-49D5-43A5-8771-940DF3E308EF}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "remote", "remote.vcproj", "{4BCC693D-1745-45ED-8302-E5E2F979549A}" @@ -181,8 +175,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilities", "utilities.vcpr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nbackup", "nbackup.vcproj", "{01A41DFA-8908-4576-A1F1-C8BC7EAE39A1}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "config", "config.vcproj", "{E83187C1-AAC2-445D-B8B2-883EFC10C39A}" @@ -200,8 +194,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbrmclib", "fbrmclib.vcproj EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbsvcmgr", "fbsvcmgr.vcproj", "{EFB07DBC-36E3-4C54-B941-3CDAFAACF47B}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "udr_engine", "udr_engine.vcproj", "{20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}" + ProjectSection(ProjectDependencies) = postProject + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbtrace", "fbtrace.vcproj", "{53F75437-15B8-4A5C-86BF-E238CC68FCBC}" @@ -212,8 +211,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbtrace", "fbtrace.vcproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbtracemgr", "fbtracemgr.vcproj", "{58C7E370-0EDD-4F5E-8617-3F5071170205}" ProjectSection(ProjectDependencies) = postProject - {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} = {492E7BDA-8948-408D-A43E-4C0A5B86AFB8} + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} EndProjectSection EndProject Global @@ -558,6 +557,12 @@ Global {EFB07DBC-36E3-4C54-B941-3CDAFAACF47B}.Release|Win32.Build.0 = Release|Win32 {EFB07DBC-36E3-4C54-B941-3CDAFAACF47B}.Release|x64.ActiveCfg = Release|x64 {EFB07DBC-36E3-4C54-B941-3CDAFAACF47B}.Release|x64.Build.0 = Release|x64 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|Win32.ActiveCfg = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|Win32.Build.0 = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|x64.ActiveCfg = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|Win32.ActiveCfg = Release|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|Win32.Build.0 = Release|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|x64.ActiveCfg = Release|Win32 {53F75437-15B8-4A5C-86BF-E238CC68FCBC}.Debug|Win32.ActiveCfg = Debug|Win32 {53F75437-15B8-4A5C-86BF-E238CC68FCBC}.Debug|Win32.Build.0 = Debug|Win32 {53F75437-15B8-4A5C-86BF-E238CC68FCBC}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/builds/win32/msvc8/Firebird2_Examples.sln b/builds/win32/msvc8/Firebird2_Examples.sln index 750fa9b7b6..9809f23627 100644 --- a/builds/win32/msvc8/Firebird2_Examples.sln +++ b/builds/win32/msvc8/Firebird2_Examples.sln @@ -1,9 +1,21 @@ Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +# Visual C++ Express 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "empbuild", "empbuild.vcproj", "{FC2859B9-56DB-40B4-86C4-2DE31ECE9144}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "intlbuild", "intlbuild.vcproj", "{9546EF04-1326-464B-A6ED-395C60DD63CC}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "udrcpp_example", "udrcpp_example.vcproj", "{FF0FD8DF-1E5C-486E-B395-A620376A4633}" + ProjectSection(ProjectDependencies) = postProject + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B} = {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "udr_engine", "udr_engine.vcproj", "{20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}" + ProjectSection(ProjectDependencies) = postProject + {15605F44-BFFD-444F-AD4C-55DC9D704465} = {15605F44-BFFD-444F-AD4C-55DC9D704465} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common.vcproj", "{15605F44-BFFD-444F-AD4C-55DC9D704465}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -21,9 +33,29 @@ Global {FC2859B9-56DB-40B4-86C4-2DE31ECE9144}.Release|x64.ActiveCfg = Release|x64 {FC2859B9-56DB-40B4-86C4-2DE31ECE9144}.Release|x64.Build.0 = Release|x64 {9546EF04-1326-464B-A6ED-395C60DD63CC}.Debug|Win32.ActiveCfg = Debug|Win32 + {9546EF04-1326-464B-A6ED-395C60DD63CC}.Debug|Win32.Build.0 = Debug|Win32 {9546EF04-1326-464B-A6ED-395C60DD63CC}.Debug|x64.ActiveCfg = Debug|x64 + {9546EF04-1326-464B-A6ED-395C60DD63CC}.Debug|x64.Build.0 = Debug|x64 {9546EF04-1326-464B-A6ED-395C60DD63CC}.Release|Win32.ActiveCfg = Release|Win32 {9546EF04-1326-464B-A6ED-395C60DD63CC}.Release|x64.ActiveCfg = Release|x64 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Debug|Win32.ActiveCfg = Debug|Win32 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Debug|Win32.Build.0 = Debug|Win32 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Debug|x64.ActiveCfg = Debug|Win32 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Release|Win32.ActiveCfg = Release|Win32 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Release|Win32.Build.0 = Release|Win32 + {FF0FD8DF-1E5C-486E-B395-A620376A4633}.Release|x64.ActiveCfg = Release|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|Win32.ActiveCfg = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|Win32.Build.0 = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Debug|x64.ActiveCfg = Debug|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|Win32.ActiveCfg = Release|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|Win32.Build.0 = Release|Win32 + {20DEBF08-EF0A-4C94-ADEB-FE9BBA14588B}.Release|x64.ActiveCfg = Release|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Debug|Win32.ActiveCfg = Debug|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Debug|Win32.Build.0 = Debug|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Debug|x64.ActiveCfg = Debug|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Release|Win32.ActiveCfg = Release|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Release|Win32.Build.0 = Release|Win32 + {15605F44-BFFD-444F-AD4C-55DC9D704465}.Release|x64.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/builds/win32/msvc8/dsql_server.vcproj b/builds/win32/msvc8/dsql_server.vcproj index 033b6c1da9..9cb8b8cfc6 100644 --- a/builds/win32/msvc8/dsql_server.vcproj +++ b/builds/win32/msvc8/dsql_server.vcproj @@ -345,6 +345,10 @@ RelativePath="..\..\..\src\dsql\DdlNodes.epp" > + + @@ -365,6 +369,10 @@ RelativePath="..\..\..\gen\$(PlatformName)\dsql\DdlNodes.cpp" > + + @@ -404,6 +412,10 @@ RelativePath="..\..\..\src\dsql\DdlNodes.h" > + + diff --git a/builds/win32/msvc8/dsql_server_classic.vcproj b/builds/win32/msvc8/dsql_server_classic.vcproj index e8484bcfd1..646b1e6459 100644 --- a/builds/win32/msvc8/dsql_server_classic.vcproj +++ b/builds/win32/msvc8/dsql_server_classic.vcproj @@ -345,6 +345,10 @@ RelativePath="..\..\..\src\dsql\DdlNodes.epp" > + + @@ -365,6 +369,10 @@ RelativePath="..\..\..\gen\$(PlatformName)\dsql\DdlNodes.cpp" > + + @@ -404,6 +412,10 @@ RelativePath="..\..\..\src\dsql\DdlNodes.h" > + + diff --git a/builds/win32/msvc8/engine.vcproj b/builds/win32/msvc8/engine.vcproj index 244fa0b7d6..ab4750423f 100644 --- a/builds/win32/msvc8/engine.vcproj +++ b/builds/win32/msvc8/engine.vcproj @@ -379,6 +379,10 @@ RelativePath="..\..\..\src\jrd\err.cpp" > + + @@ -403,6 +407,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp" > + + @@ -415,10 +423,6 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\fun.cpp" > - - @@ -543,6 +547,10 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\pcmet.cpp" > + + @@ -683,6 +691,14 @@ RelativePath="..\..\..\src\jrd\validation.cpp" > + + + + @@ -695,6 +711,10 @@ RelativePath="..\..\..\src\jrd\why.cpp" > + + @@ -899,6 +919,10 @@ RelativePath="..\..\..\src\jrd\err_proto.h" > + + @@ -935,6 +959,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.h" > + + @@ -1443,6 +1471,14 @@ RelativePath="..\..\..\src\jrd\val_proto.h" > + + + + @@ -1459,6 +1495,10 @@ RelativePath="..\..\..\src\jrd\why_proto.h" > + + + + @@ -402,6 +406,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp" > + + @@ -414,10 +422,6 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\fun.cpp" > - - @@ -542,6 +546,10 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\pcmet.cpp" > + + @@ -682,6 +690,14 @@ RelativePath="..\..\..\src\jrd\validation.cpp" > + + + + @@ -694,6 +710,10 @@ RelativePath="..\..\..\src\jrd\why.cpp" > + + @@ -898,6 +918,10 @@ RelativePath="..\..\..\src\jrd\err_proto.h" > + + @@ -934,6 +958,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.h" > + + @@ -1438,6 +1466,14 @@ RelativePath="..\..\..\src\jrd\val_proto.h" > + + + + @@ -1454,6 +1490,10 @@ RelativePath="..\..\..\src\jrd\why_proto.h" > + + + + @@ -402,6 +406,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.cpp" > + + @@ -414,10 +422,6 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\fun.cpp" > - - @@ -542,6 +546,10 @@ RelativePath="..\..\..\gen\$(PlatformName)\jrd\pcmet.cpp" > + + @@ -682,6 +690,14 @@ RelativePath="..\..\..\src\jrd\validation.cpp" > + + + + @@ -694,6 +710,10 @@ RelativePath="..\..\..\src\jrd\why.cpp" > + + @@ -898,6 +918,10 @@ RelativePath="..\..\..\src\jrd\err_proto.h" > + + @@ -934,6 +958,10 @@ RelativePath="..\..\..\src\jrd\extds\ExtDS.h" > + + @@ -1438,6 +1466,14 @@ RelativePath="..\..\..\src\jrd\val_proto.h" > + + + + @@ -1454,6 +1490,10 @@ RelativePath="..\..\..\src\jrd\why_proto.h" > + + - - - - - - - - - - - - - - - - - - - - @@ -225,6 +149,7 @@ OutputFile="..\..\..\temp\$(PlatformName)\$(ConfigurationName)\firebird\bin\$(ProjectName).exe" LinkIncremental="1" AdditionalLibraryDirectories="../../../extern/icu/$(PlatformName)/$(ConfigurationName)/lib" + ModuleDefinitionFile="..\defs\fbclient.def" SubSystem="2" StackReserveSize="4194304" TargetMachine="17" @@ -254,6 +179,84 @@ Name="VCPostBuildEventTool" /> + + + + + + + + + + + + + + + + + + + + + + @@ -389,7 +397,7 @@ /> + + diff --git a/builds/win32/msvc8/fbserver.vcproj b/builds/win32/msvc8/fbserver.vcproj index d095a81fbb..eb77241c50 100644 --- a/builds/win32/msvc8/fbserver.vcproj +++ b/builds/win32/msvc8/fbserver.vcproj @@ -68,6 +68,7 @@ OutputFile="..\..\..\temp\$(PlatformName)\$(ConfigurationName)\firebird\bin\$(ProjectName).exe" LinkIncremental="1" AdditionalLibraryDirectories="../../../extern/icu/$(PlatformName)/$(ConfigurationName)/lib" + ModuleDefinitionFile="..\defs\fbclient.def" SubSystem="2" StackReserveSize="2097152" /> @@ -145,6 +146,7 @@ OutputFile="..\..\..\temp\$(PlatformName)\$(ConfigurationName)\firebird\bin\$(ProjectName).exe" LinkIncremental="1" AdditionalLibraryDirectories="../../../extern/icu/$(PlatformName)/release/lib" + ModuleDefinitionFile="..\defs\fbclient.def" SubSystem="2" StackReserveSize="2097152" /> @@ -225,6 +227,7 @@ OutputFile="..\..\..\temp\$(PlatformName)\$(ConfigurationName)\firebird\bin\$(ProjectName).exe" LinkIncremental="1" AdditionalLibraryDirectories="../../../extern/icu/$(PlatformName)/$(ConfigurationName)/lib" + ModuleDefinitionFile="..\defs\fbclient.def" SubSystem="2" StackReserveSize="4194304" TargetMachine="17" @@ -304,6 +307,7 @@ OutputFile="..\..\..\temp\$(PlatformName)\$(ConfigurationName)\firebird\bin\$(ProjectName).exe" LinkIncremental="1" AdditionalLibraryDirectories="../../../extern/icu/$(PlatformName)/release/lib" + ModuleDefinitionFile="..\defs\fbclient.def" SubSystem="2" StackReserveSize="4194304" TargetMachine="17" @@ -352,6 +356,10 @@ RelativePath="..\..\..\src\utilities\nbackup\nbkMain.cpp" > + + @@ -414,6 +422,10 @@ + + diff --git a/builds/win32/msvc8/udr_engine.vcproj b/builds/win32/msvc8/udr_engine.vcproj new file mode 100644 index 0000000000..289565c376 --- /dev/null +++ b/builds/win32/msvc8/udr_engine.vcproj @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builds/win32/msvc8/udrcpp_example.vcproj b/builds/win32/msvc8/udrcpp_example.vcproj new file mode 100644 index 0000000000..c20c1a3918 --- /dev/null +++ b/builds/win32/msvc8/udrcpp_example.vcproj @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builds/win32/preprocess.bat b/builds/win32/preprocess.bat index b03def20d2..1e92522f37 100644 --- a/builds/win32/preprocess.bat +++ b/builds/win32/preprocess.bat @@ -55,7 +55,7 @@ goto :EOF @echo. @set GPRE=%FB_GEN_DIR%\gpre_boot -lang_internal @for %%i in (array, blob) do @call :PREPROCESS dsql %%i -@for %%i in (metd, DdlNodes) do @call :PREPROCESS dsql %%i -gds_cxx +@for %%i in (metd, DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (gpre_meta) do @call :PREPROCESS gpre %%i @for %%i in (backup, restore) do @call :PREPROCESS burp %%i @for %%i in (extract, isql, show) do @call :PREPROCESS isql %%i @@ -64,7 +64,7 @@ goto :EOF @set GPRE=%FB_GEN_DIR%\gpre_boot @for %%i in (alice_meta) do @call :PREPROCESS alice %%i @for %%i in (array, blob) do @call :PREPROCESS dsql %%i -@for %%i in (metd, DdlNodes) do @call :PREPROCESS dsql %%i -gds_cxx +@for %%i in (metd, DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (gpre_meta) do @call :PREPROCESS gpre %%i @for %%i in (dfw, dpm, dyn, dyn_def, dyn_del, dyn_mod, dyn_util, fun, grant, ini, met, pcmet, scl) do @call :PREPROCESS jrd %%i -gds_cxx @for %%i in (stats) do @call :PREPROCESS utilities %%i @@ -77,7 +77,7 @@ goto :EOF @for %%i in (backup, restore) do @call :PREPROCESS burp %%i @for %%i in (array, blob) do @call :PREPROCESS dsql %%i @for %%i in (metd) do @call :PREPROCESS dsql %%i -gds_cxx -cxx -@for %%i in (DdlNodes) do @call :PREPROCESS dsql %%i -gds_cxx +@for %%i in (DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (exe, extract) do @call :PREPROCESS dudley %%i @for %%i in (gpre_meta) do @call :PREPROCESS gpre %%i @for %%i in (dfw, dpm, dyn, dyn_def, dyn_del, dyn_mod, dyn_util, fun, grant, ini, met, pcmet, scl) do @call :PREPROCESS jrd %%i -gds_cxx diff --git a/configure.in b/configure.in index a9d049127e..67281ede0b 100644 --- a/configure.in +++ b/configure.in @@ -978,6 +978,7 @@ mkdir -p gen/jrd mkdir -p gen/jrd/extds mkdir -p gen/jrd/trace mkdir -p gen/msgs +mkdir -p gen/plugins/udr_engine mkdir -p gen/qli mkdir -p gen/utilities mkdir -p gen/examples @@ -999,11 +1000,13 @@ dnl # mkdir -p gen/firebird/examples/build_unix dnl # mkdir -p gen/firebird/examples/build_win32 mkdir -p gen/firebird/examples/empbuild mkdir -p gen/firebird/examples/include +mkdir -p gen/firebird/examples/udr mkdir -p gen/firebird/examples/stat mkdir -p gen/firebird/examples/udf mkdir -p gen/firebird/lib mkdir -p gen/firebird/misc mkdir -p gen/firebird/help +mkdir -p gen/firebird/plugins/udr dnl # rebuild version header if needed ./src/misc/writeBuildNum.sh rebuildHeader @@ -1035,6 +1038,7 @@ mkdir -p temp/boot/jrd/os/darwin mkdir -p temp/boot/lock mkdir -p temp/boot/misc mkdir -p temp/boot/msgs +# mkdir -p temp/boot/plugins/udr_engine mkdir -p temp/boot/qli mkdir -p temp/boot/remote mkdir -p temp/boot/remote/os/win32 @@ -1077,6 +1081,7 @@ mkdir -p temp/std/jrd/os/darwin mkdir -p temp/std/lock mkdir -p temp/std/misc mkdir -p temp/std/msgs +mkdir -p temp/std/plugins/udr_engine mkdir -p temp/std/qli mkdir -p temp/std/remote mkdir -p temp/std/remote/os/win32 @@ -1118,6 +1123,7 @@ mkdir -p temp/superclient/jrd/os/darwin mkdir -p temp/superclient/lock mkdir -p temp/superclient/misc mkdir -p temp/superclient/msgs +mkdir -p temp/superclient/plugins/udr_engine mkdir -p temp/superclient/qli mkdir -p temp/superclient/remote mkdir -p temp/superclient/remote/os/win32 @@ -1159,6 +1165,7 @@ mkdir -p temp/superserver/jrd/os/darwin mkdir -p temp/superserver/lock mkdir -p temp/superserver/misc mkdir -p temp/superserver/msgs +mkdir -p temp/superserver/plugins/udr_engine mkdir -p temp/superserver/qli mkdir -p temp/superserver/remote mkdir -p temp/superserver/remote/os/win32 @@ -1176,6 +1183,8 @@ mkdir -p temp/superserver/utilities/rebuild mkdir -p temp/superserver/utilities/ntrace mkdir -p temp/superserver/vulcan +mkdir -p temp/examples/udr + ]) @@ -1349,7 +1358,9 @@ gen/Makefile.client.gsec:${MAKE_SRC_DIR}/Makefile.in.client.gsec gen/Makefile.intl:${MAKE_SRC_DIR}/Makefile.in.intl gen/Makefile.msgs:${MAKE_SRC_DIR}/Makefile.in.msgs gen/Makefile.extlib:${MAKE_SRC_DIR}/Makefile.in.extlib +gen/Makefile.plugins:${MAKE_SRC_DIR}/Makefile.in.plugins gen/examples/Makefile.examples:${MAKE_SRC_DIR}/Makefile.in.examples +gen/examples/Makefile.plugins_examples:${MAKE_SRC_DIR}/Makefile.in.plugins_examples gen/Makefile.libfbembed:${MAKE_SRC_DIR}/Makefile.in.libfbembed gen/Makefile.inet_server:${MAKE_SRC_DIR}/Makefile.in.inet_server gen/Makefile.embed.util:${MAKE_SRC_DIR}/Makefile.in.embed.util diff --git a/doc/README.external_routines.txt b/doc/README.external_routines.txt new file mode 100644 index 0000000000..9f8b3fb58f --- /dev/null +++ b/doc/README.external_routines.txt @@ -0,0 +1,107 @@ +----------------- +External Routines +----------------- + +Author: + Adriano dos Santos Fernandes + +Syntax: + +{ CREATE [ OR ALTER ] | RECREATE | ALTER } PROCEDURE + [ ( ) ] + [ RETURNS ( ) ] + EXTERNAL NAME '' ENGINE + +{ CREATE [ OR ALTER ] | RECREATE | ALTER } FUNCTION + [ ] + RETURNS + EXTERNAL NAME '' ENGINE + +{ CREATE [ OR ALTER ] | RECREATE | ALTER } TRIGGER + ... + EXTERNAL NAME '' ENGINE + +Examples: + +create procedure gen_rows ( + start_n integer not null, + end_n integer not null +) returns ( + n integer not null +) external name 'udrcpp_example!gen_rows' + engine udr; + +create function wait_event ( + event_name varchar(31) character set ascii +) returns integer + external name 'udrcpp_example!wait_event' + engine udr; + +create trigger persons_replicate + after insert on persons + external name 'udrcpp_example!replicate!ds1' + engine udr; + +How it works: + +External names are opaque strings to Firebird. They are recognized by specific external engines. +External engines are declared in config files (possibly in the same file as a plugin, like in the +config example present below). + + + plugin_module UDR_engine + + + + filename $(this)/udr_engine + plugin_config UDR_config + + + + path $(this)/udr + + +When Firebird wants to load an external routine (function, procedure or trigger) into its metadata +cache, it gets (if not already done for the database*) the external engine through the plugin +external engine factory and ask it for the routine. The plugin used is the one referenced by the +attribute plugin_module of the external engine. + +* This is in Super-Server. In [Super-]Classic, different attachments to one database creates +multiple metadata caches and hence multiple external engine instances. + + +---------------------------------- +UDR - User Defined Routines engine +---------------------------------- + +The UDR (User Defined Routines) engine adds a layer on top of the FirebirdExternal interface with +these objectives: + - Establish a way to place external modules into server and make them available for usage; + - Create an API so that external modules can register their available routines; + - Make routines instances per-attachment, instead of per-database like the FirebirdExternal does + in SuperServer mode. + +External names of the UDR engine are defined as following: + '!!' + +The is used to locate the library, is used to locate the routine +registered by the given module, and is an user defined string passed to the routine +and can be read by the user. "!" may be ommitted. + +Modules available to the UDR engine should be in a directory listed through the path attribute of +the correspondent plugin_config tag. By default, UDR modules should be on /plugins/udr, +accordingly to its path attribute in /plugins/udr_engine.conf. + +The user library should include FirebirdUdr.h (or FirebirdUdrCpp.h) and link with the udr_engine +library. Routines are easily defined and registered using some macros, but nothing prevent you of +doing things manually. A example routine library is implemented in examples/plugins, showing how to +write functions, selectable procedures and triggers. Also it shows how to interact with the current +database through the ISC API. + +The UDR routines state (i.e., member variables) are shared between multiple invocations of the same +routine until it's unloaded from the metadata cache. But note that it isolates the instances per +session, different from the raw interface that shares instances by multiple sessions in SuperServer. + +By default, UDR routines uses the same character set specified by the client. They can modify it +overriding the getCharSet method. The chosen character set is valid for communication with the ISC +library as well as the communications done through the FirebirdExternal API. diff --git a/doc/sql.extensions/README.alternate_string_quoting.txt b/doc/sql.extensions/README.alternate_string_quoting.txt new file mode 100644 index 0000000000..4857b649b1 --- /dev/null +++ b/doc/sql.extensions/README.alternate_string_quoting.txt @@ -0,0 +1,25 @@ +------------------------ +Alternate String Quoting +------------------------ + +Support for alternate format of strings literals. + +Author: + Adriano dos Santos Fernandes + +Syntax: + + ::= + { q | Q } [ { }... ] + +Syntax rules: + When is '(', '{', '[' or '<', is respectively + ')', '}', ']' and '>'. In other cases, is equal to . + +Notes: + Inside the string, i.e., items, single (not escaped) quotes could be used. + Each quote will be part of the result string. + +Examples: + select q'{abc{def}ghi}' from rdb$database; -- result: abc{def}ghi + select q'!That's a string!' from rdb$database; -- result: That's a string diff --git a/doc/sql.extensions/README.ddl_triggers.txt b/doc/sql.extensions/README.ddl_triggers.txt new file mode 100644 index 0000000000..e692c3f9c3 --- /dev/null +++ b/doc/sql.extensions/README.ddl_triggers.txt @@ -0,0 +1,306 @@ +------------ +DDL triggers +------------ + +Author: + Adriano dos Santos Fernandes + (This feature was sponsored with donations gathered in the "5th Brazilian Firebird Developers Day") + +Syntax: + ::= + {CREATE | RECREATE | CREATE OR ALTER} + TRIGGER + [ACTIVE | INACTIVE] + {BEFORE | AFTER} + [POSITION ] + AS + BEGIN + ... + END + + ::= + ANY DDL STATEMENT + | [{OR }...] + + ::= + CREATE TABLE + | ALTER TABLE + | DROP TABLE + | CREATE PROCEDURE + | ALTER PROCEDURE + | DROP PROCEDURE + | CREATE FUNCTION + | ALTER FUNCTION + | DROP FUNCTION + | CREATE TRIGGER + | ALTER TRIGGER + | DROP TRIGGER + | CREATE EXCEPTION + | ALTER EXCEPTION + | DROP EXCEPTION + | CREATE VIEW + | ALTER VIEW + | DROP VIEW + | CREATE DOMAIN + | ALTER DOMAIN + | DROP DOMAIN + | CREATE ROLE + | ALTER ROLE + | DROP ROLE + | CREATE SEQUENCE + | ALTER SEQUENCE + | DROP SEQUENCE + | CREATE USER + | ALTER USER + | DROP USER + | CREATE INDEX + | ALTER INDEX + | DROP INDEX + | CREATE COLLATION + | DROP COLLATION + | ALTER CHARACTER SET + | CREATE PACKAGE + | ALTER PACKAGE + | DROP PACKAGE + | CREATE PACKAGE BODY + | DROP PACKAGE BODY + + +Syntax rules: + 1) DDL triggers type can't be changed. + +Semantics: + 1) BEFORE triggers are fired before changes to system tables and AFTER triggers are fired after + system table changes. + 2) It's possible to cancel the command raising an exception in BEFORE or in AFTER triggers. + 3) Firebird really does DDL actions only when committing the transaction that run DDL commands. + You should pay attention to that. What you can do in AFTER triggers are exactly what you can + do after a DDL command without autocommit. For example, you can't create a table and use it + on the trigger. + 4) With CREATE OR ALTER statements, trigger is fired one time using the CREATE or ALTER event + based on the previous existence of the object. With RECREATE statements, trigger is fired for + DROP (when the object exists) and for CREATE events. + 5) ALTER and DROP events are generally not fired when the object name does not exist. + 6) As exception to rule 5, BEFORE ALTER/DROP USER triggers fires even when the user name does + not exist. This is because these commands run on the security database and the verification + is not done before run the command on it. This is likely to be different with embedded users, + so do not write your code depending on this. + 7) If some exception is raised after the DDL command starts its execution and before AFTER + triggers are fired, AFTER triggers will not be fired. + 8) Packaged procedures and triggers do not fire individual {CREATE | ALTER | DROP} {PROCEDURE | + FUNCTION} triggers. + +Utilities support: + DDL triggers is a type of database triggers, so the parameters -nodbtriggers (GBAK and ISQL) + and -T (NBACKUP) also works for them. + These parameters could only be used by database owner and SYSDBA. + +Permissions: + Only database owner and SYSDBA can create/alter/drop DDL triggers. + +DDL_TRIGGER context namespace: + It has been introduced the DDL_TRIGGER context for usage with RDB$GET_CONTEXT. Usage of this + namespace is valid only when DDL triggers are running. It's valid to use it in stored + procedures called by DDL triggers. + The DDL_TRIGGER context works like a stack. Before a DDL trigger is fired, it's pushed on that + stack the values relative to the executed command. After the trigger finishes, the value is + poped. So in the case of cascade DDL statements, when an user DDL command fires a DDL trigger + and this trigger executes another DDL command with EXECUTE STATEMENT, the values of DDL_TRIGGER + namespace are the ones relative to the command that fired the last DDL trigger on the call + stack. + + The context elements are: + - DDL_EVENT: event name () + - OBJECT_NAME: metadata object name + - SQL_TEXT: sql statement text + + +Example usages: +=============== + + +1) Enforce a name consistense scheme. All procedure names should start with the prefix "SP_". + +create exception e_invalid_sp_name 'Invalid SP name (should start with SP_)'; + +set term !; + +create trigger trig_ddl_sp before create procedure +as +begin + if (rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME') not starting 'SP_') then + exception e_invalid_sp_name; +end! + +-- Test + +create procedure sp_test +as +begin +end! + +create procedure test +as +begin +end! + +-- The last command raises this exception and procedure TEST is not created +-- Statement failed, SQLSTATE = 42000 +-- exception 1 +-- -E_INVALID_SP_NAME +-- -Invalid SP name (should start with SP_) +-- -At trigger 'TRIG_DDL_SP' line: 4, col: 5 + +set term ;! + + +---------------------------------------- + + +2) Implement handy-made DDL security. Only certainly users could run DDL commands. + +create exception e_access_denied 'Access denied'; + +set term !; + +create trigger trig_ddl before any ddl statement +as +begin + if (current_user <> 'SUPER_USER') then + exception e_access_denied; +end! + +-- Test + +create procedure sp_test +as +begin +end! + +-- The last command raises this exception and procedure SP_TEST is not created +-- Statement failed, SQLSTATE = 42000 +-- exception 1 +-- -E_ACCESS_DENIED +-- -Access denied +-- -At trigger 'TRIG_DDL' line: 4, col: 5 + +set term ;! + + +---------------------------------------- + + +3) Log DDL actions and attempts. + +create sequence ddl_seq; + +create table ddl_log ( + id bigint not null primary key, + moment timestamp not null, + user_name varchar(31) not null, + ddl_event varchar(25) not null, + object_name varchar(31) not null, + sql_text blob sub_type text not null, + ok char(1) not null +); + +set term !; + +create trigger trig_ddl_log_before before any ddl statement +as + declare id type of column ddl_log.id; +begin + -- We do the changes in an AUTONOMOUS TRANSACTION, so if an exception happens and the command + -- didn't run, the log will survive. + in autonomous transaction do + begin + insert into ddl_log (id, moment, user_name, ddl_event, object_name, sql_text, ok) + values (next value for ddl_seq, current_timestamp, current_user, + rdb$get_context('DDL_TRIGGER', 'DDL_EVENT'), + rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME'), + rdb$get_context('DDL_TRIGGER', 'SQL_TEXT'), + 'N') + returning id into id; + rdb$set_context('USER_SESSION', 'trig_ddl_log_id', id); + end +end! + +-- Note: the above trigger will fire for this DDL command. It's good idea to use -nodbtriggers +-- when working with them! +create trigger trig_ddl_log_after after any ddl statement +as +begin + -- Here we need an AUTONOMOUS TRANSACTION because the original transaction will not see the + -- record inserted on the BEFORE trigger autonomous transaction if user transaction is not + -- READ COMMITTED. + in autonomous transaction do + update ddl_log set ok = 'Y' where id = rdb$get_context('USER_SESSION', 'trig_ddl_log_id'); +end! + +commit! + +set term ;! + +-- So lets delete the record about trig_ddl_log_after creation. +delete from ddl_log; +commit; + +-- Test + +-- This will be logged one time (as T1 did not exists, RECREATE acts as CREATE) with OK = Y. +recreate table t1 ( + n1 integer, + n2 integer +); + +-- This will fail as T1 already exists, so OK will be N. +create table t1 ( + n1 integer, + n2 integer +); + +-- T2 does not exists. There will be no log. +drop table t2; + +-- This will be logged two times (as T1 exists, RECREATE acts as DROP and CREATE) with OK = Y. +recreate table t1 ( + n integer +); + +commit; + +select id, ddl_event, object_name, sql_text, ok from ddl_log order by id; + + ID DDL_EVENT OBJECT_NAME SQL_TEXT OK +===================== ========================= =============================== ================= ====== + 2 CREATE TABLE T1 80:3 Y +============================================================================== +SQL_TEXT: +recreate table t1 ( + n1 integer, + n2 integer +) +============================================================================== + 3 CREATE TABLE T1 80:2 N +============================================================================== +SQL_TEXT: +create table t1 ( + n1 integer, + n2 integer +) +============================================================================== + 4 DROP TABLE T1 80:6 Y +============================================================================== +SQL_TEXT: +recreate table t1 ( + n integer +) +============================================================================== + 5 CREATE TABLE T1 80:9 Y +============================================================================== +SQL_TEXT: +recreate table t1 ( + n integer +) +============================================================================== + diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt new file mode 100644 index 0000000000..a90f028610 --- /dev/null +++ b/doc/sql.extensions/README.packages.txt @@ -0,0 +1,125 @@ +-------- +Packages +-------- + +Author: + Adriano dos Santos Fernandes + (This feature was sponsored with donations gathered in the "5th Brazilian Firebird Developers Day") + +Description: + A package is a group of procedures and functions managed as one entity. + +Syntax: + ::= + { CREATE [OR ALTER] | ALTER | RECREATE } PACKAGE + AS + BEGIN + [ ... ] + END + + ::= + ; | + ; + + ::= + FUNCTION [( )] RETURNS + + ::= + PROCEDURE [( ) [RETURNS ( )]] + + ::= + { CREATE | RECREATE } PACKAGE BODY + AS + BEGIN + [ ... ] + [ ... ] + END + + ::= + ; | + ; + + ::= + FUNCTION [( )] RETURNS + EXTERNAL NAME '' ENGINE + + ::= + PROCEDURE [( ) [RETURNS ( )]] + AS + BEGIN + ... + END + | + PROCEDURE [( ) [RETURNS ( )]] + EXTERNAL NAME '' ENGINE + + ::= + DROP PACKAGE + + ::= + DROP PACKAGE BODY + +Objectives: + - Make functional dependent code separated in logical modules like programming languages does. + + It's well know in programming world that having code grouped in some way (for example in + namespaces, units or classes) is a good thing. With standard procedures and functions in the + database this is not possible. It's possible to group them in different scripts files, but + two problems remains: + 1) The grouping is not represented in the database metadata. + 2) They all participate in a flat namespace and all routines are callable by everyone (not + talking about security permissions here). + + - Facilitate dependency tracking between its internal routines and between others packaged and + unpackaged routines. + + Firebird packages are divided in two pieces: a header (aka PACKAGE) and a body (aka + PACKAGE BODY). This division is very similar to a Delphi unit. The header correspond to the + interface part, and the body correspond to the implementation part. + + The user needs first to create the header (CREATE PACKAGE) and after it the body (CREATE + PACKAGE BODY). + + When a packaged routine uses a determined database object, it's registered on Firebird system + tables that the package body depends on that object. If you want to, for example, drop that + object, you first need to remove who depends on it. As who depends on it is a package body, + you can just drop it even if some other database object depends on this package. When the body + is dropped, the header remains, allowing you to create its body again after change it based on + the object removal. + + - Facilitate permission management. + + It's generally a good practice to create routines with a privileged database user and grant + usage to them for users or roles. As Firebird runs the routines with the caller privileges, + it's also necessary to grant resources usage to each routine, when these resources would not + be directly accessible to the callers, and grant usage of each routine to users and/or roles. + + Packaged routines do not have individual privileges. The privileges act on the package. + Privileges granted to packages are valid for all (including private) package body routines, + but are stored for the package header. Example usage: + GRANT SELECT ON TABLE secret TO PACKAGE pk_secret; + GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret; + + - Introduce private scope to routines making them available only for internal usage in the + defining package. + + All programming languages have the notion of routine scope. But without some form of grouping, + this is not possible. Firebird packages also works as Delphi units in this regard. If a + routine is not declared on the package header (interface) and is implemented in the body + (implementation), it becomes a private routine. A private routine can only be called from + inside its package. + +Syntax rules: + - A package body should implement all routines declared in the header and in the body start, + with the same signature. + - Default value for procedure parameters could not be redefined (be informed in + and ). That means, they can be in only for private + procedures not declared. + +Notes: + - DROP PACKAGE drops the package body before drop its header. + - UDFs (DECLARE EXTERNAL FUNCTION) are currently not supported inside packages. + +Examples: + - To come. + diff --git a/extern/btyacc/output.c b/extern/btyacc/output.c index 6ee886b589..e985830474 100644 --- a/extern/btyacc/output.c +++ b/extern/btyacc/output.c @@ -809,9 +809,17 @@ void output_defines() register char *s; FILE *dc_file; + /* ASF: changed to separate (_yacc_defines_keywords and _yacc_defines_yystype instead of + _yacc_defines_h_) keyword definitions of YYSTYPE because keywords may conflict with other + things */ + if(dflag) { + /*** fprintf(defines_file, "#ifndef _yacc_defines_h_\n"); fprintf(defines_file, "#define _yacc_defines_h_\n\n"); + ***/ + fprintf(defines_file, "#ifndef _yacc_defines_keywords\n"); + fprintf(defines_file, "#define _yacc_defines_keywords\n\n"); } /* VM: Print to either code file or defines file but not to both */ @@ -847,20 +855,32 @@ void output_defines() ++outline; fprintf(dc_file, "#define YYERRCODE %d\n", symbol_value[1]); + if(dflag) { + fprintf(defines_file, "\n#endif /* _yacc_defines_keywords */\n\n"); + } + if (dflag && unionized) { + fprintf(defines_file, "#ifndef _yacc_defines_yystype\n"); + fprintf(defines_file, "#define _yacc_defines_yystype\n"); + fclose(union_file); union_file = fopen(union_file_name, "r"); if (union_file == NULL) open_error(union_file_name); while ((c = getc(union_file)) != EOF) { putc(c, defines_file); } - fprintf(defines_file, "extern YYSTYPE yylval;\n"); + /* ASF: we define it on the Parser class + fprintf(defines_file, "extern YYSTYPE yylval;\n"); */ + + fprintf(defines_file, "\n#endif /* _yacc_defines_yystype */\n\n"); } + /*** if(dflag) { fprintf(defines_file, "\n#endif\n"); } + ***/ } diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index f0b7a08b85..13631475e8 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1386,6 +1386,10 @@ C -- PARAMETER (GDS__eds_expl_tran_ctrl = 335544986) INTEGER*4 GDS__no_trusted_spb PARAMETER (GDS__no_trusted_spb = 335544987) + INTEGER*4 GDS__package_name + PARAMETER (GDS__package_name = 335544988) + INTEGER*4 GDS__cannot_make_not_null + PARAMETER (GDS__cannot_make_not_null = 335544989) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw @@ -1500,8 +1504,36 @@ C -- PARAMETER (GDS__dsql_incompatible_trigger_type = 336003102) INTEGER*4 GDS__dsql_db_trigger_type_cant_change PARAMETER (GDS__dsql_db_trigger_type_cant_change = 336003103) + INTEGER*4 GDS__dyn_filter_not_found + PARAMETER (GDS__dyn_filter_not_found = 336068645) + INTEGER*4 GDS__dyn_func_not_found + PARAMETER (GDS__dyn_func_not_found = 336068649) + INTEGER*4 GDS__dyn_index_not_found + PARAMETER (GDS__dyn_index_not_found = 336068656) + INTEGER*4 GDS__dyn_view_not_found + PARAMETER (GDS__dyn_view_not_found = 336068662) + INTEGER*4 GDS__dyn_domain_not_found + PARAMETER (GDS__dyn_domain_not_found = 336068697) + INTEGER*4 GDS__dyn_cant_modify_auto_trig + PARAMETER (GDS__dyn_cant_modify_auto_trig = 336068717) INTEGER*4 GDS__dyn_dup_table PARAMETER (GDS__dyn_dup_table = 336068740) + INTEGER*4 GDS__dyn_proc_not_found + PARAMETER (GDS__dyn_proc_not_found = 336068748) + INTEGER*4 GDS__dyn_exception_not_found + PARAMETER (GDS__dyn_exception_not_found = 336068752) + INTEGER*4 GDS__dyn_proc_param_not_found + PARAMETER (GDS__dyn_proc_param_not_found = 336068754) + INTEGER*4 GDS__dyn_trig_not_found + PARAMETER (GDS__dyn_trig_not_found = 336068755) + INTEGER*4 GDS__dyn_charset_not_found + PARAMETER (GDS__dyn_charset_not_found = 336068759) + INTEGER*4 GDS__dyn_collation_not_found + PARAMETER (GDS__dyn_collation_not_found = 336068760) + INTEGER*4 GDS__dyn_role_not_found + PARAMETER (GDS__dyn_role_not_found = 336068763) + INTEGER*4 GDS__dyn_name_longer + PARAMETER (GDS__dyn_name_longer = 336068767) INTEGER*4 GDS__dyn_column_does_not_exist PARAMETER (GDS__dyn_column_does_not_exist = 336068784) INTEGER*4 GDS__dyn_role_does_not_exist @@ -1538,12 +1570,16 @@ C -- PARAMETER (GDS__dyn_dtype_conv_invalid = 336068818) INTEGER*4 GDS__dyn_zero_len_id PARAMETER (GDS__dyn_zero_len_id = 336068820) + INTEGER*4 GDS__dyn_gen_not_found + PARAMETER (GDS__dyn_gen_not_found = 336068822) INTEGER*4 GDS__max_coll_per_charset PARAMETER (GDS__max_coll_per_charset = 336068829) INTEGER*4 GDS__invalid_coll_attr PARAMETER (GDS__invalid_coll_attr = 336068830) INTEGER*4 GDS__dyn_wrong_gtt_scope PARAMETER (GDS__dyn_wrong_gtt_scope = 336068840) + INTEGER*4 GDS__dyn_table_not_found + PARAMETER (GDS__dyn_table_not_found = 336068849) INTEGER*4 GDS__dyn_scale_too_big PARAMETER (GDS__dyn_scale_too_big = 336068852) INTEGER*4 GDS__dyn_precision_too_small diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index d5a12343de..1561e51090 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -700,6 +700,8 @@ const gds_out_of_temp_space = 335544985; gds_eds_expl_tran_ctrl = 335544986; gds_no_trusted_spb = 335544987; + gds_package_name = 335544988; + gds_cannot_make_not_null = 335544989; gds_gfix_db_name = 335740929; gds_gfix_invalid_sw = 335740930; gds_gfix_incmp_sw = 335740932; @@ -757,7 +759,21 @@ const gds_upd_ins_with_complex_view = 336003101; gds_dsql_incompatible_trigger_type = 336003102; gds_dsql_db_trigger_type_cant_change = 336003103; + gds_dyn_filter_not_found = 336068645; + gds_dyn_func_not_found = 336068649; + gds_dyn_index_not_found = 336068656; + gds_dyn_view_not_found = 336068662; + gds_dyn_domain_not_found = 336068697; + gds_dyn_cant_modify_auto_trig = 336068717; gds_dyn_dup_table = 336068740; + gds_dyn_proc_not_found = 336068748; + gds_dyn_exception_not_found = 336068752; + gds_dyn_proc_param_not_found = 336068754; + gds_dyn_trig_not_found = 336068755; + gds_dyn_charset_not_found = 336068759; + gds_dyn_collation_not_found = 336068760; + gds_dyn_role_not_found = 336068763; + gds_dyn_name_longer = 336068767; gds_dyn_column_does_not_exist = 336068784; gds_dyn_role_does_not_exist = 336068796; gds_dyn_no_grant_admin_opt = 336068797; @@ -776,9 +792,11 @@ const gds_dyn_invalid_dtype_conversion = 336068817; gds_dyn_dtype_conv_invalid = 336068818; gds_dyn_zero_len_id = 336068820; + gds_dyn_gen_not_found = 336068822; gds_max_coll_per_charset = 336068829; gds_invalid_coll_attr = 336068830; gds_dyn_wrong_gtt_scope = 336068840; + gds_dyn_table_not_found = 336068849; gds_dyn_scale_too_big = 336068852; gds_dyn_precision_too_small = 336068853; gds_dyn_miss_priv_warning = 336068855; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 08a1d9a679..68b1da6565 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -132,13 +132,14 @@ void write_exceptions(); void write_field_dimensions(); void write_filters(); void write_functions(); -void write_function_args(GDS_NAME); +void write_function_args(const GDS_NAME, GDS_NAME); void write_generators(); void write_sql_roles(); void write_mapping(); void write_global_fields(); +void write_packages(); void write_procedures(); -void write_procedure_prms(GDS_NAME); +void write_procedure_prms(const GDS_NAME, const GDS_NAME); void write_ref_constraints(); void write_rel_constraints(); void write_relations(); @@ -175,9 +176,20 @@ enum backup_capabilities // rdb$valid_blr in rdb$procedures // rdb$default_value, rdb$default_source, rdb$collation_id, // rdb$null_flag and rdb$parameter_mechanism in rdb$procedure_parameters - BCK_ods11_2 = 65536 // rdb$field_name and rdb$relation_name in rdb$procedure_parameters + BCK_ods11_2 = 65536,// rdb$field_name and rdb$relation_name in rdb$procedure_parameters // rdb$admin system role // rdb$message enlarged to 1023. + BCK_ods12_0 =131072 // rdb$engine_name and rdb$entrypoint in rdb$triggers + // rdb$package_name in rdb$dependencies + // rdb$engine_name, rdb$package_name and rdb$private_flag + // in rdb$functions + // rdb$package_name in rdb$function_arguments + // rdb$engine_name, rdb$entry_point, rdb$package_name + // and rdb$private_flag in rdb$procedures + // rdb$package_name in rdb$procedure_parameters + // rdb$package_name in mon$call_stack + // Table rdb$packages + // Type of rdb$triggers.rdb$trigger_type changed from SMALLINT to BIGINT }; // ASF: Engine that works with ODS11.1 supports access to non-existent system fields. // Reads returns NULL and writes do nothing. @@ -232,6 +244,7 @@ const rfr_tab_t rfr_table[] = //{"RDB$PROCEDURE_PARAMETERS", "RDB$PARAMETER_MECHANISM", BCK_ods11_1}, {"RDB$PROCEDURE_PARAMETERS", "RDB$FIELD_NAME", BCK_ods11_2}, //{"RDB$PROCEDURE_PARAMETERS", "RDB$RELATION_NAME", BCK_ods11_2}, + {"RDB$PROCEDURES", "RDB$ENGINE_NAME", BCK_ods12_0}, {0, 0, 0} }; @@ -386,6 +399,13 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) write_collations(); } + if (tdgbl->BCK_capabilities & BCK_ods12_0) + { + // Write packages + BURP_verbose(336); // msg 336 writing packages + write_packages(); + } + if (tdgbl->BCK_capabilities & BCK_ffmptt) { // Write functions @@ -3046,38 +3066,82 @@ void write_functions() * **************************************/ GDS_NAME func; - TEXT temp[GDS_NAME_LEN]; + TEXT temp[GDS_NAME_LEN * 2]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); - FOR (REQUEST_HANDLE req_handle1) - X IN RDB$FUNCTIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 - put(tdgbl, rec_function); - const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME); - MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); - BURP_verbose (147, temp); - // msg 147 writing function %.*s - put_source_blob (att_function_description2, att_function_description, X.RDB$DESCRIPTION); - PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME); - PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT); - put_numeric (att_function_return_arg, X.RDB$RETURN_ARGUMENT); - put_numeric (att_function_type, X.RDB$FUNCTION_TYPE); - PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME); - put(tdgbl, att_end); - COPY (X.RDB$FUNCTION_NAME, func); - write_function_args (func); - put(tdgbl, rec_function_end); - END_FOR; - ON_ERROR - general_on_error(); - END_ERROR; + if (tdgbl->BCK_capabilities & BCK_ods12_0) + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FUNCTIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + put(tdgbl, rec_function); + + SSHORT prefixLen = 0; + + if (!X.RDB$PACKAGE_NAME.NULL) + { + prefixLen = PUT_TEXT(att_function_package_name, X.RDB$PACKAGE_NAME); + MISC_terminate(X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp)); + temp[prefixLen++] = '.'; + } + + const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l, sizeof(temp) - prefixLen); + BURP_verbose (147, temp); + // msg 147 writing function %.*s + put_source_blob (att_function_description2, att_function_description, X.RDB$DESCRIPTION); + PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME); + PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT); + put_numeric (att_function_return_arg, X.RDB$RETURN_ARGUMENT); + put_numeric (att_function_type, X.RDB$FUNCTION_TYPE); + PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME); + + if (!X.RDB$ENGINE_NAME.NULL) + PUT_TEXT(att_function_engine_name, X.RDB$ENGINE_NAME); + + if (!X.RDB$PRIVATE_FLAG.NULL) + put_numeric(att_function_private_flag, X.RDB$PRIVATE_FLAG); + + put(tdgbl, att_end); + COPY (X.RDB$FUNCTION_NAME, func); + write_function_args ((X.RDB$PACKAGE_NAME.NULL ? "" : X.RDB$PACKAGE_NAME), func); + put(tdgbl, rec_function_end); + END_FOR; + ON_ERROR + general_on_error(); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FUNCTIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + put(tdgbl, rec_function); + const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); + BURP_verbose (147, temp); + // msg 147 writing function %.*s + put_source_blob (att_function_description2, att_function_description, X.RDB$DESCRIPTION); + PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME); + PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT); + put_numeric (att_function_return_arg, X.RDB$RETURN_ARGUMENT); + put_numeric (att_function_type, X.RDB$FUNCTION_TYPE); + PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME); + put(tdgbl, att_end); + COPY (X.RDB$FUNCTION_NAME, func); + write_function_args ("", func); + put(tdgbl, rec_function_end); + END_FOR; + ON_ERROR + general_on_error(); + END_ERROR; + } MISC_release_request_silent(req_handle1); } -void write_function_args( GDS_NAME funcptr) +void write_function_args(const GDS_NAME package, GDS_NAME funcptr) { /************************************** * @@ -3089,7 +3153,7 @@ void write_function_args( GDS_NAME funcptr) * write all arguments for a function. * **************************************/ - TEXT temp[GDS_NAME_LEN]; + TEXT temp[GDS_NAME_LEN * 2]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -3099,7 +3163,48 @@ void write_function_args( GDS_NAME funcptr) // requests--this requires more code but it is well worth it // for the performance benefits, especially remotely--deej - if (tdgbl->BCK_capabilities & BCK_ods10) + if (tdgbl->BCK_capabilities & BCK_ods12_0) + { + FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS + WITH X.RDB$FUNCTION_NAME EQ funcptr AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(package, '') + + put(tdgbl, rec_function_arg); + + SSHORT prefixLen = 0; + + if (!X.RDB$PACKAGE_NAME.NULL) + { + prefixLen = PUT_TEXT(att_functionarg_package_name, X.RDB$PACKAGE_NAME); + MISC_terminate(X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp)); + temp[prefixLen++] = '.'; + } + + const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l, sizeof(temp) - prefixLen); + BURP_verbose (141, temp); + // msg 141 writing argument for function %s + + put_numeric (att_functionarg_position, X.RDB$ARGUMENT_POSITION); + put_numeric (att_functionarg_mechanism, X.RDB$MECHANISM); + put_numeric (att_functionarg_field_type, X.RDB$FIELD_TYPE); + put_numeric (att_functionarg_field_scale, X.RDB$FIELD_SCALE); + put_numeric (att_functionarg_field_length, X.RDB$FIELD_LENGTH); + put_numeric (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE); + if (!X.RDB$CHARACTER_SET_ID.NULL) + put_numeric (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID); + + if (!X.RDB$FIELD_PRECISION.NULL) + put_numeric (att_functionarg_field_precision, X.RDB$FIELD_PRECISION); + + put(tdgbl, att_end); + END_FOR; + ON_ERROR + general_on_error(); + END_ERROR; + } + else if (tdgbl->BCK_capabilities & BCK_ods10) { FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) X IN RDB$FUNCTION_ARGUMENTS WITH @@ -3436,6 +3541,65 @@ void write_global_fields() } +void write_packages() +{ +/************************************** + * + * w r i t e _ p a c k a g e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each package. + * + **************************************/ + TEXT temp[GDS_NAME_LEN]; + isc_req_handle req_handle1 = 0; + + BurpGlobals* tdgbl = BurpGlobals::getSpecific(); + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$PACKAGES + WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + { + put(tdgbl, rec_package); + const SSHORT l = PUT_TEXT(att_package_name, X.RDB$PACKAGE_NAME); + MISC_terminate(X.RDB$PACKAGE_NAME, temp, l, sizeof(temp)); + + BURP_verbose(335, temp); // msg 335 writing package @1 + + if (!X.RDB$PACKAGE_HEADER_SOURCE.NULL) + { + put_source_blob(att_package_header_source, att_package_header_source, + X.RDB$PACKAGE_HEADER_SOURCE); + } + + if (!X.RDB$PACKAGE_BODY_SOURCE.NULL) + { + put_source_blob(att_package_body_source, att_package_body_source, + X.RDB$PACKAGE_BODY_SOURCE); + } + + if (!X.RDB$SECURITY_CLASS.NULL) + PUT_TEXT(att_package_security_class, X.RDB$SECURITY_CLASS); + if (!X.RDB$OWNER_NAME.NULL) + PUT_TEXT(att_package_owner_name, X.RDB$OWNER_NAME); + + if (!X.RDB$DESCRIPTION.NULL) + put_source_blob(att_package_description, att_package_description, X.RDB$DESCRIPTION); + + put(tdgbl, att_end); + } + END_FOR + ON_ERROR + general_on_error(); + END_ERROR + + MISC_release_request_silent(req_handle1); +} + + void write_procedures() { /************************************** @@ -3461,18 +3625,32 @@ void write_procedures() X IN RDB$PROCEDURES WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 put(tdgbl, rec_procedure); - const SSHORT l = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME); - MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp)); + + SSHORT prefixLen = 0; + + if (!X.RDB$PACKAGE_NAME.NULL) + { + prefixLen = PUT_TEXT(att_procedure_package_name, X.RDB$PACKAGE_NAME); + MISC_terminate (X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp)); + temp[prefixLen++] = '.'; + } + + const SSHORT len = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME); + MISC_terminate (X.RDB$PROCEDURE_NAME, temp + prefixLen, len, sizeof(temp) - prefixLen); + BURP_verbose (193, temp); // msg 193 writing stored procedure %.*s put_numeric (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS); put_numeric (att_procedure_outputs, X.RDB$PROCEDURE_OUTPUTS); put_source_blob(att_procedure_description2, att_procedure_description, X.RDB$DESCRIPTION); put_source_blob (att_procedure_source2, att_procedure_source, X.RDB$PROCEDURE_SOURCE); - put_blr_blob (att_procedure_blr, X.RDB$PROCEDURE_BLR); + + if (!X.RDB$PROCEDURE_BLR.NULL) + put_blr_blob (att_procedure_blr, X.RDB$PROCEDURE_BLR); + if (!X.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_procedure_security_class, X.RDB$SECURITY_CLASS); - if (!X.RDB$SECURITY_CLASS.NULL) + if (!X.RDB$OWNER_NAME.NULL) PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME); if (!X.RDB$PROCEDURE_TYPE.NULL) put_numeric (att_procedure_type, X.RDB$PROCEDURE_TYPE); @@ -3480,9 +3658,19 @@ void write_procedures() put_numeric (att_procedure_valid_blr, X.RDB$VALID_BLR); if (!X.RDB$DEBUG_INFO.NULL) put_blr_blob (att_procedure_debug_info, X.RDB$DEBUG_INFO); + + if (!X.RDB$ENGINE_NAME.NULL) + PUT_TEXT(att_procedure_engine_name, X.RDB$ENGINE_NAME); + + if (!X.RDB$ENTRYPOINT.NULL) + PUT_TEXT(att_procedure_entrypoint, X.RDB$ENTRYPOINT); + + if (!X.RDB$PRIVATE_FLAG.NULL) + put_numeric(att_procedure_private_flag, X.RDB$PRIVATE_FLAG); + put(tdgbl, att_end); COPY(X.RDB$PROCEDURE_NAME, proc); - write_procedure_prms (proc); + write_procedure_prms ((X.RDB$PACKAGE_NAME.NULL ? "" : X.RDB$PACKAGE_NAME), proc); put(tdgbl, rec_procedure_end); END_FOR; ON_ERROR @@ -3509,7 +3697,7 @@ void write_procedures() PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME); put(tdgbl, att_end); COPY(X.RDB$PROCEDURE_NAME, proc); - write_procedure_prms (proc); + write_procedure_prms ("", proc); put(tdgbl, rec_procedure_end); END_FOR; ON_ERROR @@ -3521,7 +3709,7 @@ void write_procedures() } -void write_procedure_prms( GDS_NAME procptr) +void write_procedure_prms(const GDS_NAME package, const GDS_NAME procptr) { /************************************** * @@ -3540,7 +3728,10 @@ void write_procedure_prms( GDS_NAME procptr) if (tdgbl->BCK_capabilities & BCK_ods11_1) { FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1) - X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$PROCEDURE_NAME EQ procptr + X IN RDB$PROCEDURE_PARAMETERS + WITH X.RDB$PROCEDURE_NAME EQ procptr AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(package, '') + { put(tdgbl, rec_procedure_prm); const SSHORT l = PUT_TEXT (att_procedureprm_name, X.RDB$PARAMETER_NAME); MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp)); @@ -3568,6 +3759,7 @@ void write_procedure_prms( GDS_NAME procptr) PUT_TEXT(att_procedureprm_relation_name, X.RDB$RELATION_NAME); put(tdgbl, att_end); + } END_FOR; ON_ERROR general_on_error(); @@ -4037,7 +4229,12 @@ void write_triggers() PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME); put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE); - put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE); + + if (X.RDB$TRIGGER_TYPE >= SLONG_MIN && X.RDB$TRIGGER_TYPE <= SLONG_MAX) + put_numeric (att_trig_type, (SLONG) X.RDB$TRIGGER_TYPE); + else + put_int64 (att_trig_type2, X.RDB$TRIGGER_TYPE); + put_blr_blob (att_trig_blr, X.RDB$TRIGGER_BLR); put_source_blob (att_trig_source2, att_trig_source, X.RDB$TRIGGER_SOURCE); put_source_blob (att_trig_description2, att_trig_description, X.RDB$DESCRIPTION); @@ -4053,6 +4250,12 @@ void write_triggers() if (!X.RDB$DEBUG_INFO.NULL) put_blr_blob (att_trig_debug_info, X.RDB$DEBUG_INFO); + if (!X.RDB$ENGINE_NAME.NULL) + PUT_TEXT(att_trig_engine_name, X.RDB$ENGINE_NAME); + + if (!X.RDB$ENTRYPOINT.NULL) + PUT_TEXT(att_trig_entrypoint, X.RDB$ENTRYPOINT); + put(tdgbl, att_end); END_FOR; diff --git a/src/burp/burp.h b/src/burp/burp.h index 292bcaa8b2..bf9e9b2708 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -114,7 +114,8 @@ enum rec_type { rec_charset, // Character sets rec_collation, // Collations rec_sql_roles, // SQL roles - rec_mapping // Mapping of security names + rec_mapping, // Mapping of security names + rec_package // Package }; @@ -387,6 +388,9 @@ enum att_type { att_trig_flags, att_trig_valid_blr, att_trig_debug_info, + att_trig_engine_name, + att_trig_entrypoint, + att_trig_type2, // Function attributes @@ -399,6 +403,9 @@ enum att_type { att_function_query_name, att_function_type, att_function_description2, + att_function_engine_name, + att_function_package_name, + att_function_private_flag, // Function argument attributes @@ -411,6 +418,7 @@ enum att_type { att_functionarg_field_sub_type, att_functionarg_character_set, att_functionarg_field_precision, + att_functionarg_package_name, // TYPE relation attributes att_type_name = SERIES, @@ -474,6 +482,10 @@ enum att_type { att_procedure_type, att_procedure_valid_blr, att_procedure_debug_info, + att_procedure_engine_name, + att_procedure_entrypoint, + att_procedure_package_name, + att_procedure_private_flag, // Stored procedure parameter attributes @@ -551,7 +563,15 @@ enum att_type { att_map_os = SERIES, att_map_user, att_map_role, - att_auto_map_role + att_auto_map_role, + + // Package attributes + att_package_name = SERIES, + att_package_header_source, + att_package_body_source, + att_package_security_class, + att_package_owner_name, + att_package_description }; @@ -655,12 +675,21 @@ enum burp_rel_flags_vals { REL_external = 2 }; +// package definition +struct burp_pkg +{ + burp_pkg* pkg_next; + GDS_NAME pkg_name; + GDS_NAME pkg_owner; +}; + // procedure definition - holds useful procedure type stuff struct burp_prc { burp_prc* prc_next; //SSHORT prc_name_length; // Currently useless, but didn't want to delete it. + GDS_NAME prc_package; GDS_NAME prc_name; GDS_NAME prc_owner; // relation owner, if not us }; @@ -866,6 +895,7 @@ public: UCHAR* io_ptr; int io_cnt; burp_rel* relations; + burp_pkg* packages; burp_prc* procedures; SLONG BCK_capabilities; // Format of the backup being read on restore; gbak always creates it using the latest version @@ -922,6 +952,7 @@ public: isc_req_handle handles_get_index_req_handle2; isc_req_handle handles_get_index_req_handle3; isc_req_handle handles_get_index_req_handle4; + isc_req_handle handles_get_package_req_handle1; isc_req_handle handles_get_procedure_prm_req_handle1; isc_req_handle handles_get_procedure_req_handle1; isc_req_handle handles_get_ranges_req_handle1; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index c8583eef5a..15469a43ba 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -52,6 +52,7 @@ #include "../common/classes/ClumpletWriter.h" #include "../common/classes/UserBlob.h" #include "../common/classes/SafeArg.h" +#include "../common/utils_proto.h" #include "memory_routines.h" using MsgFormat::SafeArg; @@ -81,6 +82,7 @@ const int DB_VERSION_DDL10 = 100; // ods10 db, IB6, FB1, FB1.5 const int DB_VERSION_DDL11 = 110; // ods11 db, FB2 const int DB_VERSION_DDL11_1 = 111; // ods11.1 db, FB2.1 const int DB_VERSION_DDL11_2 = 112; // ods11.2 db, FB2.5 +const int DB_VERSION_DDL12_0 = 120; // ods12.0 db, FB3.0 const int DB_VERSION_OLDEST_SUPPORTED = DB_VERSION_DDL8; // IB4.0 is ods8 @@ -137,8 +139,9 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel*); void get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool); SLONG get_numeric(BurpGlobals* tdgbl); SINT64 get_int64(BurpGlobals* tdgbl); +bool get_package(BurpGlobals* tdgbl); bool get_procedure(BurpGlobals* tdgbl); -bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME ); +bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME, GDS_NAME); bool get_ref_constraint(BurpGlobals* tdgbl); bool get_rel_constraint(BurpGlobals* tdgbl); bool get_relation(BurpGlobals* tdgbl); @@ -258,7 +261,7 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) * **************************************/ isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0; - isc_req_handle req_handle4 = 0, req_handle5 = 0; + isc_req_handle req_handle4 = 0, req_handle5 = 0, req_handle6 = 0; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -267,6 +270,7 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) tdgbl->io_cnt = 0; tdgbl->relations = NULL; + tdgbl->packages = NULL; tdgbl->procedures = NULL; tdgbl->RESTORE_format = 0; tdgbl->RESTORE_ods = 0; @@ -482,30 +486,84 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) // update_view_dbkey_lengths(tdgbl); - // Change ownership of any procedures necessary + // Change ownership of any packages + for (burp_pkg* package = tdgbl->packages; package; package = package->pkg_next) + { + if (package->pkg_owner[0]) + { + FOR (REQUEST_HANDLE req_handle6) + X IN RDB$PACKAGES + WITH X.RDB$PACKAGE_NAME EQ package->pkg_name + { + MODIFY X + strcpy(X.RDB$OWNER_NAME, package->pkg_owner); + END_MODIFY; + ON_ERROR + MISC_release_request_silent(req_handle6); + general_on_error(); + END_ERROR; + if (!X.RDB$SECURITY_CLASS.NULL) + restore_security_class(tdgbl, package->pkg_owner, X.RDB$SECURITY_CLASS); + } + END_FOR + ON_ERROR + MISC_release_request_silent(req_handle6); + general_on_error (); + END_ERROR + } + } + + // Change ownership of any procedures necessary for (burp_prc* procedure = tdgbl->procedures; procedure; procedure = procedure->prc_next) { if (procedure->prc_owner[0]) { - FOR (REQUEST_HANDLE req_handle4) - X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name + if (tdgbl->RESTORE_ods >= DB_VERSION_DDL12_0) + { + FOR (REQUEST_HANDLE req_handle4) + X IN RDB$PROCEDURES + WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(procedure->prc_package, '') + { + MODIFY X + strcpy (X.RDB$OWNER_NAME, procedure->prc_owner); + END_MODIFY; + ON_ERROR + MISC_release_request_silent(req_handle4); + general_on_error (); + END_ERROR - MODIFY X - strcpy (X.RDB$OWNER_NAME, procedure->prc_owner); - END_MODIFY; + if (!X.RDB$SECURITY_CLASS.NULL) + restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS); + } + END_FOR; ON_ERROR MISC_release_request_silent(req_handle4); general_on_error (); - END_ERROR; + END_ERROR + } + else + { + FOR (REQUEST_HANDLE req_handle4) + X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name + { + MODIFY X + strcpy (X.RDB$OWNER_NAME, procedure->prc_owner); + END_MODIFY; + ON_ERROR + MISC_release_request_silent(req_handle4); + general_on_error (); + END_ERROR; - restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS); - - END_FOR; - ON_ERROR - MISC_release_request_silent(req_handle4); - general_on_error (); - END_ERROR; + restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS); + } + END_FOR + ON_ERROR + MISC_release_request_silent(req_handle4); + general_on_error (); + END_ERROR + } } } @@ -847,6 +905,7 @@ void check_db_version(BurpGlobals* tdgbl) {"RDB$ROLES", "RDB$DESCRIPTION", DB_VERSION_DDL11}, // FB2 {"RDB$RELATIONS", "RDB$RELATION_TYPE", DB_VERSION_DDL11_1}, // FB2.1 {"RDB$PROCEDURE_PARAMETERS", "RDB$FIELD_NAME", DB_VERSION_DDL11_2}, // FB2.5 + {"RDB$PROCEDURES", "RDB$ENGINE_NAME", DB_VERSION_DDL12_0}, // FB3.0 {0, 0, 0} }; @@ -3523,79 +3582,175 @@ bool get_function(BurpGlobals* tdgbl) * **************************************/ att_type attribute; - GDS_NAME function_name; - TEXT temp[GDS_NAME_LEN]; + ///GDS_NAME function_name; + TEXT temp[GDS_NAME_LEN * 2]; SSHORT l; scan_attr_t scan_next_attr; bool existFlag = false; - STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) - X IN RDB$FUNCTIONS - X.RDB$SYSTEM_FLAG = 0; - X.RDB$SYSTEM_FLAG.NULL = FALSE; - X.RDB$DESCRIPTION.NULL = TRUE; + if (tdgbl->RESTORE_ods >= DB_VERSION_DDL12_0) + { + STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) + X IN RDB$FUNCTIONS + X.RDB$SYSTEM_FLAG = 0; + X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$ENGINE_NAME.NULL = TRUE; + X.RDB$PACKAGE_NAME.NULL = TRUE; + X.RDB$PRIVATE_FLAG.NULL = TRUE; - skip_init(&scan_next_attr); - while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) - { - switch (attribute) + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { - case att_function_name: - l = GET_TEXT(X.RDB$FUNCTION_NAME); - MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); - BURP_verbose (118, temp); - // msg 118 restoring function %s - break; + switch (attribute) + { + case att_function_name: + { + SSHORT prefixLen = 0; + if (!X.RDB$PACKAGE_NAME.NULL) + { + prefixLen = strlen(X.RDB$PACKAGE_NAME); + memcpy(temp, X.RDB$PACKAGE_NAME, prefixLen); + temp[prefixLen++] = '.'; + } - case att_function_description: - X.RDB$DESCRIPTION.NULL = FALSE; - get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); - break; + l = GET_TEXT(X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l, + sizeof(temp) - prefixLen); + BURP_verbose (118, temp); + // msg 118 restoring function %s + break; + } - case att_function_description2: - X.RDB$DESCRIPTION.NULL = FALSE; - get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); - break; + case att_function_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); + break; - case att_function_module_name: - GET_TEXT(X.RDB$MODULE_NAME); - break; + case att_function_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); + break; - case att_function_entrypoint: - GET_TEXT(X.RDB$ENTRYPOINT); - break; + case att_function_module_name: + GET_TEXT(X.RDB$MODULE_NAME); + break; - case att_function_return_arg: - X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(tdgbl); - break; + case att_function_entrypoint: + GET_TEXT(X.RDB$ENTRYPOINT); + break; - case att_function_query_name: - GET_TEXT(X.RDB$QUERY_NAME); - break; + case att_function_return_arg: + X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(tdgbl); + break; - case att_function_type: - X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(tdgbl); - break; + case att_function_query_name: + GET_TEXT(X.RDB$QUERY_NAME); + break; - default: - bad_attribute (scan_next_attr, attribute, 89); - // msg 89 function - break; + case att_function_type: + X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(tdgbl); + break; + + case att_function_engine_name: + GET_TEXT(X.RDB$ENGINE_NAME); + X.RDB$ENGINE_NAME.NULL = FALSE; + break; + + case att_function_package_name: + GET_TEXT(X.RDB$PACKAGE_NAME); + fb_utils::exact_name(X.RDB$PACKAGE_NAME); + X.RDB$PACKAGE_NAME.NULL = FALSE; + break; + + case att_function_private_flag: + X.RDB$PRIVATE_FLAG = (USHORT) get_numeric(tdgbl); + X.RDB$PRIVATE_FLAG.NULL = FALSE; + break; + + default: + bad_attribute (scan_next_attr, attribute, 89); + // msg 89 function + break; + } } - } - strcpy (function_name, X.RDB$FUNCTION_NAME); - END_STORE; - ON_ERROR - if (gds_status[1] != isc_no_dup) - { - general_on_error (); - } - else - { - existFlag = true; - } - END_ERROR; + + ///strcpy (function_name, X.RDB$FUNCTION_NAME); + END_STORE; + ON_ERROR + if (gds_status[1] != isc_no_dup) + general_on_error (); + else + existFlag = true; + END_ERROR; + } + else + { + STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) + X IN RDB$FUNCTIONS + X.RDB$SYSTEM_FLAG = 0; + X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$DESCRIPTION.NULL = TRUE; + + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) + { + case att_function_name: + l = GET_TEXT(X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); + BURP_verbose (118, temp); + // msg 118 restoring function %s + break; + + case att_function_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); + break; + + case att_function_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); + break; + + case att_function_module_name: + GET_TEXT(X.RDB$MODULE_NAME); + break; + + case att_function_entrypoint: + GET_TEXT(X.RDB$ENTRYPOINT); + break; + + case att_function_return_arg: + X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(tdgbl); + break; + + case att_function_query_name: + GET_TEXT(X.RDB$QUERY_NAME); + break; + + case att_function_type: + X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(tdgbl); + break; + + default: + bad_attribute (scan_next_attr, attribute, 89); + // msg 89 function + break; + } + } + + ///strcpy (function_name, X.RDB$FUNCTION_NAME); + END_STORE; + ON_ERROR + if (gds_status[1] != isc_no_dup) + general_on_error (); + else + existFlag = true; + END_ERROR; + } // at the end of args for a function is the rec_function_end marker while (get(tdgbl) == rec_function_arg) @@ -3618,7 +3773,7 @@ void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments) **************************************/ att_type attribute; SSHORT l; - TEXT temp[GDS_NAME_LEN]; + TEXT temp[GDS_NAME_LEN * 2]; scan_attr_t scan_next_attr; if (skip_arguments) @@ -3665,6 +3820,10 @@ void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments) get_numeric(tdgbl); break; + case att_functionarg_package_name: + GET_TEXT(buf); + break; + default: bad_attribute (scan_next_attr, attribute, 90); // msg 90 function argument @@ -3674,7 +3833,97 @@ void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments) return; } - if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) + if (tdgbl->RESTORE_ods >= DB_VERSION_DDL12_0) + { + // with RDB$FIELD_PRECISION + STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$FIELD_PRECISION.NULL = TRUE; + X.RDB$PACKAGE_NAME.NULL = TRUE; + + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) + { + case att_functionarg_name: + { + SSHORT prefixLen = 0; + if (!X.RDB$PACKAGE_NAME.NULL) + { + prefixLen = strlen(X.RDB$PACKAGE_NAME); + memcpy(temp, X.RDB$PACKAGE_NAME, prefixLen); + temp[prefixLen++] = '.'; + } + + l = GET_TEXT(X.RDB$FUNCTION_NAME); + MISC_terminate(X.RDB$FUNCTION_NAME, temp + prefixLen, l, + sizeof(temp) - prefixLen); + BURP_verbose (119, temp); + // msg 119 restoring argument for function %s + break; + } + + case att_functionarg_position: + X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_mechanism: + X.RDB$MECHANISM = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_field_type: + X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_field_scale: + X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_field_length: + X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_field_sub_type: + X.RDB$FIELD_SUB_TYPE.NULL = FALSE; + X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_character_set: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); + break; + + case att_functionarg_field_precision: + if (tdgbl->RESTORE_format >= 6) + { + X.RDB$FIELD_PRECISION.NULL = FALSE; + X.RDB$FIELD_PRECISION = (USHORT) get_numeric(tdgbl); + } + else + bad_attribute (scan_next_attr, attribute, 90); + break; + + case att_functionarg_package_name: + l = GET_TEXT(X.RDB$PACKAGE_NAME); + X.RDB$PACKAGE_NAME.NULL = FALSE; + fb_utils::exact_name(X.RDB$PACKAGE_NAME); + break; + + default: + bad_attribute (scan_next_attr, attribute, 90); + // msg 90 function argument + break; + } + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + } + else if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { // with RDB$FIELD_PRECISION STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) @@ -4859,6 +5108,91 @@ SINT64 get_int64(BurpGlobals* tdgbl) return isc_portable_integer ((UCHAR *) value, length); } +bool get_package(BurpGlobals* tdgbl) +{ +/************************************** + * + * g e t _ p a c k a g e + * + ************************************** + * + * Functional description + * Reconstruct a package. + * + **************************************/ + att_type attribute; + TEXT temp[GDS_NAME_LEN]; + SSHORT len; + scan_attr_t scan_next_attr; + + isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + + burp_pkg* package = (burp_pkg*) BURP_alloc_zero(sizeof(burp_pkg)); + package->pkg_next = tdgbl->packages; + tdgbl->packages = package; + + STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_package_req_handle1) + X IN RDB$PACKAGES + { + X.RDB$PACKAGE_HEADER_SOURCE.NULL = TRUE; + X.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE; + X.RDB$SECURITY_CLASS.NULL = TRUE; + X.RDB$OWNER_NAME.NULL = TRUE; + X.RDB$SYSTEM_FLAG = 0; + X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$DESCRIPTION.NULL = TRUE; + + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) + { + case att_package_name: + len = GET_TEXT(X.RDB$PACKAGE_NAME); + strcpy(package->pkg_name, X.RDB$PACKAGE_NAME); + MISC_terminate(X.RDB$PACKAGE_NAME, temp, len, sizeof(temp)); + BURP_verbose(337, temp); // msg 337 restoring package %s + break; + + case att_package_header_source: + get_source_blob(tdgbl, X.RDB$PACKAGE_HEADER_SOURCE, true); + X.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; + break; + + case att_package_body_source: + get_source_blob(tdgbl, X.RDB$PACKAGE_BODY_SOURCE, true); + X.RDB$PACKAGE_BODY_SOURCE.NULL = FALSE; + break; + + case att_package_security_class: + GET_TEXT(X.RDB$SECURITY_CLASS); + if (strncmp(X.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN) != 0) + X.RDB$SECURITY_CLASS.NULL = FALSE; + break; + + case att_package_owner_name: + GET_TEXT(package->pkg_owner); + break; + + case att_package_description: + get_source_blob(tdgbl, X.RDB$DESCRIPTION, true); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + default: + bad_attribute (scan_next_attr, attribute, 338); // msg 338 package + break; + } + } + } + END_STORE + ON_ERROR + general_on_error(); + END_ERROR + + return true; +} + bool get_procedure(BurpGlobals* tdgbl) { /************************************** @@ -4875,8 +5209,9 @@ bool get_procedure(BurpGlobals* tdgbl) * **************************************/ att_type attribute; + GDS_NAME package_name = ""; GDS_NAME procedure_name = ""; - TEXT temp[GDS_NAME_LEN]; + TEXT temp[GDS_NAME_LEN * 2]; SSHORT l; scan_attr_t scan_next_attr; @@ -4901,6 +5236,11 @@ bool get_procedure(BurpGlobals* tdgbl) X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$VALID_BLR.NULL = TRUE; X.RDB$DEBUG_INFO.NULL = TRUE; + X.RDB$PROCEDURE_BLR.NULL = TRUE; + X.RDB$ENGINE_NAME.NULL = TRUE; + X.RDB$ENTRYPOINT.NULL = TRUE; + X.RDB$PACKAGE_NAME.NULL = TRUE; + X.RDB$PRIVATE_FLAG.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) @@ -4908,13 +5248,25 @@ bool get_procedure(BurpGlobals* tdgbl) switch (attribute) { case att_procedure_name: + { + SSHORT prefixLen = 0; + if (package_name[0]) + { + prefixLen = strlen(package_name); + memcpy(temp, package_name, prefixLen); + temp[prefixLen++] = '.'; + } + l = GET_TEXT(X.RDB$PROCEDURE_NAME); //procedure->prc_name_length = l; strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME); - MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp)); + + MISC_terminate (X.RDB$PROCEDURE_NAME, temp + prefixLen, l, + sizeof(temp) - prefixLen); BURP_verbose (195, temp); // msg 195 restoring stored procedure %s break; + } case att_procedure_description: get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); @@ -4938,6 +5290,7 @@ bool get_procedure(BurpGlobals* tdgbl) case att_procedure_blr: get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true); + X.RDB$PROCEDURE_BLR.NULL = FALSE; break; case att_procedure_security_class: @@ -4989,6 +5342,29 @@ bool get_procedure(BurpGlobals* tdgbl) bad_attribute (scan_next_attr, attribute, 290); break; + case att_procedure_engine_name: + GET_TEXT(X.RDB$ENGINE_NAME); + X.RDB$ENGINE_NAME.NULL = FALSE; + break; + + case att_procedure_entrypoint: + GET_TEXT(X.RDB$ENTRYPOINT); + X.RDB$ENTRYPOINT.NULL = FALSE; + break; + + case att_procedure_package_name: + GET_TEXT(X.RDB$PACKAGE_NAME); + X.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(procedure->prc_package, X.RDB$PACKAGE_NAME); + strcpy(package_name, X.RDB$PACKAGE_NAME); + fb_utils::exact_name(package_name); + break; + + case att_procedure_private_flag: + X.RDB$PRIVATE_FLAG = (USHORT) get_numeric(tdgbl); + X.RDB$PRIVATE_FLAG.NULL = FALSE; + break; + default: bad_attribute (scan_next_attr, attribute, 290); // msg 290 procedure @@ -5096,12 +5472,12 @@ bool get_procedure(BurpGlobals* tdgbl) // at the end of prms for a procedure is the rec_procedure_end marker while (get(tdgbl) == rec_procedure_prm) - get_procedure_prm (tdgbl, procedure_name); + get_procedure_prm (tdgbl, package_name, procedure_name); return true; } -bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME procptr) +bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME package_name, GDS_NAME procptr) { /************************************** * @@ -5131,6 +5507,14 @@ bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME procptr) strcpy(X.RDB$PROCEDURE_NAME, procptr); + if (package_name[0]) + { + strcpy(X.RDB$PACKAGE_NAME, package_name); + X.RDB$PACKAGE_NAME.NULL = FALSE; + } + else + X.RDB$PACKAGE_NAME.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$DEFAULT_VALUE.NULL = TRUE; X.RDB$DEFAULT_SOURCE.NULL = TRUE; @@ -6474,6 +6858,8 @@ bool get_trigger(BurpGlobals* tdgbl) X.RDB$FLAGS.NULL = TRUE; X.RDB$VALID_BLR.NULL = TRUE; X.RDB$DEBUG_INFO.NULL = TRUE; + X.RDB$ENGINE_NAME.NULL = TRUE; + X.RDB$ENTRYPOINT.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) @@ -6484,6 +6870,10 @@ bool get_trigger(BurpGlobals* tdgbl) X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(tdgbl); break; + case att_trig_type2: + X.RDB$TRIGGER_TYPE = get_int64(tdgbl); + break; + case att_trig_flags: X.RDB$FLAGS = (USHORT) get_numeric(tdgbl); X.RDB$FLAGS.NULL = FALSE; @@ -6559,6 +6949,16 @@ bool get_trigger(BurpGlobals* tdgbl) bad_attribute (scan_next_attr, attribute, 134); break; + case att_trig_engine_name: + GET_TEXT(X.RDB$ENGINE_NAME); + X.RDB$ENGINE_NAME.NULL = FALSE; + break; + + case att_trig_entrypoint: + GET_TEXT(X.RDB$ENTRYPOINT); + X.RDB$ENTRYPOINT.NULL = FALSE; + break; + default: bad_attribute (scan_next_attr, attribute, 134); // msg 134 trigger @@ -6942,10 +7342,22 @@ bool get_user_privilege(BurpGlobals* tdgbl) bool exists = false; switch (object_type) { + case obj_package_header: + { + for (const burp_pkg* pkg = tdgbl->packages; pkg; pkg = pkg->pkg_next) + if (strcmp(pkg->pkg_name, relation_name) == 0) + { + exists = true; + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + break; + } + } + break; + case obj_procedure: { for (const burp_prc* proc = tdgbl->procedures; proc; proc = proc->prc_next) - if (!strcmp(proc->prc_name, relation_name)) + if (!proc->prc_package[0] && strcmp(proc->prc_name, relation_name) == 0) { exists = true; local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; @@ -6957,7 +7369,7 @@ bool get_user_privilege(BurpGlobals* tdgbl) case obj_relation: { for (const burp_rel* rel = tdgbl->relations; rel; rel = rel->rel_next) - if (!strcmp(rel->rel_name, relation_name)) + if (strcmp(rel->rel_name, relation_name) == 0) { exists = true; if (rel->rel_flags & REL_view) @@ -6967,9 +7379,9 @@ bool get_user_privilege(BurpGlobals* tdgbl) } break; - default: - exists = true; - break; + default: + exists = true; + break; } if (tdgbl->RESTORE_ods <= DB_VERSION_DDL8) @@ -7003,10 +7415,10 @@ bool get_user_privilege(BurpGlobals* tdgbl) if (flags & USER_PRIV_GRANT_OPTION) { X.RDB$GRANT_OPTION = grant_option; - if (grant_option == 0) - X.RDB$GRANT_OPTION.NULL = TRUE; - else - X.RDB$GRANT_OPTION.NULL = FALSE; + if (grant_option == 0) + X.RDB$GRANT_OPTION.NULL = TRUE; + else + X.RDB$GRANT_OPTION.NULL = FALSE; } if (flags & USER_PRIV_OBJECT_NAME) @@ -7694,6 +8106,12 @@ bool restore(BurpGlobals* tdgbl, flag = true; break; + case rec_package: + if (!get_package(tdgbl)) + return false; + flag = true; + break; + case rec_procedure: if (!get_procedure(tdgbl)) return false; diff --git a/src/common/classes/MetaName.h b/src/common/classes/MetaName.h index 53427f94f6..cced4b91fd 100644 --- a/src/common/classes/MetaName.h +++ b/src/common/classes/MetaName.h @@ -76,9 +76,13 @@ public: size_t length() const { return count; } const char* c_str() const { return data; } + const char* nullStr() const { return (count == 0 ? NULL : data); } bool isEmpty() const { return count == 0; } bool hasData() const { return count != 0; } + const char* begin() const { return data; } + const char* end() const { return data + count; } + int compare(const char* s, size_t l) const; int compare(const char* s) const { return compare(s, s ? strlen(s) : 0); } int compare(const MetaName& m) const { return memcmp(data, m.data, MAX_SQL_IDENTIFIER_SIZE); } diff --git a/src/common/classes/QualifiedName.h b/src/common/classes/QualifiedName.h new file mode 100644 index 0000000000..c004dbf1f8 --- /dev/null +++ b/src/common/classes/QualifiedName.h @@ -0,0 +1,97 @@ +/* + * PROGRAM: Client/Server Common Code + * MODULE: QualifiedName.h + * DESCRIPTION: Qualified metadata name holder. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2009 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef COMMON_QUALIFIEDNAME_H +#define COMMON_QUALIFIEDNAME_H + +#include "MetaName.h" +#include "array.h" + +namespace Firebird { + +class QualifiedName +{ +public: + QualifiedName(MemoryPool& p, const MetaName& aIdentifier, const MetaName& aQualifier) + : identifier(p, aIdentifier), + qualifier(p, aQualifier) + { + } + + QualifiedName(const MetaName& aIdentifier, const MetaName& aQualifier) + : identifier(aIdentifier), + qualifier(aQualifier) + { + } + + QualifiedName(MemoryPool& p) + : identifier(p), + qualifier(p) + { + } + + QualifiedName() + { + } + + QualifiedName(MemoryPool& p, const QualifiedName& src) + : identifier(p, src.identifier), + qualifier(p, src.qualifier) + { + } + +public: + bool operator >(const QualifiedName& m) const + { + return qualifier > m.qualifier || (qualifier == m.qualifier && identifier > m.identifier); + } + + bool operator ==(const QualifiedName& m) const + { + return identifier == m.identifier && qualifier == m.qualifier; + } + +public: + string toString() const + { + string s; + if (qualifier.hasData()) + { + s = qualifier.c_str(); + s.append("."); + } + s.append(identifier.c_str()); + return s; + } + +public: + MetaName identifier; + MetaName qualifier; +}; + +} // namespace Firebird + +#endif // COMMON_QUALIFIEDNAME_H diff --git a/src/common/classes/TriState.h b/src/common/classes/TriState.h index a5926a632a..86939ca5cb 100644 --- a/src/common/classes/TriState.h +++ b/src/common/classes/TriState.h @@ -32,6 +32,71 @@ #include "firebird.h" +// Do not have constructor to allow usage in unions (used in the parser). +template class TriStateRawType +{ +public: + static TriStateRawType val(const T& v) + { + TriStateRawType triState; + triState.value = v; + triState.specified = true; + return triState; + } + + static TriStateRawType empty() + { + TriStateRawType triState; + triState.value = (T) 0; + triState.specified = false; + return triState; + } + + void operator =(const T& newValue) + { + value = newValue; + specified = true; + } + + bool operator ==(const TriStateRawType& o) const + { + return (!specified && !o.specified) || (specified == o.specified && value == o.value); + } + +public: + T value; + bool specified; +}; + +template class TriStateType : public TriStateRawType +{ +public: + TriStateType(const T& v) + { + this->value = v; + this->specified = true; + } + + TriStateType(const TriStateType& o) + { + this->value = o.value; + this->specified = o.specified; + } + + TriStateType() + { + this->value = 0; + this->specified = false; + } + +public: + void operator =(const TriStateRawType& o) + { + this->value = o.value; + this->specified = o.specified; + } +}; + class TriState { public: diff --git a/src/common/classes/auto.h b/src/common/classes/auto.h index b7a322847d..a3951ab43c 100644 --- a/src/common/classes/auto.h +++ b/src/common/classes/auto.h @@ -40,7 +40,7 @@ template class SimpleDelete { public: - static void clear(What *ptr) + static void clear(What* ptr) { delete ptr; } diff --git a/src/common/classes/fb_string.h b/src/common/classes/fb_string.h index b1fea2703c..6cb9345192 100644 --- a/src/common/classes/fb_string.h +++ b/src/common/classes/fb_string.h @@ -650,7 +650,8 @@ namespace Firebird inline StringType& operator=(const StringType& v) { - fb_assert(&v != this); + if (&v == this) + return *this; return assign(v); } inline StringType& operator=(const_pointer s) diff --git a/src/common/classes/objects_array.h b/src/common/classes/objects_array.h index 1eac440eeb..b068ce3f0f 100644 --- a/src/common/classes/objects_array.h +++ b/src/common/classes/objects_array.h @@ -350,7 +350,9 @@ namespace Firebird explicit SortedObjectsArray(MemoryPool& p) : ObjectsArray >(p) { } + ObjectCmp> >(p) + { } + bool find(const ObjectKey& item, size_t& pos) const { const ObjectKey* const pItem = &item; @@ -358,11 +360,13 @@ namespace Firebird ObjectStorage, const ObjectKey*, ObjectKeyOfValue, ObjectCmp>*>(this)->find(pItem, pos); } + bool exist(const ObjectKey& item) const { - size_t pos; + size_t pos; // ignored return find(item, pos); } + size_t add(const ObjectValue& item) { return inherited::add(item); diff --git a/src/common/classes/rwlock.h b/src/common/classes/rwlock.h index b6234199e4..4a97fd196c 100644 --- a/src/common/classes/rwlock.h +++ b/src/common/classes/rwlock.h @@ -265,12 +265,23 @@ namespace Firebird { class ReadLockGuard { public: - ReadLockGuard(RWLock &alock) + ReadLockGuard(RWLock& alock) : lock(&alock) { lock->beginRead(); } - ~ReadLockGuard() { release(); } + + ReadLockGuard(RWLock* alock) + : lock(alock) + { + if (lock) + lock->beginRead(); + } + + ~ReadLockGuard() + { + release(); + } void release() { @@ -284,19 +295,30 @@ public: private: // Forbid copy constructor ReadLockGuard(const ReadLockGuard& source); - RWLock *lock; + RWLock* lock; }; // RAII holder of write lock class WriteLockGuard { public: - WriteLockGuard(RWLock &alock) + WriteLockGuard(RWLock& alock) : lock(&alock) { lock->beginWrite(); } - ~WriteLockGuard() { release(); } + + WriteLockGuard(RWLock* alock) + : lock(alock) + { + if (lock) + lock->beginWrite(); + } + + ~WriteLockGuard() + { + release(); + } void release() { @@ -310,10 +332,9 @@ public: private: // Forbid copy constructor WriteLockGuard(const WriteLockGuard& source); - RWLock *lock; + RWLock* lock; }; } // namespace Firebird #endif // #ifndef CLASSES_RWLOCK_H - diff --git a/src/common/classes/stack.h b/src/common/classes/stack.h index d492b906e4..4730ccfcfe 100644 --- a/src/common/classes/stack.h +++ b/src/common/classes/stack.h @@ -180,6 +180,25 @@ namespace Firebird { } } + // Push a element on the stack and pop when we go out of scope. + class AutoPushPop + { + public: + AutoPushPop(Stack& s, Object& o) + : stack(s) + { + stack.push(o); + } + + ~AutoPushPop() + { + stack.pop(); + } + + private: + Stack& stack; + }; + class iterator; friend class iterator; diff --git a/src/config/ConfObject.cpp b/src/config/ConfObject.cpp index efb9bfbcc1..81869e6ce4 100644 --- a/src/config/ConfObject.cpp +++ b/src/config/ConfObject.cpp @@ -271,7 +271,10 @@ Firebird::string ConfObject::expand(const char* rawValue) if (!changed) return temp; - return PathName::expandFilename (temp); + // ASF: Do not try to expand relative filenames. It seems better to use $(this), or if necessary + // a new expansion variable. Also, this function is bugged and is returning incorrect values. + // return PathName::expandFilename (temp); + return temp; } Firebird::string ConfObject::getValue(const char* attributeName) diff --git a/src/dbs/grant.gdl b/src/dbs/grant.gdl index 1d3c4bd2f7..e6a6892268 100644 --- a/src/dbs/grant.gdl +++ b/src/dbs/grant.gdl @@ -46,6 +46,10 @@ define trigger grant_trigger for rdb$user_privileges pre store 0 not any prc in rdb$procedures with prc.rdb$procedure_name eq new.rdb$relation_name abort 0; + if new.rdb$object_type = 18 /* obj_package_header */ and + not any pkg in rdb$packages + with pkg.rdb$package_name eq new.rdb$relation_name + abort 0; if new.rdb$user_type = 0 or new.rdb$user_type = 3 or new.rdb$user_type = 4 or new.rdb$user_type = 6 or @@ -177,6 +181,28 @@ define trigger grant_trigger for rdb$user_privileges pre store 0 else if prc.rdb$security_class not starting "SQL$" abort 3; end_for; + else if new.rdb$object_type = 18 /* obj_package_header */ + for pkg in rdb$packages + with pkg.rdb$package_name eq new.rdb$relation_name + if pkg.rdb$owner_name ne UPPERCASE (rdb$user_name) + and UPPERCASE (rdb$user_name) ne "SYSDBA" + if not any priv in rdb$user_privileges + with priv.rdb$relation_name eq new.rdb$relation_name and + priv.rdb$object_type eq 18 and + priv.rdb$privilege eq new.rdb$privilege and + priv.rdb$user eq new.rdb$grantor and + priv.rdb$user_type eq 8 and + priv.rdb$grant_option ne 0 and + (priv.rdb$field_name missing or + priv.rdb$field_name eq new.rdb$field_name) + abort 2; + if pkg.rdb$security_class missing + modify pkg + pkg.rdb$security_class = "SQL$" | gen_id (RDB$SECURITY_CLASS, 1); + end_modify + else if pkg.rdb$security_class not starting "SQL$" + abort 3; + end_for; end; end_trigger; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 1a41cbc279..ac92716cd2 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -21,26 +21,376 @@ #include "firebird.h" #include "../jrd/common.h" #include "../dsql/DdlNodes.h" +#include "../dsql/node.h" +#include "../jrd/blr.h" +#include "../jrd/dyn.h" +#include "../jrd/flags.h" +#include "../jrd/intl.h" #include "../jrd/jrd.h" +#include "../jrd/obj.h" +#include "../jrd/tra.h" +#include "../jrd/PreparedStatement.h" +#include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" +#include "../jrd/dyn_dl_proto.h" +#include "../jrd/dyn_ut_proto.h" #include "../jrd/exe_proto.h" +#include "../jrd/intl_proto.h" #include "../jrd/met_proto.h" +#include "../jrd/vio_proto.h" +#include "../dsql/ddl_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" +#include "../dsql/pass1_proto.h" +#include "../common/StatusArg.h" using namespace Firebird; namespace Jrd { +using namespace Firebird; +using namespace Dsql; DATABASE DB = STATIC "ODS.RDB"; +//---------------------- + + +// Escape a string accordingly to SQL rules. +template static string escapeString(const T& s) +{ + string ret; + + for (const char* p = s.begin(); p != s.end(); ++p) + { + ret += *p; + if (*p == '\'') + ret += '\''; + } + + return ret; +} + + +void DdlNode::executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, + DdlTriggerWhen when, int action, const Firebird::MetaName& objectName, + const Firebird::string& sqlText) +{ + Attachment* attachment = transaction->tra_attachment; + + // do nothing if user doesn't want database triggers + if (attachment->att_flags & ATT_no_db_triggers) + return; + + DdlTriggerContext context; + context.ddlEvent = DDL_TRIGGER_ACTION_NAMES[action - 1]; + context.objectName = objectName; + context.sqlText = sqlText; + + Stack::AutoPushPop autoContext(attachment->ddlTriggersContext, context); + AutoSavePoint savePoint(tdbb, transaction); + + EXE_execute_ddl_triggers(tdbb, transaction, when == DTW_BEFORE, action); + + savePoint.release(); // everything is ok +} + + +void DdlNode::executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, + DdlNode::DdlTriggerWhen when, int action, const MetaName& objectName) +{ + executeDdlTrigger(tdbb, transaction, when, action, objectName, sqlText); +} + + +void DdlNode::checkEmptyName(const MetaName& name) +{ + // ASF: Not passing in DYN, it's better to remove this function and make the scanner doesn't + // recognize empty double-quoted string as identifiers. + if (name.isEmpty()) + status_exception::raise(Arg::Gds(isc_dyn_zero_len_id)); +} + + +MetaName DdlNode::nameInMetaCharSet(thread_db* tdbb, const MetaName& name) +{ + const CHARSET_ID charSet = tdbb->getCharSet(); + if (charSet == CS_METADATA || charSet == CS_NONE) + return name; + + Attachment* attachment = tdbb->getAttachment(); + UCHAR buffer[MAX_SQL_IDENTIFIER_SIZE]; + + ULONG len = INTL_convert_bytes(tdbb, CS_METADATA, buffer, MAX_SQL_IDENTIFIER_SIZE - 1, + charSet, (const BYTE*) name.c_str(), name.length(), ERR_post); + buffer[len] = '\0'; + + return MetaName((const char*) buffer); +} + + +MetaName DdlNode::nameInUserCharSet(thread_db* tdbb, const MetaName& name) +{ + const CHARSET_ID charSet = tdbb->getCharSet(); + if (charSet == CS_METADATA || charSet == CS_NONE) + return name; + + Attachment* attachment = tdbb->getAttachment(); + UCHAR buffer[MAX_SQL_IDENTIFIER_SIZE]; + + ULONG len = INTL_convert_bytes(tdbb, charSet, buffer, MAX_SQL_IDENTIFIER_SIZE - 1, + CS_METADATA, (const BYTE*) name.c_str(), name.length(), ERR_post); + buffer[len] = '\0'; + + return MetaName((const char*) buffer); +} + + +void DdlNode::storeTextBlob(thread_db* tdbb, jrd_tra* transaction, + bid* blobId, const string& text) +{ + Attachment* attachment = tdbb->getAttachment(); + + UCharBuffer bpb; + BLB_gen_bpb(isc_blob_text, isc_blob_text, attachment->att_charset, CS_METADATA, bpb); + + blb* blob = BLB_create2(tdbb, transaction, blobId, bpb.getCount(), bpb.begin()); + try + { + BLB_put_data(tdbb, blob, (const UCHAR*) text.c_str(), text.length()); + } + catch (const Exception&) + { + BLB_close(tdbb, blob); + throw; + } + + BLB_close(tdbb, blob); +} + + +void DdlNode::storeBlob(thread_db* tdbb, jrd_tra* transaction, + bid* blobId, const UCHAR* data, unsigned length) +{ + blb* blob = BLB_create2(tdbb, transaction, blobId, 0, NULL); + try + { + BLB_put_data(tdbb, blob, data, length); + } + catch (const Exception&) + { + BLB_close(tdbb, blob); + throw; + } + + BLB_close(tdbb, blob); +} + + +void DdlNode::putType(const TypeClause& type, bool useSubType) +{ +#ifdef DEV_BUILD + // Check if the field describes a known datatype + if (type.type > FB_NELEM(blr_dtypes) || !blr_dtypes[type.type]) + { + SCHAR buffer[100]; + + sprintf(buffer, "Invalid dtype %d in put_dtype", type.type); + ERRD_bugcheck(buffer); + } +#endif + + if (type.notNull) + compiledStatement->append_uchar(blr_not_nullable); + + if (type.typeOfName.hasData()) + { + if (type.typeOfTable.hasData()) + { + if (type.collateSpecified) + { + compiledStatement->append_uchar(blr_column_name2); + compiledStatement->append_uchar(type.fullDomain ? blr_domain_full : blr_domain_type_of); + compiledStatement->append_meta_string(type.typeOfTable.c_str()); + compiledStatement->append_meta_string(type.typeOfName.c_str()); + compiledStatement->append_ushort(type.textType); + } + else + { + compiledStatement->append_uchar(blr_column_name); + compiledStatement->append_uchar(type.fullDomain ? blr_domain_full : blr_domain_type_of); + compiledStatement->append_meta_string(type.typeOfTable.c_str()); + compiledStatement->append_meta_string(type.typeOfName.c_str()); + } + } + else + { + if (type.collateSpecified) + { + compiledStatement->append_uchar(blr_domain_name2); + compiledStatement->append_uchar(type.fullDomain ? blr_domain_full : blr_domain_type_of); + compiledStatement->append_meta_string(type.typeOfName.c_str()); + compiledStatement->append_ushort(type.textType); + } + else + { + compiledStatement->append_uchar(blr_domain_name); + compiledStatement->append_uchar(type.fullDomain ? blr_domain_full : blr_domain_type_of); + compiledStatement->append_meta_string(type.typeOfName.c_str()); + } + } + + return; + } + + switch (type.type) + { + case dtype_cstring: + case dtype_text: + case dtype_varying: + case dtype_blob: + if (!useSubType) + compiledStatement->append_uchar(blr_dtypes[type.type]); + else if (type.type == dtype_varying) + { + compiledStatement->append_uchar(blr_varying2); + compiledStatement->append_ushort(type.textType); + } + else if (type.type == dtype_cstring) + { + compiledStatement->append_uchar(blr_cstring2); + compiledStatement->append_ushort(type.textType); + } + else if (type.type == dtype_blob) + { + compiledStatement->append_uchar(blr_blob2); + compiledStatement->append_ushort(type.subType); + compiledStatement->append_ushort(type.textType); + } + else + { + compiledStatement->append_uchar(blr_text2); + compiledStatement->append_ushort(type.textType); + } + + if (type.type == dtype_varying) + compiledStatement->append_ushort(type.length - sizeof(USHORT)); + else if (type.type != dtype_blob) + compiledStatement->append_ushort(type.length); + break; + + default: + compiledStatement->append_uchar(blr_dtypes[type.type]); + if (DTYPE_IS_EXACT(type.type) || dtype_quad == type.type) + compiledStatement->append_uchar(type.scale); + break; + } +} + + +void DdlNode::resetContextStack() +{ + compiledStatement->req_context->clear(); + compiledStatement->req_context_number = 0; +} + + +//---------------------- + + +TypeClause::TypeClause(dsql_fld* aLegacyField, dsql_str* aLegacyCollate) + : legacyField(aLegacyField), + legacyCollate(aLegacyCollate) +{ +} + + +void TypeClause::resolve(CompiledStatement* compiledStatement) +{ + DDL_resolve_intl_type(compiledStatement, legacyField, legacyCollate); + + type = legacyField->fld_dtype; + length = legacyField->fld_length; + scale = legacyField->fld_scale; + subType = legacyField->fld_sub_type; + segLength = legacyField->fld_seg_length; + precision = legacyField->fld_precision; + charLength = legacyField->fld_character_length; + charSetId = legacyField->fld_character_set_id; + collationId = legacyField->fld_collation_id; + collateSpecified = legacyCollate != NULL; + textType = legacyField->fld_ttype; + fullDomain = legacyField->fld_full_domain; + notNull = legacyField->fld_not_nullable; + fieldSource = legacyField->fld_source; + + if (legacyField->fld_type_of_table) + typeOfTable = legacyField->fld_type_of_table->str_data; + + typeOfName = legacyField->fld_type_of_name; +} + + +void TypeClause::print(string& text) const +{ + text.printf("typeOfTable: '%s' typeOfName: '%s' notNull: %d fieldSource: '%s'", + typeOfTable.c_str(), typeOfName.c_str(), notNull, fieldSource.c_str()); +} + + +//---------------------- + + +ParameterClause::ParameterClause(dsql_fld* field, dsql_str* collate, dsql_nod* dflt) + : TypeClause(field, collate), + name(field->fld_name), + legacyDefault(dflt) +{ +} + + +void ParameterClause::print(string& text) const +{ + string s; + TypeClause::print(s); + text.printf("name: '%s' %s", name.c_str(), s.c_str()); +} + + +void ParameterClause::fromLegacyParameterList(Array& parameters, dsql_nod* list) +{ + if (list) + { + fb_assert(list->nod_type == Dsql::nod_list); + + for (unsigned i = 0; i < list->nod_count; ++i) + { + dsql_nod* defField = list->nod_arg[i]; + fb_assert(defField->nod_type == Dsql::nod_def_field); + + dsql_fld* field = (dsql_fld*) defField->nod_arg[e_dfl_field]; + dsql_str* collate = (dsql_str*) defField->nod_arg[e_dfl_collate]; + dsql_nod* dflt = defField->nod_arg[e_dfl_default]; + + ParameterClause parameter(field, collate, dflt); + parameters.add(parameter); + } + } +} + + +//---------------------- + + void AlterCharSetNode::print(string& text, Array& /*nodes*/) const { text.printf( - "alter character set\n" - " charSet: %s\n" - " defaultCollation: %s\n", + "AlterCharSetNode\n" + " charSet: %s\n" + " defaultCollation: %s\n", charSet.c_str(), defaultCollation.c_str()); } @@ -54,58 +404,2182 @@ void AlterCharSetNode::execute(thread_db* tdbb, jrd_tra* transaction) } Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, charSet)); bool charSetFound = false; bool collationFound = false; - jrd_req* request1 = CMP_find_request(tdbb, drq_m_charset, DYN_REQUESTS); + AutoCacheRequest requestHandle1(tdbb, drq_m_charset, DYN_REQUESTS); - FOR (REQUEST_HANDLE request1 TRANSACTION_HANDLE transaction) + FOR (REQUEST_HANDLE requestHandle1 TRANSACTION_HANDLE transaction) CS IN RDB$CHARACTER_SETS - WITH CS.RDB$CHARACTER_SET_NAME EQ charSet.c_str() - + WITH CS.RDB$CHARACTER_SET_NAME EQ metaName.c_str() + { charSetFound = true; - if (!DYN_REQUEST(drq_m_charset)) - DYN_REQUEST(drq_m_charset) = request1; + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_CHARACTER_SET, metaName); - jrd_req* request2 = CMP_find_request(tdbb, drq_l_collation, DYN_REQUESTS); + AutoCacheRequest requestHandle2(tdbb, drq_l_collation, DYN_REQUESTS); - FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) COLL IN RDB$COLLATIONS WITH COLL.RDB$CHARACTER_SET_ID EQ CS.RDB$CHARACTER_SET_ID AND - COLL.RDB$COLLATION_NAME EQ defaultCollation.c_str() - - if (!DYN_REQUEST(drq_l_collation)) - DYN_REQUEST(drq_l_collation) = request2; - + COLL.RDB$COLLATION_NAME EQ nameInMetaCharSet(tdbb, defaultCollation).c_str() + { collationFound = true; + } END_FOR - if (!DYN_REQUEST(drq_l_collation)) - DYN_REQUEST(drq_l_collation) = request2; - if (collationFound) { MODIFY CS CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE; - strcpy(CS.RDB$DEFAULT_COLLATE_NAME, defaultCollation.c_str()); + strcpy(CS.RDB$DEFAULT_COLLATE_NAME, nameInMetaCharSet(tdbb, defaultCollation).c_str()); END_MODIFY } + } END_FOR - if (!DYN_REQUEST(drq_m_charset)) - DYN_REQUEST(drq_m_charset) = request1; - if (!charSetFound) - { status_exception::raise(Arg::Gds(isc_charset_not_found) << Arg::Str(charSet)); - } if (!collationFound) { status_exception::raise(Arg::Gds(isc_collation_not_found) << Arg::Str(defaultCollation) << Arg::Str(charSet)); } + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_CHARACTER_SET, metaName); +} + + +//---------------------- + + +void CommentOnNode::print(string& text, Array& nodes) const +{ + text.printf( + "CommentOnNode\n" + " objType: %s\n" + " objName: %s\n" + " text: %s\n", + objType, objName.c_str(), text.c_str()); +} + + +// select rdb$relation_name from rdb$relation_fields where rdb$field_name = 'RDB$DESCRIPTION'; +// gives the list of objects that accept descriptions. At FB2 time, the only +// subobjects with descriptions are relation's fields and procedure's parameters. +void CommentOnNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = transaction->tra_attachment; + Database* dbb = attachment->att_database; + + string table; + string column; + string subColumn; + string addWhere; + Arg::StatusVector status; + + switch (objType) + { + case ddl_database: + table = "rdb$database"; + break; + + case ddl_domain: + table = "rdb$fields"; + column = "rdb$field_name"; + status << Arg::Gds(isc_dyn_domain_not_found); + break; + + case ddl_relation: + if (subName.hasData()) + { + table = "rdb$relation_fields"; + subColumn = "rdb$field_name"; + status << Arg::Gds(isc_dyn_column_does_not_exist) << + Arg::Str(subName) << Arg::Str(objName); + } + else + { + table = "rdb$relations"; + addWhere = "rdb$view_blr is null"; + status << Arg::Gds(isc_dyn_table_not_found) << Arg::Str(objName); + } + column = "rdb$relation_name"; + break; + + case ddl_view: + table = "rdb$relations"; + column = "rdb$relation_name"; + status << Arg::Gds(isc_dyn_view_not_found) << Arg::Str(objName); + addWhere = "rdb$view_blr is not null"; + break; + + case ddl_procedure: + if (subName.hasData()) + { + table = "rdb$procedure_parameters"; + subColumn = "rdb$parameter_name"; + status << Arg::Gds(isc_dyn_proc_param_not_found) << + Arg::Str(subName) << Arg::Str(objName); + } + else + { + table = "rdb$procedures"; + status << Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(objName); + } + + if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_12_0) + addWhere = "rdb$package_name is null"; + + column = "rdb$procedure_name"; + break; + + case ddl_trigger: + table = "rdb$triggers"; + column = "rdb$trigger_name"; + status << Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(objName); + break; + + case ddl_udf: + table = "rdb$functions"; + column = "rdb$function_name"; + + if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_12_0) + addWhere = "rdb$package_name is null"; + + status << Arg::Gds(isc_dyn_func_not_found) << Arg::Str(objName); + break; + + case ddl_blob_filter: + table = "rdb$filters"; + column = "rdb$function_name"; + status << Arg::Gds(isc_dyn_filter_not_found) << Arg::Str(objName); + break; + + case ddl_exception: + table = "rdb$exceptions"; + column = "rdb$exception_name"; + status << Arg::Gds(isc_dyn_exception_not_found) << Arg::Str(objName); + break; + + case ddl_generator: + table = "rdb$generators"; + column = "rdb$generator_name"; + status << Arg::Gds(isc_dyn_gen_not_found) << Arg::Str(objName); + break; + + case ddl_index: + table = "rdb$indices"; + column = "rdb$index_name"; + status << Arg::Gds(isc_dyn_index_not_found) << Arg::Str(objName); + break; + + case ddl_role: + table = "rdb$roles"; + column = "rdb$role_name"; + status << Arg::Gds(isc_dyn_role_not_found) << Arg::Str(objName); + break; + + case ddl_charset: + table = "rdb$character_sets"; + column = "rdb$character_set_name"; + status << Arg::Gds(isc_dyn_charset_not_found) << Arg::Str(objName); + break; + + case ddl_collation: + table = "rdb$collations"; + column = "rdb$collation_name"; + status << Arg::Gds(isc_dyn_collation_not_found) << Arg::Str(objName); + break; + + case ddl_package: + dbb->checkOdsForDsql(ODS_12_0); + + table = "rdb$packages"; + column = "rdb$package_name"; + + //// TODO: localize + status << Arg::Gds(isc_random) << Arg::Str("Package not found") << + Arg::Gds(isc_random) << Arg::Str(objName); + break; + } + + fb_assert(table.hasData()); + + if (table.hasData()) + { + string sqlStmt("update " + table + " set rdb$description = ?"); + + if (column.hasData()) + { + sqlStmt += " where " + column + " = ?"; + + if (subColumn.hasData()) + sqlStmt += " and " + subColumn + " = ?"; + } + + if (addWhere.hasData()) + sqlStmt += " and " + addWhere; + + AutoPtr ps(attachment->prepareStatement(tdbb, *tdbb->getDefaultPool(), + transaction, sqlStmt)); + + int n = 0; + + if (text.isEmpty()) + ++n; + else + ps->setString(tdbb, ++n, text); + + if (column.hasData()) + { + ps->setString(tdbb, ++n, objName); + + if (subColumn.hasData()) + ps->setString(tdbb, ++n, subName); + } + + ps->execute(tdbb, transaction); + + if (ps->getUpdateCount() == 0) + status_exception::raise(status); + } +} + + +//---------------------- + + +void CreateAlterFunctionNode::print(string& text, Array& nodes) const +{ + text.printf( + "CreateAlterFunctionNode\n" + " name: '%s' create: %d alter: %d\n", + name.c_str(), create, alter); + + if (external) + { + string s; + s.printf(" external -> name: '%s' engine: '%s'\n", + external->name.c_str(), external->engine.c_str()); + text += s; + } + + text += " Parameters:\n"; + + for (size_t i = 0; i < parameters.getCount(); ++i) + { + const ParameterClause& parameter = parameters[i]; + + string s; + parameter.print(s); + text += " " + s + "\n"; + } +} + + +void CreateAlterFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + checkEmptyName(name); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + parameters[i].resolve(compiledStatement); + + returnType.resolve(compiledStatement); + + fb_assert(create || alter); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + if (alter) + { + if (!executeAlter(tdbb, transaction)) + { + if (create) // create or alter + executeCreate(tdbb, transaction); + else + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_func_not_found) << + Arg::Str(name)); + } + } + + // Update DSQL cache + AutoPtr str(MAKE_string(name.c_str(), name.length())); + METD_drop_function(compiledStatement, str, package); + MET_dsql_cache_release(tdbb, SYM_udf, str->str_data, package); + } + else + executeCreate(tdbb, transaction); + + savePoint.release(); // everything is ok +} + + +void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_FUNCTION, metaName); + + dbb->checkOdsForDsql(ODS_12_0); + + AutoCacheRequest requestHandle(tdbb, drq_s_funcs2, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + { + FUN.RDB$FUNCTION_TYPE.NULL = TRUE; + FUN.RDB$QUERY_NAME.NULL = TRUE; + FUN.RDB$DESCRIPTION.NULL = TRUE; + FUN.RDB$MODULE_NAME.NULL = TRUE; + FUN.RDB$PACKAGE_NAME.NULL = TRUE; // ODS_12_0 + + FUN.RDB$SYSTEM_FLAG.NULL = FALSE; + FUN.RDB$SYSTEM_FLAG = 0; + + FUN.RDB$FUNCTION_NAME.NULL = FALSE; + strcpy(FUN.RDB$FUNCTION_NAME, metaName.c_str()); + + if (external) + { + FUN.RDB$ENGINE_NAME.NULL = FALSE; + strcpy(FUN.RDB$ENGINE_NAME, nameInMetaCharSet(tdbb, external->engine).c_str()); + + if (external->name.length() >= sizeof(FUN.RDB$ENTRYPOINT)) + { + status_exception::raise( + Arg::Gds(isc_arith_except) << + Arg::Gds(isc_string_truncation)); + } + + FUN.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty(); + strcpy(FUN.RDB$ENTRYPOINT, external->name.c_str()); + } + else + { + FUN.RDB$ENGINE_NAME.NULL = TRUE; + FUN.RDB$ENTRYPOINT.NULL = TRUE; + } + + FUN.RDB$FUNCTION_SOURCE.NULL = !(source.hasData() && (external || package.isEmpty())); + if (!FUN.RDB$FUNCTION_SOURCE.NULL) + storeTextBlob(tdbb, transaction, &FUN.RDB$FUNCTION_SOURCE, source); + + FUN.RDB$RETURN_ARGUMENT.NULL = FALSE; + FUN.RDB$RETURN_ARGUMENT = 0; + + if (package.hasData()) + { + FUN.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(FUN.RDB$PACKAGE_NAME, nameInMetaCharSet(tdbb, package).c_str()); + + FUN.RDB$PRIVATE_FLAG.NULL = FALSE; // ODS_12_0 + FUN.RDB$PRIVATE_FLAG = privateScope; + } + else + FUN.RDB$PRIVATE_FLAG.NULL = TRUE; + } + END_STORE + + storeArgument(tdbb, transaction, 0, returnType, NULL); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + storeArgument(tdbb, transaction, i + 1, parameter, parameter.legacyDefault); + } + + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_FUNCTION, metaName); +} + + +bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + bool modified = false; + + AutoCacheRequest requestHandle(tdbb, drq_m_funcs2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$FUNCTION_NAME EQ metaName.c_str() AND + FUN.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, metaName); + + dbb->checkOdsForDsql(ODS_12_0); + + MODIFY FUN + FUN.RDB$MODULE_NAME.NULL = TRUE; + + if (external) + { + FUN.RDB$ENGINE_NAME.NULL = FALSE; + strcpy(FUN.RDB$ENGINE_NAME, nameInMetaCharSet(tdbb, external->engine).c_str()); + + if (external->name.length() >= sizeof(FUN.RDB$ENTRYPOINT)) + { + status_exception::raise( + Arg::Gds(isc_arith_except) << + Arg::Gds(isc_string_truncation)); + } + + FUN.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty(); + strcpy(FUN.RDB$ENTRYPOINT, external->name.c_str()); + } + else + { + FUN.RDB$ENGINE_NAME.NULL = TRUE; + FUN.RDB$ENTRYPOINT.NULL = TRUE; + } + + FUN.RDB$FUNCTION_SOURCE.NULL = !(source.hasData() && (external || package.isEmpty())); + if (!FUN.RDB$FUNCTION_SOURCE.NULL) + storeTextBlob(tdbb, transaction, &FUN.RDB$FUNCTION_SOURCE, source); + + FUN.RDB$RETURN_ARGUMENT.NULL = FALSE; + FUN.RDB$RETURN_ARGUMENT = 0; + + if (package.hasData()) + { + FUN.RDB$PRIVATE_FLAG.NULL = FALSE; + FUN.RDB$PRIVATE_FLAG = privateScope; + } + else + FUN.RDB$PRIVATE_FLAG.NULL = TRUE; + END_MODIFY + + modified = true; + } + END_FOR + + if (modified) + { + // delete all old parameters and return + + requestHandle.reset(tdbb, drq_e_func_args2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS + WITH ARG.RDB$FUNCTION_NAME EQ metaName.c_str() AND + ARG.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + ERASE ARG; + } + END_FOR + + // and insert the new ones + + storeArgument(tdbb, transaction, 0, returnType, NULL); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + storeArgument(tdbb, transaction, i + 1, parameter, parameter.legacyDefault); + } + + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_FUNCTION, metaName); + } + + return modified; +} + + +void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transaction, + unsigned pos, const TypeClause& parameter, dsql_nod* legacyDefault) +{ + // current limitations caused by rdb$functions structure + + if (parameter.typeOfName.hasData()) + { + status_exception::raise( + Arg::Gds(isc_wish_list) << + Arg::Gds(isc_random) << + Arg::Str("TYPE OF in function parameters or return value")); + } + + if (parameter.collateSpecified) + { + status_exception::raise( + Arg::Gds(isc_wish_list) << + Arg::Gds(isc_random) << + Arg::Str("COLLATE in function parameters or return value")); + } + + if (legacyDefault) + { + status_exception::raise( + Arg::Gds(isc_wish_list) << + Arg::Gds(isc_random) << + Arg::Str("DEFAULT in function parameters")); + } + + Database* dbb = tdbb->getDatabase(); + AutoCacheRequest requestHandle(tdbb, drq_s_func_args2, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS + { + ARG.RDB$FIELD_SCALE.NULL = TRUE; + ARG.RDB$FIELD_SUB_TYPE.NULL = TRUE; + ARG.RDB$CHARACTER_SET_ID.NULL = TRUE; + ARG.RDB$FIELD_PRECISION.NULL = TRUE; + ARG.RDB$CHARACTER_LENGTH.NULL = TRUE; + ARG.RDB$PACKAGE_NAME.NULL = TRUE; // ODS_12_0 + + ARG.RDB$FUNCTION_NAME.NULL = FALSE; + strcpy(ARG.RDB$FUNCTION_NAME, nameInMetaCharSet(tdbb, name).c_str()); + + if (package.hasData()) + { + ARG.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(ARG.RDB$PACKAGE_NAME, nameInMetaCharSet(tdbb, package).c_str()); + } + + ARG.RDB$ARGUMENT_POSITION.NULL = FALSE; + ARG.RDB$ARGUMENT_POSITION = pos; + + ARG.RDB$MECHANISM.NULL = FALSE; + ARG.RDB$MECHANISM = FUN_value; + + ARG.RDB$FIELD_TYPE.NULL = FALSE; + ARG.RDB$FIELD_TYPE = blr_dtypes[parameter.type]; + + ARG.RDB$FIELD_LENGTH.NULL = FALSE; + ARG.RDB$FIELD_LENGTH = parameter.length; + + if (parameter.type == dtype_blob) + { + ARG.RDB$FIELD_SUB_TYPE.NULL = FALSE; + ARG.RDB$FIELD_SUB_TYPE = parameter.subType; + + ARG.RDB$FIELD_SCALE.NULL = FALSE; + ARG.RDB$FIELD_SCALE = 0; + + if (parameter.subType == isc_blob_text) + { + ARG.RDB$CHARACTER_SET_ID.NULL = FALSE; + ARG.RDB$CHARACTER_SET_ID = parameter.charSetId; + } + } + else if (parameter.type <= dtype_any_text) + { + ARG.RDB$FIELD_SUB_TYPE.NULL = FALSE; + ARG.RDB$FIELD_SUB_TYPE = parameter.subType; + + ARG.RDB$FIELD_SCALE.NULL = FALSE; + ARG.RDB$FIELD_SCALE = 0; + + ARG.RDB$FIELD_LENGTH.NULL = FALSE; + + if (parameter.type == dtype_varying) + { + fb_assert(parameter.length <= MAX_SSHORT); + ARG.RDB$FIELD_LENGTH = (SSHORT) (parameter.length - sizeof(USHORT)); + } + else + ARG.RDB$FIELD_LENGTH = parameter.length; + + ARG.RDB$CHARACTER_LENGTH.NULL = FALSE; + ARG.RDB$CHARACTER_LENGTH = parameter.charLength; + + ARG.RDB$CHARACTER_SET_ID.NULL = FALSE; + ARG.RDB$CHARACTER_SET_ID = parameter.charSetId; + } + else + { + ARG.RDB$FIELD_SCALE.NULL = FALSE; + ARG.RDB$FIELD_SCALE = parameter.scale; + + if (DTYPE_IS_EXACT(parameter.type)) + { + ARG.RDB$FIELD_PRECISION.NULL = FALSE; + ARG.RDB$FIELD_PRECISION = parameter.precision; + + ARG.RDB$FIELD_SUB_TYPE.NULL = FALSE; + ARG.RDB$FIELD_SUB_TYPE = parameter.subType; + } + } + } + END_STORE +} + + +//---------------------- + + +void DropFunctionNode::print(string& text, Array& nodes) const +{ + text.printf( + "DropFunctionNode\n" + " name: '%s'\n", + name.c_str()); +} + + +void DropFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + bool found = false; + + AutoCacheRequest requestHandle(tdbb, drq_e_funcs, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$FUNCTION_NAME EQ metaName.c_str() AND + FUN.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + if (!FUN.RDB$SYSTEM_FLAG.NULL && FUN.RDB$SYSTEM_FLAG) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Cannot ALTER or DROP a system function")); + } + + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_FUNCTION, metaName); + + ERASE FUN; + + found = true; + } + END_FOR + + if (!found && !silent) + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_func_not_found) << + Arg::Str(name)); + } + + requestHandle.reset(tdbb, drq_e_func_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS + WITH ARG.RDB$FUNCTION_NAME EQ metaName.c_str() AND + ARG.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + ERASE ARG; + } + END_FOR + + savePoint.release(); // everything is ok + + // Update DSQL cache + AutoPtr str(MAKE_string(name.c_str(), name.length())); + METD_drop_function(compiledStatement, str, package); + MET_dsql_cache_release(tdbb, SYM_udf, str->str_data, package); + + if (found && package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, metaName); +} + + +//---------------------- + + +void ProcedureNode::genReturn() +{ + GEN_return(compiledStatement, getCreateAlterNode()->legacyReturns, false); +} + + +dsql_nod* ProcedureNode::resolveVariable(const dsql_str* varName) +{ + // try to resolve variable name against input and output parameters and local variables + + CreateAlterProcedureNode* node = getCreateAlterNode(); + dsql_nod* varNode; + + if (node->legacyParameters) + { + if (varNode = PASS1_resolve_variable_name(node->legacyParameters, varName)) + return varNode; + } + + if (node->legacyReturns) + { + if (varNode = PASS1_resolve_variable_name(node->legacyReturns, varName)) + return varNode; + } + + if (node->localDeclList) + { + if (varNode = PASS1_resolve_variable_name(node->localDeclList, varName)) + return varNode; + } + + return NULL; +} + + +//---------------------- + + +void CreateAlterProcedureNode::print(string& text, Array& nodes) const +{ + text.printf( + "CreateAlterProcedureNode\n" + " name: '%s' create: %d alter: %d\n", + name.c_str(), create, alter); + + if (external) + { + string s; + s.printf(" external -> name: '%s' engine: '%s'\n", + external->name.c_str(), external->engine.c_str()); + text += s; + } + + text += " Parameters:\n"; + + for (size_t i = 0; i < parameters.getCount(); ++i) + { + const ParameterClause& parameter = parameters[i]; + + string s; + parameter.print(s); + text += " " + s + "\n"; + } + + text += " Returns:\n"; + + for (size_t i = 0; i < returns.getCount(); ++i) + { + const ParameterClause& parameter = returns[i]; + + string s; + parameter.print(s); + text += " " + s + "\n"; + } +} + + +Node* CreateAlterProcedureNode::internalDsqlPass() +{ + compiledStatement->blockNode = this; + compiledStatement->req_flags |= (REQ_block | REQ_procedure); + + const dsql_nod* variables = localDeclList; + if (variables) + { + // insure that variable names do not duplicate parameter names + + const dsql_nod* const* ptr = variables->nod_arg; + for (const dsql_nod* const* const end = ptr + variables->nod_count; ptr < end; ptr++) + { + if ((*ptr)->nod_type == nod_def_field) + { + const dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field]; + DEV_BLKCHK(field, dsql_type_fld); + + const dsql_nod* parameters = legacyParameters; + if (parameters) + { + const dsql_nod* const* ptr2 = parameters->nod_arg; + for (const dsql_nod* const* const end2 = + ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) + { + const dsql_fld* field2 = (dsql_fld*) (*ptr2)->nod_arg[e_dfl_field]; + DEV_BLKCHK(field2, dsql_type_fld); + + if (field->fld_name == field2->fld_name) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << + Arg::Num(-901) << + Arg::Gds(isc_dsql_var_conflict) << + Arg::Str(field->fld_name)); + } + } + } + + parameters = legacyReturns; + if (parameters) + { + const dsql_nod* const* ptr2 = parameters->nod_arg; + for (const dsql_nod* const* const end2 = + ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) + { + const dsql_fld* field2 = (dsql_fld*) (*ptr2)->nod_arg[e_dfl_field]; + DEV_BLKCHK(field2, dsql_type_fld); + + if (field->fld_name == field2->fld_name) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << + Arg::Num(-901) << + Arg::Gds(isc_dsql_var_conflict) << + Arg::Str(field->fld_name)); + } + } + } + } + } + } + + source.ltrim("\n\r\t "); + + // compile default expressions + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + + if (parameter.legacyDefault) + { + parameter.legacyDefault->nod_arg[e_dft_default] = + PASS1_node(compiledStatement, parameter.legacyDefault->nod_arg[e_dft_default]); + } + } + + return DdlNode::internalDsqlPass(); +} + + +void CreateAlterProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + fb_assert(create || alter); + + checkEmptyName(name); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + parameters[i].resolve(compiledStatement); + + for (unsigned i = 0; i < returns.getCount(); ++i) + returns[i].resolve(compiledStatement); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + bool altered = false; + + // first pass + if (alter) + { + if (executeAlter(tdbb, transaction, false, true)) + altered = true; + else + { + if (create) // create or alter + executeCreate(tdbb, transaction); + else + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_proc_not_found) << + Arg::Str(name)); + } + } + } + else + executeCreate(tdbb, transaction); + + compile(tdbb, transaction); + + executeAlter(tdbb, transaction, true, false); // second pass + + if (package.isEmpty()) + { + MetaName metaName(nameInMetaCharSet(tdbb, name)); + executeDdlTrigger(tdbb, transaction, DTW_AFTER, + (altered ? DDL_TRIGGER_ALTER_PROCEDURE : DDL_TRIGGER_CREATE_PROCEDURE), metaName); + } + + savePoint.release(); // everything is ok + + if (alter) + { + // Update DSQL cache + AutoPtr str(MAKE_string(name.c_str(), name.length())); + METD_drop_procedure(compiledStatement, str, package); + MET_dsql_cache_release(tdbb, SYM_procedure, str->str_data, package); + } +} + + +void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + Attachment* attachment = tdbb->getAttachment(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + if (package.isEmpty()) + { + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PROCEDURE, metaName); + + DYN_UTIL_check_unique_name(tdbb, transaction, metaName, obj_procedure); + } + + AutoCacheRequest requestHandle(tdbb, drq_s_prcs2, DYN_REQUESTS); + + int faults = 0; + + while (true) + { + try + { + SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_prc_id, "RDB$PROCEDURES"); + id %= (MAX_SSHORT + 1); + + if (!id) + continue; + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + P IN RDB$PROCEDURES + { + P.RDB$PROCEDURE_ID = id; + P.RDB$SYSTEM_FLAG = 0; + strcpy(P.RDB$PROCEDURE_NAME, metaName.c_str()); + + if (package.hasData()) + { + P.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(P.RDB$PACKAGE_NAME, nameInMetaCharSet(tdbb, package).c_str()); + + P.RDB$PRIVATE_FLAG.NULL = FALSE; + P.RDB$PRIVATE_FLAG = privateScope; + + strcpy(P.RDB$OWNER_NAME, packageOwner.c_str()); + } + else + { + P.RDB$PACKAGE_NAME.NULL = TRUE; + P.RDB$PRIVATE_FLAG.NULL = TRUE; + + strcpy(P.RDB$OWNER_NAME, attachment->att_user->usr_user_name.c_str()); + } + } + END_STORE + + break; + } + catch (const Firebird::status_exception& ex) + { + if (ex.value()[1] != isc_no_dup) + throw; + + if (++faults > MAX_SSHORT) + throw; + + fb_utils::init_status(tdbb->tdbb_status_vector); + } + } + + if (package.isEmpty()) + { + for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++) + { + requestHandle.reset(tdbb, drq_s_prc_usr_prvs, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + X IN RDB$USER_PRIVILEGES + { + strcpy(X.RDB$RELATION_NAME, metaName.c_str()); + strcpy(X.RDB$USER, attachment->att_user->usr_user_name.c_str()); + X.RDB$USER_TYPE = obj_user; + X.RDB$OBJECT_TYPE = obj_procedure; + X.RDB$PRIVILEGE[0] = *p; + X.RDB$PRIVILEGE[1] = 0; + } + END_STORE + } + } + + executeAlter(tdbb, transaction, false, false); +} + + +bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, + bool secondPass, bool runTriggers) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); + bool modified = false; + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + P IN RDB$PROCEDURES + WITH P.RDB$PROCEDURE_NAME EQ metaName.c_str() AND + P.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + if (!P.RDB$SYSTEM_FLAG.NULL && P.RDB$SYSTEM_FLAG) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Cannot ALTER or DROP a system object")); + } + + if (!secondPass && runTriggers && package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, metaName); + + MODIFY P + if (secondPass) + { + P.RDB$PROCEDURE_BLR.NULL = TRUE; + P.RDB$DEBUG_INFO.NULL = TRUE; + P.RDB$PROCEDURE_TYPE.NULL = TRUE; + + P.RDB$PROCEDURE_INPUTS = (USHORT) parameters.getCount(); + P.RDB$PROCEDURE_OUTPUTS = (USHORT) returns.getCount(); + } + else + { + P.RDB$ENGINE_NAME.NULL = TRUE; + P.RDB$ENTRYPOINT.NULL = TRUE; + P.RDB$PROCEDURE_SOURCE.NULL = TRUE; + + P.RDB$VALID_BLR = TRUE; // ODS_11_1 + + P.RDB$PROCEDURE_SOURCE.NULL = !(source.hasData() && (external || package.isEmpty())); + if (!P.RDB$PROCEDURE_SOURCE.NULL) + storeTextBlob(tdbb, transaction, &P.RDB$PROCEDURE_SOURCE, source); + } + + if (external) + { + dbb->checkOdsForDsql(ODS_12_0); + + if (secondPass) + { + // ODS_11_1 + P.RDB$PROCEDURE_TYPE.NULL = FALSE; + P.RDB$PROCEDURE_TYPE = (USHORT) prc_selectable; + } + else + { + // ODS_12_0 + P.RDB$ENGINE_NAME.NULL = FALSE; + strcpy(P.RDB$ENGINE_NAME, nameInMetaCharSet(tdbb, external->engine).c_str()); + + // ODS_12_0 + if (external->name.length() >= sizeof(P.RDB$ENTRYPOINT)) + { + status_exception::raise( + Arg::Gds(isc_arith_except) << + Arg::Gds(isc_string_truncation)); + } + + P.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty(); + strcpy(P.RDB$ENTRYPOINT, external->name.c_str()); + } + } + else if (body) + { + if (secondPass) + { + P.RDB$PROCEDURE_BLR.NULL = FALSE; + storeBlob(tdbb, transaction, &P.RDB$PROCEDURE_BLR, + compiledStatement->req_blr_data.begin(), + compiledStatement->req_blr_data.getCount()); + + P.RDB$DEBUG_INFO.NULL = FALSE; + storeBlob(tdbb, transaction, &P.RDB$DEBUG_INFO, + compiledStatement->req_debug_data.begin(), + compiledStatement->req_debug_data.getCount()); + + // ODS_11_1 + P.RDB$PROCEDURE_TYPE.NULL = FALSE; + P.RDB$PROCEDURE_TYPE = (USHORT) (compiledStatement->req_flags & REQ_selectable ? + prc_selectable : prc_executable); + } + } + END_MODIFY + + modified = true; + } + END_FOR + + if (!secondPass && modified) + { + // delete all old input and output parameters + DropProcedureNode::dropParameters(tdbb, transaction, name, package); + + // and insert the new ones + + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + storeParameter(tdbb, transaction, 0, i, parameter); + } + + for (unsigned i = 0; i < returns.getCount(); ++i) + { + ParameterClause& parameter = returns[i]; + storeParameter(tdbb, transaction, 1, i, parameter); + } + } + + return modified; +} + + +void CreateAlterProcedureNode::storeParameter(thread_db* tdbb, jrd_tra* transaction, + USHORT type, unsigned pos, const ParameterClause& parameter) +{ + checkEmptyName(parameter.name); + + Database* dbb = tdbb->getDatabase(); + AutoCacheRequest requestHandle(tdbb, drq_s_prms4, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS + { + PRM.RDB$PARAMETER_NAME.NULL = FALSE; + strcpy(PRM.RDB$PARAMETER_NAME, nameInMetaCharSet(tdbb, parameter.name).c_str()); + + PRM.RDB$PROCEDURE_NAME.NULL = FALSE; + strcpy(PRM.RDB$PROCEDURE_NAME, nameInMetaCharSet(tdbb, name).c_str()); + + if (package.hasData()) + { + PRM.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(PRM.RDB$PACKAGE_NAME, nameInMetaCharSet(tdbb, package).c_str()); + } + else + PRM.RDB$PACKAGE_NAME.NULL = TRUE; + + PRM.RDB$SYSTEM_FLAG = 0; + PRM.RDB$SYSTEM_FLAG.NULL = FALSE; + + PRM.RDB$PARAMETER_NUMBER.NULL = FALSE; + PRM.RDB$PARAMETER_NUMBER = pos; + + PRM.RDB$PARAMETER_TYPE.NULL = FALSE; + PRM.RDB$PARAMETER_TYPE = type; + + // ODS_11_1 + PRM.RDB$PARAMETER_MECHANISM.NULL = FALSE; + PRM.RDB$PARAMETER_MECHANISM = + (USHORT) (parameter.fullDomain ? prm_mech_normal : prm_mech_type_of); + + if (parameter.notNull) + dbb->checkOdsForDsql(ODS_11_1); + + PRM.RDB$NULL_FLAG.NULL = !parameter.notNull; + PRM.RDB$NULL_FLAG = parameter.notNull; + + if (parameter.typeOfTable.hasData()) + dbb->checkOdsForDsql(ODS_11_2); + + PRM.RDB$RELATION_NAME.NULL = parameter.typeOfTable.isEmpty(); + PRM.RDB$FIELD_NAME.NULL = PRM.RDB$RELATION_NAME.NULL || parameter.typeOfName.isEmpty(); + + PRM.RDB$FIELD_SOURCE.NULL = FALSE; + + if (PRM.RDB$RELATION_NAME.NULL) + { + if (parameter.typeOfName.hasData()) + strcpy(PRM.RDB$FIELD_SOURCE, nameInMetaCharSet(tdbb, parameter.typeOfName).c_str()); + else + { + AutoCacheRequest requestHandle2(tdbb, drq_s_prm_src2, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + PS IN RDB$FIELDS + { + PS.RDB$FIELD_SUB_TYPE.NULL = TRUE; + PS.RDB$FIELD_SCALE.NULL = TRUE; + PS.RDB$CHARACTER_SET_ID.NULL = TRUE; + PS.RDB$FIELD_LENGTH.NULL = TRUE; + PS.RDB$CHARACTER_LENGTH.NULL = TRUE; + PS.RDB$FIELD_PRECISION.NULL = TRUE; + PS.RDB$COLLATION_ID.NULL = TRUE; + PS.RDB$SEGMENT_LENGTH.NULL = TRUE; + + PS.RDB$SYSTEM_FLAG = 0; + + DYN_UTIL_generate_field_name(tdbb, NULL, PS.RDB$FIELD_NAME); + strcpy(PRM.RDB$FIELD_SOURCE, PS.RDB$FIELD_NAME); + + if (parameter.type == dtype_blob) + { + PS.RDB$FIELD_SUB_TYPE.NULL = FALSE; + PS.RDB$FIELD_SUB_TYPE = parameter.subType; + + PS.RDB$FIELD_SCALE.NULL = FALSE; + PS.RDB$FIELD_SCALE = 0; + + if (parameter.subType == isc_blob_text) + { + PS.RDB$CHARACTER_SET_ID.NULL = FALSE; + PS.RDB$CHARACTER_SET_ID = parameter.charSetId; + + PS.RDB$COLLATION_ID.NULL = !parameter.collateSpecified; + PS.RDB$COLLATION_ID = parameter.collationId; + } + + if (parameter.segLength != 0) + { + PS.RDB$SEGMENT_LENGTH.NULL = FALSE; + PS.RDB$SEGMENT_LENGTH = parameter.segLength; + } + } + else if (parameter.type <= dtype_any_text) + { + PS.RDB$FIELD_SUB_TYPE.NULL = FALSE; + PS.RDB$FIELD_SUB_TYPE = parameter.subType; + + PS.RDB$FIELD_SCALE.NULL = FALSE; + PS.RDB$FIELD_SCALE = 0; + + PS.RDB$FIELD_LENGTH.NULL = FALSE; + + if (parameter.type == dtype_varying) + { + fb_assert(parameter.length <= MAX_SSHORT); + PS.RDB$FIELD_LENGTH = (SSHORT) (parameter.length - sizeof(USHORT)); + } + else + PS.RDB$FIELD_LENGTH = parameter.length; + + PS.RDB$CHARACTER_LENGTH.NULL = FALSE; + PS.RDB$CHARACTER_LENGTH = parameter.charLength; + + PS.RDB$CHARACTER_SET_ID.NULL = FALSE; + PS.RDB$CHARACTER_SET_ID = parameter.charSetId; + + PS.RDB$COLLATION_ID.NULL = !parameter.collateSpecified; + PS.RDB$COLLATION_ID = parameter.collationId; + } + else + { + PS.RDB$FIELD_SCALE.NULL = FALSE; + PS.RDB$FIELD_SCALE = parameter.scale; + + if (DTYPE_IS_EXACT(parameter.type)) + { + PS.RDB$FIELD_PRECISION.NULL = FALSE; + PS.RDB$FIELD_PRECISION = parameter.precision; + + PS.RDB$FIELD_SUB_TYPE.NULL = FALSE; + PS.RDB$FIELD_SUB_TYPE = parameter.subType; + } + } + + PS.RDB$FIELD_TYPE = blr_dtypes[parameter.type]; + } + END_STORE + } + } + else + { + strcpy(PRM.RDB$RELATION_NAME, nameInMetaCharSet(tdbb, parameter.typeOfTable).c_str()); + strcpy(PRM.RDB$FIELD_NAME, nameInMetaCharSet(tdbb, parameter.typeOfName).c_str()); + strcpy(PRM.RDB$FIELD_SOURCE, nameInMetaCharSet(tdbb, parameter.fieldSource).c_str()); + } + + // ASF: I moved this block to write defaults on RDB$PROCEDURE_PARAMETERS. + // It was writing in RDB$FIELDS, but that would require special support + // for packaged procedures signature verification. + + PRM.RDB$DEFAULT_VALUE.NULL = !parameter.legacyDefault; + PRM.RDB$DEFAULT_SOURCE.NULL = !parameter.legacyDefault; + + if (parameter.legacyDefault) + { + dsql_str* defaultString = + (dsql_str*) parameter.legacyDefault->nod_arg[e_dft_default_source]; + string defaultSource = string(defaultString->str_data, defaultString->str_length); + storeTextBlob(tdbb, transaction, &PRM.RDB$DEFAULT_SOURCE, defaultSource); + + compiledStatement->req_blr_data.clear(); + + if (compiledStatement->req_flags & REQ_blr_version4) + compiledStatement->append_uchar(blr_version4); + else + compiledStatement->append_uchar(blr_version5); + + GEN_expr(compiledStatement, parameter.legacyDefault->nod_arg[e_dft_default]); + + compiledStatement->append_uchar(blr_eoc); + + storeBlob(tdbb, transaction, &PRM.RDB$DEFAULT_VALUE, + compiledStatement->req_blr_data.begin(), + compiledStatement->req_blr_data.getCount()); + } + } + END_STORE +} + + +void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* transaction) +{ + if (invalid) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Invalid DDL statement")); + } + + if (compiled) + return; + + compiled = true; + invalid = true; + + if (body) + { + compiledStatement->begin_debug(); + compiledStatement->req_blr_data.clear(); + + if (compiledStatement->req_flags & REQ_blr_version4) + compiledStatement->append_uchar(blr_version4); + else + compiledStatement->append_uchar(blr_version5); + + compiledStatement->append_uchar(blr_begin); + + if (parameters.getCount() != 0) + { + compiledStatement->append_uchar(blr_message); + compiledStatement->append_uchar(0); + compiledStatement->append_ushort(2 * parameters.getCount()); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + compiledStatement->put_debug_argument(fb_dbg_arg_input, i, + nameInMetaCharSet(tdbb, parameter.name).c_str()); + putType(parameter, true); + + // add slot for null flag (parameter2) + compiledStatement->append_uchar(blr_short); + compiledStatement->append_uchar(0); + + legacyParameters->nod_arg[i] = MAKE_variable(parameter.legacyField, + parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0); + } + } + + compiledStatement->append_uchar(blr_message); + compiledStatement->append_uchar(1); + compiledStatement->append_ushort(2 * returns.getCount() + 1); + + if (returns.getCount() != 0) + { + for (unsigned i = 0; i < returns.getCount(); ++i) + { + ParameterClause& parameter = returns[i]; + compiledStatement->put_debug_argument(fb_dbg_arg_output, i, + nameInMetaCharSet(tdbb, parameter.name).c_str()); + putType(parameter, true); + + // add slot for null flag (parameter2) + compiledStatement->append_uchar(blr_short); + compiledStatement->append_uchar(0); + + legacyReturns->nod_arg[i] = MAKE_variable(parameter.legacyField, + parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i); + } + } + + // add slot for EOS + compiledStatement->append_uchar(blr_short); + compiledStatement->append_uchar(0); + + if (parameters.getCount() != 0) + { + compiledStatement->append_uchar(blr_receive); + compiledStatement->append_uchar(0); + } + + compiledStatement->append_uchar(blr_begin); + + for (unsigned i = 0; i < parameters.getCount(); ++i) + { + ParameterClause& parameter = parameters[i]; + + if (parameter.fullDomain || parameter.notNull) + { + // ASF: To validate input parameters we need only to read his value. + // Assigning it to null is an easy way to do this. + compiledStatement->append_uchar(blr_assignment); + compiledStatement->append_uchar(blr_parameter2); + compiledStatement->append_uchar(0); // input + compiledStatement->append_ushort(i * 2); + compiledStatement->append_ushort(i * 2 + 1); + compiledStatement->append_uchar(blr_null); + } + } + + if (returns.getCount() != 0) + { + dsql_nod* params = legacyReturns; + dsql_nod** ptr = params->nod_arg; + for (const dsql_nod* const* const end = ptr + params->nod_count; ptr < end; ptr++) + { + dsql_nod* parameter = *ptr; + dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable]; + DDL_put_local_variable(compiledStatement, variable, 0, NULL); + } + } + + // ASF: This is here to not change the old logic (proc_flag) + // of previous calls to PASS1_node and PASS1_statement. + compiledStatement->setPsql(true); + + DDL_put_local_variables(compiledStatement, localDeclList, returns.getCount()); + + compiledStatement->append_uchar(blr_stall); + // put a label before body of procedure, + // so that any EXIT statement can get out + compiledStatement->append_uchar(blr_label); + compiledStatement->append_uchar(0); + compiledStatement->req_loop_level = 0; + compiledStatement->req_cursor_number = 0; + + GEN_statement(compiledStatement, PASS1_statement(compiledStatement, body)); + + compiledStatement->req_type = REQ_DDL; + compiledStatement->append_uchar(blr_end); + GEN_return(compiledStatement, legacyReturns, true); + + compiledStatement->append_uchar(blr_end); + compiledStatement->append_uchar(blr_eoc); + + compiledStatement->end_debug(); + } + + invalid = false; +} + + +//---------------------- + + +void DropProcedureNode::dropParameters(thread_db* tdbb, jrd_tra* transaction, + const Firebird::MetaName& procedureName, const Firebird::MetaName& packageName) +{ + Database* dbb = tdbb->getDatabase(); + AutoCacheRequest requestHandle(tdbb, drq_e_prms2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS + WITH PRM.RDB$PROCEDURE_NAME EQ nameInMetaCharSet(tdbb, procedureName).c_str() AND + PRM.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, packageName).c_str(), '') + { + // get rid of parameters in rdb$fields + if (!PRM.RDB$FIELD_SOURCE.NULL) + { + AutoCacheRequest requestHandle2(tdbb, drq_d_gfields3, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + FLD IN RDB$FIELDS + WITH FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE AND + FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX + { + bool erase = true; + + if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2) + { + AutoCacheRequest requestHandle3(tdbb, drq_d_gfields4, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle3 TRANSACTION_HANDLE transaction) + PRM2 IN RDB$PROCEDURE_PARAMETERS + WITH PRM2.RDB$PROCEDURE_NAME = PRM.RDB$PROCEDURE_NAME AND + PRM2.RDB$PACKAGE_NAME EQUIV + NULLIF(nameInMetaCharSet(tdbb, packageName).c_str(), '') AND + PRM2.RDB$PARAMETER_NAME = PRM.RDB$PARAMETER_NAME + { + if (!PRM2.RDB$RELATION_NAME.NULL && !PRM2.RDB$FIELD_NAME.NULL) + erase = false; + } + END_FOR + } + + if (erase) + ERASE FLD; + } + END_FOR + } + + ERASE PRM; + } + END_FOR +} + + +void DropProcedureNode::print(string& text, Array& nodes) const +{ + text.printf( + "DropProcedureNode\n" + " name: '%s'\n", + name.c_str()); +} + + +Node* DropProcedureNode::internalDsqlPass() +{ + compiledStatement->req_flags |= (REQ_block | REQ_procedure); + return DdlNode::internalDsqlPass(); +} + + +void DropProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + bool found = false; + + dropParameters(tdbb, transaction, metaName, package); + + AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRC IN RDB$PROCEDURES + WITH PRC.RDB$PROCEDURE_NAME EQ metaName.c_str() AND + PRC.RDB$PACKAGE_NAME EQUIV NULLIF(nameInMetaCharSet(tdbb, package).c_str(), '') + { + if (!PRC.RDB$SYSTEM_FLAG.NULL && PRC.RDB$SYSTEM_FLAG) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Cannot ALTER or DROP a system object")); + } + + if (package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PROCEDURE, metaName); + + ERASE PRC; + + if (!PRC.RDB$SECURITY_CLASS.NULL) + DYN_delete_security_class2(transaction, PRC.RDB$SECURITY_CLASS); + + found = true; + } + END_FOR + + if (!found && !silent) + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_proc_not_found) << + Arg::Str(name)); + } + + if (package.isEmpty()) + { + requestHandle.reset(tdbb, drq_e_prc_prvs2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$RELATION_NAME EQ metaName.c_str() + AND PRIV.RDB$OBJECT_TYPE = obj_procedure + { + ERASE PRIV; + } + END_FOR + + requestHandle.reset(tdbb, drq_e_prc_prv2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$USER EQ metaName.c_str() + AND PRIV.RDB$USER_TYPE = obj_procedure + { + ERASE PRIV; + } + END_FOR + } + + savePoint.release(); // everything is ok + + // Update DSQL cache + AutoPtr str(MAKE_string(name.c_str(), name.length())); + METD_drop_procedure(compiledStatement, str, package); + MET_dsql_cache_release(tdbb, SYM_procedure, str->str_data, package); + + if (found && package.isEmpty()) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, metaName); +} + + +//---------------------- + + +void RecreateProcedureNode::print(string& text, Array& nodes) const +{ + text.printf("RecreateProcedureNode\n"); +} + + +Node* RecreateProcedureNode::internalDsqlPass() +{ + createNode->dsqlPass(compiledStatement); + dropNode.dsqlPass(compiledStatement); + return DdlNode::internalDsqlPass(); +} + + +void RecreateProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +dsql_nod* TriggerNode::resolveVariable(const dsql_str* varName) +{ + // try to resolve variable name against local variables + + CreateAlterTriggerNode* node = getCreateAlterNode(); + dsql_nod* varNode; + + if (node->localDeclList) + { + if (varNode = PASS1_resolve_variable_name(node->localDeclList, varName)) + return varNode; + } + + return NULL; +} + + +//---------------------- + + +void CreateAlterTriggerNode::print(string& text, Array& nodes) const +{ + text.printf( + "CreateAlterTriggerNode\n" + " name: '%s' create: %d alter: %d relationName: '%s'\n" + " type: %d, %d active: %d, %d position: %d, %d\n", + name.c_str(), create, alter, relationName.c_str(), + type.specified, type.value, active.specified, active.value, + position.specified, position.value); + + if (external) + { + string s; + s.printf(" external -> name: '%s' engine: '%s'\n", + external->name.c_str(), external->engine.c_str()); + text += s; + } +} + + +Node* CreateAlterTriggerNode::internalDsqlPass() +{ + compiledStatement->blockNode = this; + compiledStatement->req_flags |= (REQ_block | REQ_procedure | REQ_trigger); + + if (type.specified) + { + if (create && // ALTER TRIGGER doesn't accept table name + ((relationName.hasData() && + (type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DML) || + (relationName.isEmpty() && + (type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DB && + (type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DDL))) + { + status_exception::raise( + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_incompatible_trigger_type)); + } + } + + return DdlNode::internalDsqlPass(); +} + + +void CreateAlterTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + fb_assert(create || alter); + + checkEmptyName(name); + + Attachment* attachment = tdbb->getAttachment(); + + if (relationName.isEmpty() && !attachment->locksmith()) + status_exception::raise(Arg::Gds(isc_adm_task_denied)); + + source.ltrim("\n\r\t "); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + if (!create) + { + jrd_req* requestHandle = NULL; + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$TRIGGER_NAME EQ nameInMetaCharSet(tdbb, name).c_str() + { + if (!type.specified && !TRG.RDB$TRIGGER_TYPE.NULL) + type = TRG.RDB$TRIGGER_TYPE; + + if (relationName.isEmpty() && !TRG.RDB$RELATION_NAME.NULL) + relationName = TRG.RDB$RELATION_NAME; + } + END_FOR + + CMP_release(tdbb, requestHandle); + + if (!type.specified) + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_trig_not_found) << + Arg::Str(name)); + } + } + + compile(tdbb, transaction); + + if (alter) + { + if (!executeAlter(tdbb, transaction, true)) + { + if (create) // create or alter + executeCreate(tdbb, transaction); + else + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_trig_not_found) << + Arg::Str(name)); + } + } + } + else + executeCreate(tdbb, transaction); + + savePoint.release(); // everything is ok +} + + +void CreateAlterTriggerNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + Attachment* attachment = tdbb->getAttachment(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TRIGGER, metaName); + + if (type.specified && (type.value & unsigned(TRIGGER_TYPE_MASK)) == unsigned(TRIGGER_TYPE_DDL)) + dbb->checkOdsForDsql(ODS_12_0); + + AutoCacheRequest requestHandle(tdbb, drq_s_triggers2, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + { + TRG.RDB$SYSTEM_FLAG = 0; + TRG.RDB$FLAGS = TRG_sql; // ASF: For FK triggers, TRG_ignore_perm will also be needed. + strcpy(TRG.RDB$TRIGGER_NAME, metaName.c_str()); + + TRG.RDB$RELATION_NAME.NULL = relationName.isEmpty(); + strcpy(TRG.RDB$RELATION_NAME, nameInMetaCharSet(tdbb, relationName).c_str()); + + fb_assert(type.specified); + TRG.RDB$TRIGGER_TYPE = type.value; + + TRG.RDB$TRIGGER_SEQUENCE = (!position.specified ? 0 : position.value); + TRG.RDB$TRIGGER_INACTIVE = (!active.specified ? 0 : (USHORT) !active.value); + } + END_STORE + + executeAlter(tdbb, transaction, false); + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TRIGGER, metaName); +} + + +bool CreateAlterTriggerNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool runTriggers) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + bool modified = false; + + // ASF: Unregistered bug (2.0, 2.1, 2.5, 3.0): CREATE OR ALTER TRIGGER accepts different table + // than one used in already created trigger. + + AutoCacheRequest requestHandle(tdbb, drq_m_trigger2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$TRIGGER_NAME EQ metaName.c_str() + { + if (type.specified && type.value != (FB_UINT64) TRG.RDB$TRIGGER_TYPE && + ((create && relationName.isEmpty()) || TRG.RDB$RELATION_NAME.NULL)) + { + status_exception::raise( + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_db_trigger_type_cant_change)); + } + + if (type.specified && (type.value & unsigned(TRIGGER_TYPE_MASK)) == unsigned(TRIGGER_TYPE_DDL)) + dbb->checkOdsForDsql(ODS_12_0); + + if (!TRG.RDB$SYSTEM_FLAG.NULL) + { + switch (TRG.RDB$SYSTEM_FLAG) + { + case fb_sysflag_check_constraint: + case fb_sysflag_referential_constraint: + case fb_sysflag_view_check: + status_exception::raise(Arg::Gds(isc_dyn_cant_modify_auto_trig)); + break; + + case fb_sysflag_system: + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Cannot ALTER or DROP a system object")); + break; + + default: + break; + } + } + + if (runTriggers) + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TRIGGER, metaName); + + MODIFY TRG + if (body || external) + { + fb_assert(!(body && external)); + + TRG.RDB$ENGINE_NAME.NULL = TRUE; + TRG.RDB$ENTRYPOINT.NULL = TRUE; + TRG.RDB$TRIGGER_SOURCE.NULL = TRUE; + TRG.RDB$TRIGGER_BLR.NULL = TRUE; + TRG.RDB$DEBUG_INFO.NULL = TRUE; + } + + if (type.specified) + TRG.RDB$TRIGGER_TYPE = type.value; + + if (position.specified) + TRG.RDB$TRIGGER_SEQUENCE = position.value; + if (active.specified) + TRG.RDB$TRIGGER_INACTIVE = (USHORT) !active.value; + + if (external) + { + dbb->checkOdsForDsql(ODS_12_0); + + // ODS_12_0 + TRG.RDB$ENGINE_NAME.NULL = FALSE; + strcpy(TRG.RDB$ENGINE_NAME, nameInMetaCharSet(tdbb, external->engine).c_str()); + + // ODS_12_0 + if (external->name.length() >= sizeof(TRG.RDB$ENTRYPOINT)) + { + status_exception::raise( + Arg::Gds(isc_arith_except) << + Arg::Gds(isc_string_truncation)); + } + + TRG.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty(); + strcpy(TRG.RDB$ENTRYPOINT, external->name.c_str()); + } + else if (body) + { + TRG.RDB$TRIGGER_BLR.NULL = FALSE; + storeBlob(tdbb, transaction, &TRG.RDB$TRIGGER_BLR, + compiledStatement->req_blr_data.begin(), + compiledStatement->req_blr_data.getCount()); + + // ODS_11_1 + TRG.RDB$DEBUG_INFO.NULL = FALSE; + storeBlob(tdbb, transaction, &TRG.RDB$DEBUG_INFO, + compiledStatement->req_debug_data.begin(), + compiledStatement->req_debug_data.getCount()); + } + + if (source.hasData()) + { + TRG.RDB$TRIGGER_SOURCE.NULL = FALSE; + storeTextBlob(tdbb, transaction, &TRG.RDB$TRIGGER_SOURCE, source); + } + + TRG.RDB$VALID_BLR = TRUE; // ODS_11_1 + END_MODIFY + + modified = true; + } + END_FOR + + if (modified && runTriggers) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TRIGGER, metaName); + + return modified; +} + + +void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* transaction) +{ + if (invalid) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Invalid DDL statement")); + } + + if (compiled) + return; + + compiled = true; + invalid = true; + + if (body) + { + compiledStatement->begin_debug(); + compiledStatement->req_blr_data.clear(); + + // Create the "OLD" and "NEW" contexts for the trigger -- + // the new one could be a dummy place holder to avoid resolving + // fields to that context but prevent relations referenced in + // the trigger actions from referencing the predefined "1" context. + if (compiledStatement->req_context_number) + resetContextStack(); + + if (relationName.hasData()) + { + dsql_nod* relationNode = FB_NEW_RPT(getPool(), e_rln_count) dsql_nod; + ///trigger_node->nod_arg[e_trg_table] = relationNode; + relationNode->nod_type = nod_relation_name; + relationNode->nod_count = e_rln_count; + relationNode->nod_arg[e_rln_name] = (dsql_nod*) + MAKE_string(relationName.c_str(), relationName.length()); + + dsql_nod* const temp = relationNode->nod_arg[e_rln_alias]; + if (hasOldContext(type.value)) + { + relationNode->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT); + dsql_ctx* oldContext = PASS1_make_context(compiledStatement, relationNode); + oldContext->ctx_flags |= CTX_system; + } + else + compiledStatement->req_context_number++; + + if (hasNewContext(type.value)) + { + relationNode->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT); + dsql_ctx* newContext = PASS1_make_context(compiledStatement, relationNode); + newContext->ctx_flags |= CTX_system; + } + else + compiledStatement->req_context_number++; + + relationNode->nod_arg[e_rln_alias] = temp; + } + + // generate the trigger blr + + if (compiledStatement->req_flags & REQ_blr_version4) + compiledStatement->append_uchar(blr_version4); + else + compiledStatement->append_uchar(blr_version5); + + compiledStatement->append_uchar(blr_begin); + + compiledStatement->setPsql(true); + + DDL_put_local_variables(compiledStatement, localDeclList, 0); + + compiledStatement->req_scope_level++; + // dimitr: I see no reason to deny EXIT command in triggers, + // hence I've added zero label at the beginning. + // My first suspicion regarding an obvious conflict + // with trigger messages (nod_abort) is wrong, + // although the fact that they use the same BLR code + // is still a potential danger and must be fixed. + // Hopefully, system triggers are never recompiled. + compiledStatement->append_uchar(blr_label); + compiledStatement->append_uchar(0); + compiledStatement->req_loop_level = 0; + compiledStatement->req_cursor_number = 0; + GEN_statement(compiledStatement, PASS1_statement(compiledStatement, body)); + compiledStatement->req_scope_level--; + compiledStatement->append_uchar(blr_end); + compiledStatement->append_uchar(blr_eoc); + + compiledStatement->end_debug(); + + // The statement type may have been set incorrectly when parsing + // the trigger actions, so reset it to reflect the fact that this + // is a data definition statement; also reset the ddl node. + compiledStatement->req_type = REQ_DDL; + } + + invalid = false; +} + + +//---------------------- + + +void DropTriggerNode::print(string& text, Array& nodes) const +{ + text.printf( + "DropTriggerNode\n" + " name: '%s'\n", + name.c_str()); +} + + +Node* DropTriggerNode::internalDsqlPass() +{ + compiledStatement->req_flags |= (REQ_block | REQ_procedure | REQ_trigger); + return DdlNode::internalDsqlPass(); +} + + +void DropTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + bool found = false; + MetaName relationName; + AutoCacheRequest requestHandle(tdbb, drq_e_trigger3, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + X IN RDB$TRIGGERS + WITH X.RDB$TRIGGER_NAME EQ metaName.c_str() + { + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_TRIGGER, metaName); + + if (X.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith()) + status_exception::raise(Arg::Gds(isc_adm_task_denied)); + + relationName = X.RDB$RELATION_NAME; + ERASE X; + found = true; + } + END_FOR + + if (!found && !silent) + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_trig_not_found) << + Arg::Str(name)); + } + + requestHandle.reset(tdbb, drq_e_trg_msgs3, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + TM IN RDB$TRIGGER_MESSAGES + WITH TM.RDB$TRIGGER_NAME EQ metaName.c_str() + { + ERASE TM; + } + END_FOR + + requestHandle.reset(tdbb, drq_e_trg_prv2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES + WITH PRIV.RDB$USER EQ metaName.c_str() AND + PRIV.RDB$USER_TYPE = obj_trigger + { + ERASE PRIV; + } + END_FOR + + // Clear the update flags on the fields if this is the last remaining + // trigger that changes a view. + + bool viewFound = false; + requestHandle.reset(tdbb, drq_e_trg_prv3, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FIRST 1 V IN RDB$VIEW_RELATIONS + CROSS F IN RDB$RELATION_FIELDS + CROSS T IN RDB$TRIGGERS + WITH V.RDB$VIEW_NAME EQ relationName.c_str() AND + F.RDB$RELATION_NAME EQ V.RDB$VIEW_NAME AND + F.RDB$RELATION_NAME EQ T.RDB$RELATION_NAME + { + viewFound = true; + } + END_FOR + + if (!viewFound) + { + requestHandle.reset(tdbb, drq_m_rel_flds2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + F IN RDB$RELATION_FIELDS + WITH F.RDB$RELATION_NAME EQ relationName.c_str() + { + MODIFY F USING + F.RDB$UPDATE_FLAG = FALSE; + END_MODIFY + } + END_FOR + } + + if (found) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_TRIGGER, metaName); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +void RecreateTriggerNode::print(string& text, Array& nodes) const +{ + text.printf("RecreateTriggerNode\n"); +} + + +Node* RecreateTriggerNode::internalDsqlPass() +{ + createNode->dsqlPass(compiledStatement); + dropNode.dsqlPass(compiledStatement); + return DdlNode::internalDsqlPass(); +} + + +void RecreateTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); + + savePoint.release(); // everything is ok } diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index b04aee197b..9e20abc7e2 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -25,18 +25,88 @@ #include "../jrd/common.h" #include "../dsql/Nodes.h" +#include "../common/classes/array.h" +#include "../common/classes/TriState.h" namespace Jrd { +class ExternalClause +{ +public: + explicit ExternalClause(MemoryPool& pool) + : name(pool), + engine(pool) + { + } + +public: + Firebird::string name; + Firebird::MetaName engine; +}; + + +class TypeClause +{ +public: + explicit TypeClause(dsql_fld* aField, dsql_str* aCollate); + virtual ~TypeClause() {} + +public: + void resolve(CompiledStatement* compiledStatement); + +public: + virtual void print(Firebird::string& text) const; + +public: + USHORT type; + FLD_LENGTH length; + SSHORT scale; + SSHORT subType; + USHORT segLength; + USHORT precision; + USHORT charLength; + SSHORT charSetId; + SSHORT collationId; + SSHORT textType; + bool collateSpecified; + bool fullDomain; + bool notNull; + Firebird::MetaName fieldSource; + Firebird::MetaName typeOfTable; + Firebird::MetaName typeOfName; + +public: + dsql_fld* legacyField; + dsql_str* legacyCollate; +}; + + +class ParameterClause : public TypeClause +{ +public: + explicit ParameterClause(dsql_fld* field, dsql_str* collate, dsql_nod* dflt); + +public: + void print(Firebird::string& text) const; + +public: + static void fromLegacyParameterList(Firebird::Array& parameters, dsql_nod* list); + +public: + Firebird::MetaName name; + dsql_nod* legacyDefault; +}; + + class AlterCharSetNode : public DdlNode { public: - explicit AlterCharSetNode(MemoryPool& pool, const Firebird::MetaName& aCharSet, - const Firebird::MetaName& aDefaultCollation) - : DdlNode(pool), - charSet(getPool(), aCharSet), - defaultCollation(getPool(), aDefaultCollation) + explicit AlterCharSetNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aCharSet, const Firebird::MetaName& aDefaultCollation) + : DdlNode(pool, sqlText), + charSet(pool, aCharSet), + defaultCollation(pool, aDefaultCollation) { } @@ -50,6 +120,394 @@ private: }; +class CommentOnNode : public DdlNode +{ +public: + explicit CommentOnNode(MemoryPool& pool, const Firebird::string& sqlText, int aObjType, + const Firebird::MetaName& aObjName, const Firebird::MetaName& aSubName, + const Firebird::string& aText) + : DdlNode(pool, sqlText), + objType(aObjType), + objName(pool, aObjName), + subName(pool, aSubName), + text(pool, aText) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +private: + int objType; + Firebird::MetaName objName; + Firebird::MetaName subName; + Firebird::string text; +}; + + +class CreateAlterFunctionNode : public DdlNode +{ +public: + explicit CreateAlterFunctionNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName, const TypeClause& aReturnType) + : DdlNode(pool, sqlText), + name(pool, aName), + returnType(aReturnType), + create(true), + alter(false), + external(NULL), + parameters(pool), + package(pool), + privateScope(false), + source(pool) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +private: + void executeCreate(thread_db* tdbb, jrd_tra* transaction); + bool executeAlter(thread_db* tdbb, jrd_tra* transaction); + + void storeArgument(thread_db* tdbb, jrd_tra* transaction, + unsigned pos, const TypeClause& parameter, dsql_nod* legacyDefault); + +public: + Firebird::MetaName name; + TypeClause returnType; + bool create; + bool alter; + ExternalClause* external; + Firebird::Array parameters; + Firebird::MetaName package; + bool privateScope; + Firebird::string source; +}; + + +class DropFunctionNode : public DdlNode +{ +public: + explicit DropFunctionNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName), + silent(false), + package(pool) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +public: + Firebird::MetaName name; + bool silent; + Firebird::MetaName package; +}; + + +class CreateAlterProcedureNode; + + +class ProcedureNode : public DdlNode, public BlockNode +{ +public: + explicit ProcedureNode(MemoryPool& p, const Firebird::string& sqlText) + : DdlNode(p, sqlText) + { + } + +public: + virtual void genReturn(); + virtual dsql_nod* resolveVariable(const dsql_str* varName); + +protected: + virtual CreateAlterProcedureNode* getCreateAlterNode() = 0; +}; + + +class CreateAlterProcedureNode : public ProcedureNode +{ +public: + explicit CreateAlterProcedureNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : ProcedureNode(pool, sqlText), + name(pool, aName), + create(true), + alter(false), + external(NULL), + parameters(pool), + returns(pool), + legacyParameters(NULL), + legacyReturns(NULL), + source(pool), + localDeclList(NULL), + body(NULL), + compiled(false), + invalid(false), + package(pool), + packageOwner(pool), + privateScope(false) + { + } + +protected: + virtual CreateAlterProcedureNode* getCreateAlterNode() + { + return this; + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + void executeCreate(thread_db* tdbb, jrd_tra* transaction); + bool executeAlter(thread_db* tdbb, jrd_tra* transaction, bool secondPass, bool runTriggers); + void storeParameter(thread_db* tdbb, jrd_tra* transaction, + USHORT type, unsigned pos, const ParameterClause& parameter); + void compile(thread_db* tdbb, jrd_tra* transaction); + +public: + Firebird::MetaName name; + bool create; + bool alter; + ExternalClause* external; + Firebird::Array parameters; + Firebird::Array returns; + dsql_nod* legacyParameters; + dsql_nod* legacyReturns; + Firebird::string source; + dsql_nod* localDeclList; + dsql_nod* body; + bool compiled; + bool invalid; + Firebird::MetaName package; + Firebird::string packageOwner; + bool privateScope; +}; + + +class DropProcedureNode : public DdlNode +{ +public: + explicit DropProcedureNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName), + silent(false), + package(pool) + { + } + +public: + static void dropParameters(thread_db* tdbb, jrd_tra* transaction, + const Firebird::MetaName& procedureName, const Firebird::MetaName& packageName); + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +public: + Firebird::MetaName name; + bool silent; + Firebird::MetaName package; +}; + + +class RecreateProcedureNode : public ProcedureNode +{ +public: + explicit RecreateProcedureNode(MemoryPool& p, const Firebird::string& sqlText, + CreateAlterProcedureNode* aCreateNode) + : ProcedureNode(p, sqlText), + createNode(aCreateNode), + dropNode(p, sqlText, createNode->name) + { + dropNode.silent = true; + } + +protected: + virtual CreateAlterProcedureNode* getCreateAlterNode() + { + return createNode; + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + CreateAlterProcedureNode* createNode; + DropProcedureNode dropNode; +}; + + +class CreateAlterTriggerNode; + + +class TriggerNode : public DdlNode, public BlockNode +{ +public: + explicit TriggerNode(MemoryPool& p, const Firebird::string& sqlText) + : DdlNode(p, sqlText) + { + } + +public: + virtual void genReturn() + { + fb_assert(false); + } + + virtual dsql_nod* resolveVariable(const dsql_str* varName); + +protected: + virtual CreateAlterTriggerNode* getCreateAlterNode() = 0; +}; + + +class CreateAlterTriggerNode : public TriggerNode +{ +public: + explicit CreateAlterTriggerNode(MemoryPool& p, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : TriggerNode(p, sqlText), + name(p, aName), + create(true), + alter(false), + relationName(p), + external(NULL), + source(p), + localDeclList(NULL), + body(NULL), + compiled(false), + invalid(false) + { + } + + +protected: + virtual CreateAlterTriggerNode* getCreateAlterNode() + { + return this; + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + void executeCreate(thread_db* tdbb, jrd_tra* transaction); + bool executeAlter(thread_db* tdbb, jrd_tra* transaction, bool runTriggers); + void compile(thread_db* tdbb, jrd_tra* transaction); + + static inline bool hasOldContext(const unsigned value) + { + const unsigned val1 = ((value + 1) >> 1) & 3; + const unsigned val2 = ((value + 1) >> 3) & 3; + const unsigned val3 = ((value + 1) >> 5) & 3; + return (val1 && val1 != 1) || (val2 && val2 != 1) || (val3 && val3 != 1); + } + + static inline bool hasNewContext(const unsigned value) + { + const unsigned val1 = ((value + 1) >> 1) & 3; + const unsigned val2 = ((value + 1) >> 3) & 3; + const unsigned val3 = ((value + 1) >> 5) & 3; + return (val1 && val1 != 3) || (val2 && val2 != 3) || (val3 && val3 != 3); + } + +public: + Firebird::MetaName name; + bool create; + bool alter; + Firebird::MetaName relationName; + TriStateType type; + TriStateType active; + TriStateType position; + ExternalClause* external; + Firebird::string source; + dsql_nod* localDeclList; + dsql_nod* body; + bool compiled; + bool invalid; +}; + + +class DropTriggerNode : public DdlNode +{ +public: + explicit DropTriggerNode(MemoryPool& p, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(p, sqlText), + name(p, aName), + silent(false) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +public: + Firebird::MetaName name; + bool silent; +}; + + +class RecreateTriggerNode : public TriggerNode +{ +public: + explicit RecreateTriggerNode(MemoryPool& p, const Firebird::string& sqlText, + CreateAlterTriggerNode* aCreateNode) + : TriggerNode(p, sqlText), + createNode(aCreateNode), + dropNode(p, sqlText, createNode->name) + { + dropNode.silent = true; + } + + +protected: + virtual CreateAlterTriggerNode* getCreateAlterNode() + { + return createNode; + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + CreateAlterTriggerNode* createNode; + DropTriggerNode dropNode; +}; + + } // namespace #endif // DSQL_DDL_NODES_H diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 27fb57e58d..591a37b1dc 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -29,6 +29,7 @@ namespace Jrd { class CompilerScratch; +class TypeClause; class Node : public Firebird::PermanentStorage @@ -48,14 +49,14 @@ public: Node* dsqlPass(CompiledStatement* aCompiledStatement) { compiledStatement = aCompiledStatement; - return dsqlPass(); + return internalDsqlPass(); } public: virtual void print(Firebird::string& text, Firebird::Array& nodes) const = 0; protected: - virtual Node* dsqlPass() + virtual Node* internalDsqlPass() { return this; } @@ -68,13 +69,41 @@ protected: class DdlNode : public Node { public: - explicit DdlNode(MemoryPool& pool) - : Node(pool) + explicit DdlNode(MemoryPool& pool, const Firebird::string& aSqlText) + : Node(pool), + sqlText(pool, aSqlText) { } +public: + const Firebird::string& getSqlText() + { + return sqlText; + } + +public: + enum DdlTriggerWhen { DTW_BEFORE, DTW_AFTER }; + static void executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, + DdlTriggerWhen when, int action, const Firebird::MetaName& objectName, + const Firebird::string& sqlText); + +public: + static void checkEmptyName(const Firebird::MetaName& name); + static Firebird::MetaName nameInMetaCharSet(thread_db* tdbb, const Firebird::MetaName& name); + static Firebird::MetaName nameInUserCharSet(thread_db* tdbb, const Firebird::MetaName& name); + static void storeTextBlob(thread_db* tdbb, jrd_tra* transaction, bid* blobId, + const Firebird::string& text); + static void storeBlob(thread_db* tdbb, jrd_tra* transaction, bid* blobId, + const UCHAR* data, unsigned length); + protected: - virtual Node* dsqlPass() + void executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, + DdlTriggerWhen when, int action, const Firebird::MetaName& objectName); + void putType(const TypeClause& type, bool useSubType); + void resetContextStack(); + +protected: + virtual Node* internalDsqlPass() { compiledStatement->req_type = REQ_DDL; return this; @@ -82,6 +111,9 @@ protected: public: virtual void execute(thread_db* tdbb, jrd_tra* transaction) = 0; + +private: + Firebird::string sqlText; }; @@ -89,7 +121,8 @@ class DmlNode : public Node { public: explicit DmlNode(MemoryPool& pool) - : Node(pool) + : Node(pool), + node(NULL) { } @@ -117,6 +150,20 @@ public: }; +// Common node for all "code blocks" (i.e.: procedures, triggers and execute block) +class BlockNode +{ +public: + virtual ~BlockNode() + { + } + +public: + virtual void genReturn() = 0; + virtual dsql_nod* resolveVariable(const dsql_str* varName) = 0; +}; + + } // namespace #endif // DSQL_NODES_H diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp new file mode 100644 index 0000000000..1cc42f018d --- /dev/null +++ b/src/dsql/PackageNodes.epp @@ -0,0 +1,1140 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2009 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/common.h" +#include "../dsql/PackageNodes.h" +#include "../jrd/dyn.h" +#include "../jrd/intl.h" +#include "../jrd/jrd.h" +#include "../jrd/tra.h" +#include "../jrd/dfw_proto.h" +#include "../jrd/dyn_dl_proto.h" +#include "../jrd/exe_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/vio_proto.h" +#include "../dsql/make_proto.h" +#include "../dsql/metd_proto.h" +#include "../dsql/pass1_proto.h" +#include "../common/StatusArg.h" + +using namespace Firebird; + +namespace Jrd { + +using namespace Firebird; +using namespace Dsql; + +DATABASE DB = STATIC "ODS.RDB"; + + +//---------------------- + + +namespace +{ + struct ParameterInfo + { + ParameterInfo(MemoryPool& p) + : type(0), + number(0), + name(p), + fieldSource(p), + fieldName(p), + relationName(p), + mechanism(0) + { + defaultSource.clear(); + defaultValue.clear(); + } + + ParameterInfo(MemoryPool& p, const ParameterInfo& o) + : type(o.type), + number(o.number), + name(p, o.name), + fieldSource(p, o.fieldSource), + fieldName(p, o.fieldName), + relationName(p, o.relationName), + collationId(o.collationId), + nullFlag(o.nullFlag), + mechanism(0), + fieldLength(o.fieldLength), + fieldScale(o.fieldScale), + fieldType(o.fieldType), + fieldSubType(o.fieldSubType), + fieldSegmentLength(o.fieldSegmentLength), + fieldNullFlag(o.fieldNullFlag), + fieldCharLength(o.fieldCharLength), + fieldCollationId(o.fieldCollationId), + fieldCharSetId(o.fieldCharSetId), + fieldPrecision(o.fieldPrecision), + defaultSource(o.defaultSource), + defaultValue(o.defaultValue) + { + } + + SSHORT type; + SSHORT number; + MetaName name; + MetaName fieldSource; + MetaName fieldName; + MetaName relationName; + TriStateType collationId; + TriStateType nullFlag; + SSHORT mechanism; + TriStateType fieldLength; + TriStateType fieldScale; + TriStateType fieldType; + TriStateType fieldSubType; + TriStateType fieldSegmentLength; + TriStateType fieldNullFlag; + TriStateType fieldCharLength; + TriStateType fieldCollationId; + TriStateType fieldCharSetId; + TriStateType fieldPrecision; + + // Not compared + bid defaultSource; + bid defaultValue; + + bool operator >(const ParameterInfo& o) const + { + return type > o.type || (type == o.type && number > o.number); + } + + bool operator ==(const ParameterInfo& o) const + { + return type == o.type && number == o.number && name == o.name && + (fieldSource == o.fieldSource || + (fb_utils::implicit_domain(fieldSource.c_str()) && + fb_utils::implicit_domain(o.fieldSource.c_str()))) && + fieldName == o.fieldName && relationName == o.relationName && + collationId == o.collationId && nullFlag == o.nullFlag && + mechanism == o.mechanism && fieldLength == o.fieldLength && + fieldScale == o.fieldScale && fieldType == o.fieldType && + fieldSubType == o.fieldSubType && fieldSegmentLength == o.fieldSegmentLength && + fieldNullFlag == o.fieldNullFlag && fieldCharLength == o.fieldCharLength && + fieldCollationId == o.fieldCollationId && fieldCharSetId == o.fieldCharSetId && + fieldPrecision == o.fieldPrecision; + } + + bool operator !=(const ParameterInfo& o) const + { + return !(*this == o); + } + }; + + struct Signature + { + Signature(MemoryPool& p, const MetaName& aName) + : name(p, aName), + parameters(p), + defined(false) + { + } + + Signature(const MetaName& aName) + : name(aName), + parameters(*getDefaultMemoryPool()), + defined(false) + { + } + + Signature(MemoryPool& p) + : name(p), + parameters(p), + defined(false) + { + } + + Signature(MemoryPool& p, const Signature& o) + : name(p, o.name), + parameters(p), + defined(o.defined) + { + for (SortedObjectsArray::const_iterator i = o.parameters.begin(); + i != o.parameters.end(); ++i) + { + parameters.add(*i); + } + } + + bool operator >(const Signature& o) const + { + return name > o.name; + } + + bool operator ==(const Signature& o) const + { + if (name != o.name || parameters.getCount() != o.parameters.getCount()) + return false; + + for (SortedObjectsArray::const_iterator i = parameters.begin(), + j = o.parameters.begin(); + i != parameters.end(); ++i, ++j) + { + if (*i != *j) + return false; + } + + return true; + } + + bool operator !=(const Signature& o) const + { + return !(*this == o); + } + + MetaName name; + SortedObjectsArray parameters; + bool defined; + }; + + + // Return function and procedure names (in the user charset) and optionally its details for a + // given package. + void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const MetaName& metaName, + SortedObjectsArray& functions, + SortedObjectsArray& procedures, bool details) + { + Database* dbb = tdbb->getDatabase(); + + AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); + AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$PACKAGE_NAME EQ metaName.c_str() + { + Signature function(DdlNode::nameInUserCharSet(tdbb, FUN.RDB$FUNCTION_NAME)); + function.defined = !FUN.RDB$ENTRYPOINT.NULL; + + if (details) + { + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS + WITH ARG.RDB$PACKAGE_NAME EQ metaName.c_str() AND + ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME + { + ParameterInfo parameter(*getDefaultMemoryPool()); + parameter.number = ARG.RDB$ARGUMENT_POSITION; + parameter.mechanism = ARG.RDB$MECHANISM; + + if (!ARG.RDB$FIELD_LENGTH.NULL) + parameter.fieldLength = ARG.RDB$FIELD_LENGTH; + if (!ARG.RDB$FIELD_SCALE.NULL) + parameter.fieldScale = ARG.RDB$FIELD_SCALE; + if (!ARG.RDB$FIELD_TYPE.NULL) + parameter.fieldType = ARG.RDB$FIELD_TYPE; + if (!ARG.RDB$FIELD_SUB_TYPE.NULL) + parameter.fieldSubType = ARG.RDB$FIELD_SUB_TYPE; + if (!ARG.RDB$CHARACTER_LENGTH.NULL) + parameter.fieldCharLength = ARG.RDB$CHARACTER_LENGTH; + if (!ARG.RDB$CHARACTER_SET_ID.NULL) + parameter.fieldCharSetId = ARG.RDB$CHARACTER_SET_ID; + if (!ARG.RDB$FIELD_PRECISION.NULL) + parameter.fieldPrecision = ARG.RDB$FIELD_PRECISION; + + function.parameters.add(parameter); + } + END_FOR + } + + functions.add(function); + } + END_FOR + + requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); + requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRC IN RDB$PROCEDURES + WITH PRC.RDB$PACKAGE_NAME EQ metaName.c_str() + { + Signature procedure(DdlNode::nameInUserCharSet(tdbb, PRC.RDB$PROCEDURE_NAME)); + procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + + if (details) + { + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS CROSS + FLD IN RDB$FIELDS + WITH PRM.RDB$PACKAGE_NAME EQ metaName.c_str() AND + PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND + FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE + { + ParameterInfo parameter(*getDefaultMemoryPool()); + parameter.type = PRM.RDB$PARAMETER_TYPE; + parameter.number = PRM.RDB$PARAMETER_NUMBER; + parameter.name = DdlNode::nameInUserCharSet(tdbb, PRM.RDB$PARAMETER_NAME); + parameter.fieldSource = DdlNode::nameInUserCharSet(tdbb, PRM.RDB$FIELD_SOURCE); + + if (!PRM.RDB$FIELD_NAME.NULL) + parameter.fieldName = DdlNode::nameInUserCharSet(tdbb, PRM.RDB$FIELD_NAME); + + if (!PRM.RDB$RELATION_NAME.NULL) + { + parameter.relationName = DdlNode::nameInUserCharSet(tdbb, + PRM.RDB$RELATION_NAME); + } + + if (!PRM.RDB$COLLATION_ID.NULL) + parameter.collationId = PRM.RDB$COLLATION_ID; + if (!PRM.RDB$NULL_FLAG.NULL) + parameter.nullFlag = PRM.RDB$NULL_FLAG; + + parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM; + + if (!FLD.RDB$FIELD_LENGTH.NULL) + parameter.fieldLength = FLD.RDB$FIELD_LENGTH; + if (!FLD.RDB$FIELD_SCALE.NULL) + parameter.fieldScale = FLD.RDB$FIELD_SCALE; + if (!FLD.RDB$FIELD_TYPE.NULL) + parameter.fieldType = FLD.RDB$FIELD_TYPE; + if (!FLD.RDB$FIELD_SUB_TYPE.NULL) + parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; + if (!FLD.RDB$SEGMENT_LENGTH.NULL) + parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; + if (!FLD.RDB$NULL_FLAG.NULL) + parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; + if (!FLD.RDB$CHARACTER_LENGTH.NULL) + parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; + if (!FLD.RDB$COLLATION_ID.NULL) + parameter.fieldCollationId = FLD.RDB$COLLATION_ID; + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + parameter.fieldCharSetId = FLD.RDB$CHARACTER_SET_ID; + if (!FLD.RDB$FIELD_PRECISION.NULL) + parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; + + if (!PRM.RDB$DEFAULT_SOURCE.NULL) + parameter.defaultSource = PRM.RDB$DEFAULT_SOURCE; + if (!PRM.RDB$DEFAULT_VALUE.NULL) + parameter.defaultValue = PRM.RDB$DEFAULT_VALUE; + + procedure.parameters.add(parameter); + } + END_FOR + } + + procedures.add(procedure); + } + END_FOR + } +} // namespace + + +//---------------------- + + +void CreateAlterPackageNode::print(string& text, Array& nodes) const +{ + fb_assert(items); + + text.printf( + "CreateAlterPackageNode\n" + " name: '%s' create: %d alter: %d\n" + " Items:\n" + "--------\n", + name.c_str(), create, alter); + + for (unsigned i = 0; i < items->getCount(); ++i) + { + string item; + Array nodes; + + switch ((*items)[i].type) + { + case Item::FUNCTION: + (*items)[i].function->print(item, nodes); + break; + + case Item::PROCEDURE: + (*items)[i].procedure->print(item, nodes); + break; + } + + text += item; + } + + text += "--------\n"; +} + + +Node* CreateAlterPackageNode::internalDsqlPass() +{ + source.ltrim("\n\r\t "); + + // items + for (unsigned i = 0; i < items->getCount(); ++i) + { + CompiledStatement* itemStatement = FB_NEW(getPool()) CompiledStatement(getPool()); + itemStatement->req_dbb = compiledStatement->req_dbb; + itemStatement->req_transaction = compiledStatement->req_transaction; + itemStatement->req_client_dialect = compiledStatement->req_client_dialect; + itemStatement->req_package = name; + itemStatement->req_ddl_node = MAKE_node(nod_class_node, 1); + + switch ((*items)[i].type) + { + case CreateAlterPackageNode::Item::FUNCTION: + functionNames.add((*items)[i].function->name); + (*items)[i].function->alter = true; + (*items)[i].function->package = name; + itemStatement->req_ddl_node->nod_arg[0] = (dsql_nod*) (*items)[i].function; + (*items)[i].function->dsqlPass(itemStatement); + break; + + case CreateAlterPackageNode::Item::PROCEDURE: + procedureNames.add((*items)[i].procedure->name); + (*items)[i].procedure->alter = true; + (*items)[i].procedure->package = name; + itemStatement->req_ddl_node->nod_arg[0] = (dsql_nod*) (*items)[i].procedure; + (*items)[i].procedure->dsqlPass(itemStatement); + break; + } + } + + return DdlNode::internalDsqlPass(); +} + + +void CreateAlterPackageNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + fb_assert(create || alter); + + Database* dbb = tdbb->getDatabase(); + + dbb->checkOdsForDsql(ODS_12_0); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + if (alter) + { + if (!executeAlter(tdbb, transaction)) + { + if (create) // create or alter + executeCreate(tdbb, transaction); + else + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << + Arg::Str("Package not found") << + Arg::Gds(isc_random) << + Arg::Str(name)); + } + } + } + else + executeCreate(tdbb, transaction); + + // items + for (unsigned i = 0; i < items->getCount(); ++i) + { + switch ((*items)[i].type) + { + case Item::FUNCTION: + (*items)[i].function->execute(tdbb, transaction); + break; + + case Item::PROCEDURE: + (*items)[i].procedure->packageOwner = owner; + (*items)[i].procedure->execute(tdbb, transaction); + break; + } + } + + savePoint.release(); // everything is ok +} + + +void CreateAlterPackageNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE, metaName); + + AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES USING + { + PKG.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(PKG.RDB$PACKAGE_NAME, metaName.c_str()); + + PKG.RDB$SYSTEM_FLAG.NULL = FALSE; + PKG.RDB$SYSTEM_FLAG = 0; + + PKG.RDB$OWNER_NAME.NULL = FALSE; + strcpy(PKG.RDB$OWNER_NAME, attachment->att_user->usr_user_name.c_str()); + + PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; + storeTextBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source); + } + END_STORE + + for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++) + { + requestHandle.reset(tdbb, drq_s_pkg_usr_prvs, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + X IN RDB$USER_PRIVILEGES + { + strcpy(X.RDB$RELATION_NAME, metaName.c_str()); + strcpy(X.RDB$USER, attachment->att_user->usr_user_name.c_str()); + X.RDB$USER_TYPE = obj_user; + X.RDB$OBJECT_TYPE = obj_package_header; + X.RDB$PRIVILEGE[0] = *p; + X.RDB$PRIVILEGE[1] = 0; + } + END_STORE; + } + + owner = attachment->att_user->usr_user_name; + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE, metaName); +} + + +bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS); + bool modified = false; + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ metaName.c_str() + { + modified = true; + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, metaName); + + SortedObjectsArray existingFuncs(getPool()); + SortedObjectsArray existingProcs(getPool()); + collectPackagedItems(tdbb, transaction, metaName, existingFuncs, existingProcs, false); + + for (SortedObjectsArray::iterator i = existingFuncs.begin(); + i != existingFuncs.end(); ++i) + { + if (!functionNames.exist(i->name)) + { + DropFunctionNode dropNode(getPool(), "", i->name); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + } + + for (SortedObjectsArray::iterator i = existingProcs.begin(); + i != existingProcs.end(); ++i) + { + if (!procedureNames.exist(i->name)) + { + DropProcedureNode dropNode(getPool(), "", i->name); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + } + + MODIFY PKG + PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; + storeTextBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, getSqlText()); + + PKG.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE; + END_MODIFY + + owner = PKG.RDB$OWNER_NAME; + } + END_FOR + + if (modified) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_PACKAGE, metaName); + + return modified; +} + + +//---------------------- + + +void DropPackageNode::print(string& text, Array& nodes) const +{ + text.printf( + "DropPackageNode\n" + " name: '%s'\n", + name.c_str()); +} + + +void DropPackageNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + dbb->checkOdsForDsql(ODS_12_0); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + bool found = false; + AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ metaName.c_str() + { + found = true; + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE, metaName); + + ERASE PKG; + + if (!PKG.RDB$SECURITY_CLASS.NULL) + DYN_delete_security_class2(transaction, PKG.RDB$SECURITY_CLASS); + + dsc desc; + desc.makeText(metaName.length(), ttype_metadata, + (UCHAR*) const_cast(metaName.c_str())); // safe const_cast + DFW_post_work(transaction, dfw_drop_package_header, &desc, 0); + } + END_FOR + + if (!found && !silent) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << + Arg::Str("Package not found") << + Arg::Gds(isc_random) << + Arg::Str(name)); + } + + SortedObjectsArray existingFuncs(getPool()); + SortedObjectsArray existingProcs(getPool()); + collectPackagedItems(tdbb, transaction, metaName, existingFuncs, existingProcs, false); + + for (SortedObjectsArray::iterator i = existingFuncs.begin(); + i != existingFuncs.end(); ++i) + { + DropFunctionNode dropNode(getPool(), "", i->name); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + + for (SortedObjectsArray::iterator i = existingProcs.begin(); + i != existingProcs.end(); ++i) + { + DropProcedureNode dropNode(getPool(), "", i->name); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + + requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRIV IN RDB$USER_PRIVILEGES + WITH (PRIV.RDB$RELATION_NAME EQ metaName.c_str() AND + PRIV.RDB$OBJECT_TYPE = obj_package_header) OR + (PRIV.RDB$USER EQ metaName.c_str() AND PRIV.RDB$USER_TYPE = obj_package_header) + { + ERASE PRIV; + } + END_FOR + + if (found) + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE, metaName); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +void RecreatePackageNode::print(string& text, Array& nodes) const +{ + text.printf("RecreatePackageNode\n"); +} + + +Node* RecreatePackageNode::internalDsqlPass() +{ + createNode->dsqlPass(compiledStatement); + dropNode.dsqlPass(compiledStatement); + return DdlNode::internalDsqlPass(); +} + + +void RecreatePackageNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +void CreatePackageBodyNode::print(string& text, Array& nodes) const +{ + fb_assert(items); + + text.printf( + "CreatePackageBodyNode\n" + " name: '%s'\n" + " declaredItems:\n" + "--------\n", + name.c_str()); + + if (declaredItems) + { + for (unsigned i = 0; i < declaredItems->getCount(); ++i) + { + string item; + Array nodes; + + switch ((*declaredItems)[i].type) + { + case CreateAlterPackageNode::Item::FUNCTION: + (*declaredItems)[i].function->print(item, nodes); + break; + + case CreateAlterPackageNode::Item::PROCEDURE: + (*declaredItems)[i].procedure->print(item, nodes); + break; + } + + text += item; + } + } + + text += + "--------\n" + " items:\n" + "--------\n"; + + for (unsigned i = 0; i < items->getCount(); ++i) + { + string item; + Array nodes; + + switch ((*items)[i].type) + { + case CreateAlterPackageNode::Item::FUNCTION: + (*items)[i].function->print(item, nodes); + break; + + case CreateAlterPackageNode::Item::PROCEDURE: + (*items)[i].procedure->print(item, nodes); + break; + } + + text += item; + } + + text += "--------\n"; +} + + +Node* CreatePackageBodyNode::internalDsqlPass() +{ + source.ltrim("\n\r\t "); + + // process declaredItems and items + Array* arrays[] = {declaredItems, items}; + + for (unsigned i = 0; i < FB_NELEM(arrays); ++i) + { + if (!arrays[i]) + continue; + + for (unsigned j = 0; j < arrays[i]->getCount(); ++j) + { + CompiledStatement* itemStatement = FB_NEW(getPool()) CompiledStatement(getPool()); + itemStatement->req_dbb = compiledStatement->req_dbb; + itemStatement->req_transaction = compiledStatement->req_transaction; + itemStatement->req_client_dialect = compiledStatement->req_client_dialect; + itemStatement->req_package = name; + itemStatement->req_ddl_node = MAKE_node(nod_class_node, 1); + + switch ((*arrays[i])[j].type) + { + case CreateAlterPackageNode::Item::FUNCTION: + (*arrays[i])[j].function->package = name; + (*arrays[i])[j].function->create = true; + if (arrays[i] == items) + (*arrays[i])[j].function->alter = true; + else + (*arrays[i])[j].function->privateScope = true; + itemStatement->req_ddl_node->nod_arg[0] = (dsql_nod*) (*arrays[i])[j].function; + (*arrays[i])[j].function->dsqlPass(itemStatement); + break; + + case CreateAlterPackageNode::Item::PROCEDURE: + (*arrays[i])[j].procedure->package = name; + (*arrays[i])[j].procedure->create = true; + if (arrays[i] == items) + (*arrays[i])[j].procedure->alter = true; + else + (*arrays[i])[j].procedure->privateScope = true; + itemStatement->req_ddl_node->nod_arg[0] = (dsql_nod*) (*arrays[i])[j].procedure; + (*arrays[i])[j].procedure->dsqlPass(itemStatement); + break; + } + } + } + + return DdlNode::internalDsqlPass(); +} + + +void CreatePackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + dbb->checkOdsForDsql(ODS_12_0); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS); + bool modified = false; + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ metaName.c_str() + { + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE_BODY, metaName); + + MODIFY PKG + storeTextBlob(tdbb, transaction, &PKG.RDB$PACKAGE_BODY_SOURCE, source); + END_MODIFY + + modified = true; + + owner = PKG.RDB$OWNER_NAME; + } + END_FOR + + if (!modified) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << + Arg::Str("Package not found") << + Arg::Gds(isc_random) << + Arg::Str(name)); + } + + SortedObjectsArray existingFuncs(getPool()); + SortedObjectsArray existingProcs(getPool()); + + // process declaredItems and items + Array* arrays[] = {declaredItems, items}; + + for (unsigned i = 0; i < FB_NELEM(arrays); ++i) + { + if (!arrays[i]) + continue; + + if (arrays[i] == items) + collectPackagedItems(tdbb, transaction, metaName, existingFuncs, existingProcs, true); + + for (unsigned j = 0; j < arrays[i]->getCount(); ++j) + { + switch ((*arrays[i])[j].type) + { + case CreateAlterPackageNode::Item::FUNCTION: + (*arrays[i])[j].function->execute(tdbb, transaction); + break; + + case CreateAlterPackageNode::Item::PROCEDURE: + (*arrays[i])[j].procedure->packageOwner = owner; + (*arrays[i])[j].procedure->execute(tdbb, transaction); + break; + } + } + } + + SortedObjectsArray newFuncs(getPool()); + SortedObjectsArray newProcs(getPool()); + collectPackagedItems(tdbb, transaction, metaName, newFuncs, newProcs, true); + + for (SortedObjectsArray::iterator i = existingFuncs.begin(); + i != existingFuncs.end(); ++i) + { + size_t pos; + bool found = newFuncs.find(i->name, pos); + + if (!found || !newFuncs[pos].defined) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << Arg::Str(string("Function ") + i->name.c_str() + + " has not defined on the package body " + name.c_str())); + } + else if (newFuncs[pos] != *i) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << Arg::Str(string("Function ") + i->name.c_str() + + " signature mismatch on package body " + name.c_str())); + } + } + + for (SortedObjectsArray::iterator i = existingProcs.begin(); + i != existingProcs.end(); ++i) + { + size_t pos; + bool found = newProcs.find(i->name, pos); + + if (!found || !newProcs[pos].defined) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << Arg::Str(string("Procedure ") + i->name.c_str() + + " has not defined on the package body " + name.c_str())); + } + else if (newProcs[pos] != *i) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << Arg::Str(string("Procedure ") + i->name.c_str() + + " signature mismatch on package body " + name.c_str())); + } + + for (SortedObjectsArray::iterator j = newProcs[pos].parameters.begin(), + k = i->parameters.begin(); + j != newProcs[pos].parameters.end(); ++j, ++k) + { + if (!j->defaultSource.isEmpty() || !j->defaultValue.isEmpty()) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << Arg::Str("Default values for parameters are " + "allowed only in declaration of packaged procedure")); + } + } + } + + // Lets recreate default of public procedure parameters + + requestHandle.reset(tdbb, drq_m_pkg_prm_defs, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS + WITH PRM.RDB$PACKAGE_NAME EQ metaName.c_str() + { + size_t pos; + if (existingProcs.find(MetaName(PRM.RDB$PROCEDURE_NAME), pos)) + { + const Signature& proc = existingProcs[pos]; + + ParameterInfo param(getPool()); + param.type = PRM.RDB$PARAMETER_TYPE; + param.number = PRM.RDB$PARAMETER_NUMBER; + + if (proc.parameters.find(param, pos)) + { + const ParameterInfo& param = proc.parameters[pos]; + + MODIFY PRM + PRM.RDB$DEFAULT_SOURCE = param.defaultSource; + PRM.RDB$DEFAULT_SOURCE.NULL = param.defaultSource.isEmpty(); + + PRM.RDB$DEFAULT_VALUE = param.defaultValue; + PRM.RDB$DEFAULT_VALUE.NULL = param.defaultValue.isEmpty(); + END_MODIFY + } + } + } + END_FOR + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, metaName); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +void DropPackageBodyNode::print(string& text, Array& nodes) const +{ + text.printf( + "DropPackageBodyNode\n" + " name: '%s'\n", + name.c_str()); +} + + +void DropPackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + MetaName metaName(nameInMetaCharSet(tdbb, name)); + + dbb->checkOdsForDsql(ODS_12_0); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + bool found = false; + AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ metaName.c_str() + { + found = true; + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE_BODY, metaName); + + MODIFY PKG + PKG.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE; + + dsc desc; + desc.makeText(metaName.length(), ttype_metadata, + (UCHAR*) const_cast(metaName.c_str())); // safe const_cast + DFW_post_work(transaction, dfw_drop_package_body, &desc, 0); + END_MODIFY + } + END_FOR + + if (!found) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_random) << + Arg::Str("Package not found") << + Arg::Gds(isc_random) << + Arg::Str(name)); + } + + requestHandle.reset(tdbb, drq_m_pkg_fun, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$PACKAGE_NAME EQ metaName.c_str() + { + if (!FUN.RDB$PRIVATE_FLAG.NULL && FUN.RDB$PRIVATE_FLAG != 0) + { + DropFunctionNode dropNode(getPool(), "", nameInUserCharSet(tdbb, FUN.RDB$FUNCTION_NAME)); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + else + { + MODIFY FUN + FUN.RDB$MODULE_NAME.NULL = TRUE; + FUN.RDB$ENGINE_NAME.NULL = TRUE; + FUN.RDB$ENTRYPOINT.NULL = TRUE; + END_MODIFY + } + } + END_FOR + + requestHandle.reset(tdbb, drq_m_pkg_prc, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRC IN RDB$PROCEDURES + WITH PRC.RDB$PACKAGE_NAME EQ metaName.c_str() + { + if (!PRC.RDB$PRIVATE_FLAG.NULL && PRC.RDB$PRIVATE_FLAG != 0) + { + DropProcedureNode dropNode(getPool(), "", nameInUserCharSet(tdbb, PRC.RDB$PROCEDURE_NAME)); + dropNode.package = name; + dropNode.dsqlPass(compiledStatement); + dropNode.execute(tdbb, transaction); + } + else + { + MODIFY PRC + PRC.RDB$PROCEDURE_TYPE = (SSHORT) prc_legacy; + PRC.RDB$PROCEDURE_BLR.NULL = TRUE; + PRC.RDB$DEBUG_INFO.NULL = TRUE; + PRC.RDB$ENGINE_NAME.NULL = TRUE; + PRC.RDB$ENTRYPOINT.NULL = TRUE; + END_MODIFY + } + } + END_FOR + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, metaName); + + savePoint.release(); // everything is ok +} + + +//---------------------- + + +void RecreatePackageBodyNode::print(string& text, Array& nodes) const +{ + text.printf("RecreatePackageBodyNode\n"); +} + + +Node* RecreatePackageBodyNode::internalDsqlPass() +{ + createNode->dsqlPass(compiledStatement); + dropNode.dsqlPass(compiledStatement); + return DdlNode::internalDsqlPass(); +} + + +void RecreatePackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); + + savePoint.release(); // everything is ok +} + + +} // namespace Jrd diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h new file mode 100644 index 0000000000..69b702e066 --- /dev/null +++ b/src/dsql/PackageNodes.h @@ -0,0 +1,230 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2009 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef DSQL_PACKAGE_NODES_H +#define DSQL_PACKAGE_NODES_H + +#include "../jrd/common.h" +#include "../dsql/DdlNodes.h" +#include "../common/classes/array.h" + +namespace Jrd { + + +class CreateAlterPackageNode : public DdlNode +{ +public: + struct Item + { + static Item create(CreateAlterFunctionNode* function) + { + Item item; + item.type = FUNCTION; + item.function = function; + return item; + } + + static Item create(CreateAlterProcedureNode* procedure) + { + Item item; + item.type = PROCEDURE; + item.procedure = procedure; + return item; + } + + enum + { + FUNCTION, + PROCEDURE + } type; + + union + { + CreateAlterFunctionNode* function; + CreateAlterProcedureNode* procedure; + }; + }; + +public: + explicit CreateAlterPackageNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName), + create(true), + alter(false), + source(pool), + items(NULL), + functionNames(pool), + procedureNames(pool), + owner(pool) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + void executeCreate(thread_db* tdbb, jrd_tra* transaction); + bool executeAlter(thread_db* tdbb, jrd_tra* transaction); + +public: + Firebird::MetaName name; + bool create; + bool alter; + Firebird::string source; + Firebird::Array* items; + Firebird::SortedArray functionNames; + Firebird::SortedArray procedureNames; + +private: + Firebird::string owner; +}; + + +class DropPackageNode : public DdlNode +{ +public: + explicit DropPackageNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName), + silent(false) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +public: + Firebird::MetaName name; + bool silent; +}; + + +class RecreatePackageNode : public DdlNode +{ +public: + explicit RecreatePackageNode(MemoryPool& p, const Firebird::string& sqlText, + CreateAlterPackageNode* aCreateNode) + : DdlNode(p, sqlText), + createNode(aCreateNode), + dropNode(p, sqlText, createNode->name) + { + dropNode.silent = true; + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + CreateAlterPackageNode* createNode; + DropPackageNode dropNode; +}; + + +class CreatePackageBodyNode : public DdlNode +{ +public: + explicit CreatePackageBodyNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName), + source(pool), + declaredItems(NULL), + items(NULL), + owner(pool) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +public: + Firebird::MetaName name; + Firebird::string source; + Firebird::Array* declaredItems; + Firebird::Array* items; + +private: + Firebird::string owner; +}; + + +class DropPackageBodyNode : public DdlNode +{ +public: + explicit DropPackageBodyNode(MemoryPool& pool, const Firebird::string& sqlText, + const Firebird::MetaName& aName) + : DdlNode(pool, sqlText), + name(pool, aName) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +public: + Firebird::MetaName name; +}; + + +class RecreatePackageBodyNode : public DdlNode +{ +public: + explicit RecreatePackageBodyNode(MemoryPool& p, const Firebird::string& sqlText, + CreatePackageBodyNode* aCreateNode) + : DdlNode(p, sqlText), + createNode(aCreateNode), + dropNode(p, sqlText, createNode->name) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual Node* internalDsqlPass(); + +private: + CreatePackageBodyNode* createNode; + DropPackageBodyNode dropNode; +}; + + +} // namespace + +#endif // DSQL_PACKAGE_NODES_H diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index b9f6d66597..23c871176b 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -31,6 +31,7 @@ using namespace Jrd; Parser::Parser(MemoryPool& pool, USHORT aClientDialect, USHORT aDbDialect, USHORT aParserVersion, const TEXT* string, USHORT length, SSHORT characterSet) : PermanentStorage(pool), + compilingText(pool, string, length), client_dialect(aClientDialect), db_dialect(aDbDialect), parser_version(aParserVersion), @@ -89,7 +90,7 @@ Parser::~Parser() } -Parser::YYSTYPE Parser::parse() +dsql_nod* Parser::parse() { if (parseAux() != 0) return NULL; diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index 59f2ad75eb..3aceed19cb 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -27,9 +27,14 @@ #include "../dsql/dsql.h" #include "../dsql/node.h" #include "../dsql/DdlNodes.h" +#include "../dsql/PackageNodes.h" #include "../dsql/StmtNodes.h" +#include "../common/classes/TriState.h" #include "../common/classes/stack.h" +#define _yacc_defines_keywords +#include "../dsql/dsql.tab.h" + namespace Jrd { class dsql_nod; @@ -38,7 +43,6 @@ class Parser : public Firebird::PermanentStorage { private: typedef int Yshort; - typedef dsql_nod* YYSTYPE; typedef int YYPOSN; // user-defined text position type struct yyparsestate @@ -64,7 +68,7 @@ private: // This is, in fact, parser state. Not used in lexer itself dsql_fld* g_field; dsql_fil* g_file; - YYSTYPE g_field_name; + dsql_nod* g_field_name; int dsql_debug; // Actual lexer state begins from here @@ -101,7 +105,7 @@ public: ~Parser(); public: - YYSTYPE parse(); + dsql_nod* parse(); const Firebird::string& getTransformedString() const { @@ -139,14 +143,15 @@ private: void yyerror_detailed(const TEXT* error_string, int yychar, YYSTYPE&, YYPOSN&); const TEXT* lex_position(); - YYSTYPE make_list (YYSTYPE node); - YYSTYPE make_parameter(); - YYSTYPE make_node(Dsql::nod_t type, int count, ...); - YYSTYPE makeClassNode(Node* node); - YYSTYPE make_flag_node(Dsql::nod_t type, SSHORT flag, int count, ...); + dsql_nod* make_list (dsql_nod* node); + dsql_nod* make_parameter(); + dsql_nod* make_node(Dsql::nod_t type, int count, ...); + dsql_nod* makeClassNode(Node* node); + dsql_nod* make_flag_node(Dsql::nod_t type, SSHORT flag, int count, ...); // end - defined in parse.y private: + Firebird::string compilingText; USHORT client_dialect; USHORT db_dialect; USHORT parser_version; @@ -154,7 +159,7 @@ private: Firebird::string transformedString; Firebird::Array introducerMarks; bool stmt_ambiguous; - YYSTYPE DSQL_parse; + dsql_nod* DSQL_parse; // These value/posn are taken from the lexer YYSTYPE yylval; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index fda5d07349..f3e8a8220c 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -21,6 +21,7 @@ #include "firebird.h" #include "../jrd/common.h" #include "../dsql/StmtNodes.h" +#include "../dsql/node.h" #include "../jrd/jrd.h" #include "../jrd/blr.h" #include "../jrd/exe.h" @@ -29,10 +30,13 @@ #include "../jrd/exe_proto.h" #include "../jrd/par_proto.h" #include "../jrd/tra_proto.h" +#include "../dsql/ddl_proto.h" #include "../jrd/vio_proto.h" #include "../dsql/gen_proto.h" +#include "../dsql/make_proto.h" #include "../dsql/pass1_proto.h" +using namespace Firebird; using namespace Jrd; #include "gen/blrtable.h" @@ -65,7 +69,7 @@ DmlNode* DmlNode::pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode) //-------------------- -RegisterNode regInAutonomousTransactionNode(blr_auto_trans); +static RegisterNode regInAutonomousTransactionNode(blr_auto_trans); DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, @@ -82,7 +86,7 @@ DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, } -InAutonomousTransactionNode* InAutonomousTransactionNode::dsqlPass() +InAutonomousTransactionNode* InAutonomousTransactionNode::internalDsqlPass() { const bool autoTrans = compiledStatement->req_flags & REQ_in_auto_trans_block; compiledStatement->req_flags |= REQ_in_auto_trans_block; @@ -98,8 +102,7 @@ InAutonomousTransactionNode* InAutonomousTransactionNode::dsqlPass() } -void InAutonomousTransactionNode::print(Firebird::string& text, - Firebird::Array& nodes) const +void InAutonomousTransactionNode::print(string& text, Array& nodes) const { text = "in autonomous transaction"; nodes.add(dsqlAction); @@ -176,14 +179,14 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) } { // scope - Firebird::AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } // end scope break; case jrd_req::req_unwind: - if (request->req_flags & req_leave) + if (request->req_flags & (req_leave | req_continue_loop)) { try { @@ -201,13 +204,13 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) VIO_verb_cleanup(tdbb, transaction); } - Firebird::AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } catch (...) { - request->req_flags &= ~req_leave; + request->req_flags &= ~(req_leave | req_continue_loop); throw; } } @@ -223,7 +226,7 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_rollback); } - catch (const Firebird::Exception&) + catch (const Exception&) { if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck) { @@ -234,7 +237,7 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) try { - Firebird::AutoSetRestore2 autoNullifyRequest( + AutoSetRestore2 autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); // undo all savepoints up to our one @@ -248,7 +251,7 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) TRA_rollback(tdbb, transaction, false, false); } - catch (const Firebird::Exception&) + catch (const Exception&) { if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck) { @@ -269,4 +272,283 @@ jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) } +//-------------------- + + +ExecBlockNode* ExecBlockNode::internalDsqlPass() +{ + compiledStatement->blockNode = this; + + if (legacyReturns && legacyReturns->nod_count) + compiledStatement->req_type = REQ_SELECT_BLOCK; + else + compiledStatement->req_type = REQ_EXEC_BLOCK; + compiledStatement->req_flags |= REQ_block; + + ExecBlockNode* node = FB_NEW(getPool()) ExecBlockNode(getPool()); + node->compiledStatement = compiledStatement; + + node->legacyParameters = PASS1_node_psql(compiledStatement, legacyParameters, false); + node->legacyReturns = legacyReturns; + + node->localDeclList = localDeclList; + node->body = body; + + const size_t count = node->legacyParameters ? node->legacyParameters->nod_count : 0 + + node->legacyReturns ? node->legacyReturns->nod_count : 0 + + node->localDeclList ? node->localDeclList->nod_count : 0; + + if (count) + { + StrArray names(*getDefaultMemoryPool(), count); + + PASS1_check_unique_fields_names(names, node->legacyParameters); + PASS1_check_unique_fields_names(names, node->legacyReturns); + PASS1_check_unique_fields_names(names, node->localDeclList); + } + + return node; +} + + +void ExecBlockNode::print(string& text, Array& nodes) const +{ + text = "execute block"; + nodes.add(legacyParameters); + nodes.add(legacyReturns); + nodes.add(localDeclList); + nodes.add(body); +} + + +void ExecBlockNode::genBlr() +{ + // Update blockNode, because we have a reference to the original unprocessed node. + compiledStatement->blockNode = this; + + compiledStatement->begin_debug(); + + SSHORT inputs = 0, outputs = 0, locals = 0; + dsql_nod* parameters; + + // now do the input parameters + if (parameters = legacyParameters) + { + SSHORT position = 0; + + dsql_nod** ptr = parameters->nod_arg; + for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) + { + dsql_nod* parameter = (*ptr)->nod_arg[Dsql::e_prm_val_fld]; + dsql_fld* field = (dsql_fld*) parameter->nod_arg[Dsql::e_dfl_field]; + // parameter = (*ptr)->nod_arg[Dsql::e_prm_val_val]; USELESS + + DDL_resolve_intl_type(compiledStatement, field, + reinterpret_cast(parameter->nod_arg[Dsql::e_dfl_collate])); + + *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_input, 0, + (USHORT) (2 * position), locals); + // ASF: do not increment locals here - CORE-2341 + position++; + } + inputs = position; + } + + // now do the output parameters + if (parameters = legacyReturns) + { + SSHORT position = 0; + dsql_nod** ptr = parameters->nod_arg; + for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ++ptr) + { + dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[Dsql::e_dfl_field]; + + DDL_resolve_intl_type(compiledStatement, field, + reinterpret_cast((*ptr)->nod_arg[Dsql::e_dfl_collate])); + + *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_output, 1, + (USHORT) (2 * position), locals++); + position++; + } + outputs = position; + } + + compiledStatement->append_uchar(blr_begin); + + if (inputs) { + compiledStatement->req_send->msg_parameters = + revertParametersOrder(compiledStatement->req_send->msg_parameters, NULL); + GEN_port(compiledStatement, compiledStatement->req_send); + } + else + compiledStatement->req_send = NULL; + + if (outputs) + { + SSHORT position = 0; + parameters = legacyReturns; + + dsql_nod** ptr = parameters->nod_arg; + for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) + { + dsql_par* param = MAKE_parameter(compiledStatement->req_receive, true, true, ++position, *ptr); + param->par_node = *ptr; + MAKE_desc(compiledStatement, ¶m->par_desc, *ptr, NULL); + param->par_desc.dsc_flags |= DSC_nullable; + } + } + + // Set up parameter to handle EOF + dsql_par* param = MAKE_parameter(compiledStatement->req_receive, false, false, 0, NULL); + compiledStatement->req_eof = param; + param->par_desc.dsc_dtype = dtype_short; + param->par_desc.dsc_scale = 0; + param->par_desc.dsc_length = sizeof(SSHORT); + + compiledStatement->req_receive->msg_parameters = + revertParametersOrder(compiledStatement->req_receive->msg_parameters, NULL); + GEN_port(compiledStatement, compiledStatement->req_receive); + + if (inputs) { + compiledStatement->append_uchar(blr_receive); + compiledStatement->append_uchar(0); + } + + compiledStatement->append_uchar(blr_begin); + + if (parameters = legacyParameters) + { + dsql_nod** ptr = parameters->nod_arg; + for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) + { + const dsql_nod* parameter = *ptr; + const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable]; + const dsql_fld* field = variable->var_field; + + if (field->fld_full_domain || field->fld_not_nullable) + { + // ASF: Validation of execute block input parameters is different than procedure + // parameters, because we can't generate messages using the domains due to the + // connection charset influence. So to validate, we cast them and assign to null. + compiledStatement->append_uchar(blr_assignment); + compiledStatement->append_uchar(blr_cast); + DDL_put_field_dtype(compiledStatement, field, true); + compiledStatement->append_uchar(blr_parameter2); + compiledStatement->append_uchar(0); + compiledStatement->append_ushort(variable->var_msg_item); + compiledStatement->append_ushort(variable->var_msg_item + 1); + compiledStatement->append_uchar(blr_null); + } + } + } + + if (outputs) + { + parameters = legacyReturns; + dsql_nod** ptr = parameters->nod_arg; + for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) + { + dsql_nod* parameter = *ptr; + dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable]; + DDL_put_local_variable(compiledStatement, variable, 0, NULL); + } + } + + compiledStatement->setPsql(true); + + DDL_put_local_variables(compiledStatement, localDeclList, locals); + + compiledStatement->req_loop_level = 0; + + dsql_nod* stmtNode = PASS1_statement(compiledStatement, body); + GEN_hidden_variables(compiledStatement, false); + + compiledStatement->append_uchar(blr_stall); + // Put a label before body of procedure, so that + // any exit statement can get out + compiledStatement->append_uchar(blr_label); + compiledStatement->append_uchar(0); + GEN_statement(compiledStatement, stmtNode); + if (outputs) + compiledStatement->req_type = REQ_SELECT_BLOCK; + else + compiledStatement->req_type = REQ_EXEC_BLOCK; + compiledStatement->append_uchar(blr_end); + GEN_return(compiledStatement, legacyReturns, true); + compiledStatement->append_uchar(blr_end); + + compiledStatement->end_debug(); +} + + +ExecBlockNode* ExecBlockNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + fb_assert(false); + return this; +} + + +ExecBlockNode* ExecBlockNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + fb_assert(false); + return this; +} + + +jrd_nod* ExecBlockNode::execute(thread_db* tdbb, jrd_req* request) +{ + fb_assert(false); + return NULL; +} + + +void ExecBlockNode::genReturn() +{ + GEN_return(compiledStatement, legacyReturns, false); +} + + +dsql_nod* ExecBlockNode::resolveVariable(const dsql_str* varName) +{ + // try to resolve variable name against input and output parameters and local variables + + dsql_nod* varNode; + + if (localDeclList) + { + if (varNode = PASS1_resolve_variable_name(localDeclList, varName)) + return varNode; + } + + if (legacyParameters) + { + if (varNode = PASS1_resolve_variable_name(legacyParameters, varName)) + return varNode; + } + + if (legacyReturns) + { + if (varNode = PASS1_resolve_variable_name(legacyReturns, varName)) + return varNode; + } + + return NULL; +} + + +// Revert parameters order for EXECUTE BLOCK statement +dsql_par* ExecBlockNode::revertParametersOrder(dsql_par* parameter, dsql_par* prev) +{ + dsql_par* result; + + if (parameter->par_next) + result = revertParametersOrder(parameter->par_next, parameter); + else + result = parameter; + parameter->par_next = prev; + + return result; +} + + } // namespace Jrd diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 9e247f56f9..8f64108eec 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -44,7 +44,7 @@ public: static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb); protected: - virtual InAutonomousTransactionNode* dsqlPass(); + virtual InAutonomousTransactionNode* internalDsqlPass(); public: virtual void print(Firebird::string& text, Firebird::Array& nodes) const; @@ -60,6 +60,43 @@ public: }; +class ExecBlockNode : public StmtNode, public BlockNode +{ +public: + explicit ExecBlockNode(MemoryPool& pool) + : StmtNode(pool), + legacyParameters(NULL), + legacyReturns(NULL), + localDeclList(NULL), + body(NULL) + { + } + +protected: + virtual ExecBlockNode* internalDsqlPass(); + +public: + virtual void print(Firebird::string& text, Firebird::Array& nodes) const; + virtual void genBlr(); + virtual ExecBlockNode* pass1(thread_db* tdbb, CompilerScratch* csb); + virtual ExecBlockNode* pass2(thread_db* tdbb, CompilerScratch* csb); + virtual jrd_nod* execute(thread_db* tdbb, jrd_req* request); + +public: + virtual void genReturn(); + virtual dsql_nod* resolveVariable(const dsql_str* varName); + +private: + static dsql_par* revertParametersOrder(dsql_par* parameter, dsql_par* prev); + +public: + dsql_nod* legacyParameters; + dsql_nod* legacyReturns; + dsql_nod* localDeclList; + dsql_nod* body; +}; + + } // namespace #endif // DSQL_STMT_NODES_H diff --git a/src/dsql/blob.epp b/src/dsql/blob.epp index fcb3b62e1b..fe7b2ba69e 100644 --- a/src/dsql/blob.epp +++ b/src/dsql/blob.epp @@ -179,19 +179,20 @@ ISC_STATUS API_ROUTINE isc_blob_lookup_desc(ISC_STATUS* status, handle = 0; FOR (REQUEST_HANDLE handle TRANSACTION_HANDLE *trans_handle) - X IN RDB$PROCEDURE_PARAMETERS CROSS Y IN RDB$FIELDS + X IN RDB$PROCEDURE_PARAMETERS + CROSS Y IN RDB$FIELDS WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND - X.RDB$PROCEDURE_NAME EQ desc->blob_desc_relation_name AND - X.RDB$PARAMETER_NAME EQ desc->blob_desc_field_name + X.RDB$PROCEDURE_NAME EQ desc->blob_desc_relation_name AND + X.RDB$PACKAGE_NAME MISSING AND + X.RDB$PARAMETER_NAME EQ desc->blob_desc_field_name flag = true; desc->blob_desc_subtype = Y.RDB$FIELD_SUB_TYPE; desc->blob_desc_charset = Y.RDB$CHARACTER_SET_ID; desc->blob_desc_segment_size = Y.RDB$SEGMENT_LENGTH; - if (global) { + if (global) copy_exact_name((UCHAR*) Y.RDB$FIELD_NAME, global, sizeof(Y.RDB$FIELD_NAME)); - } END_FOR ON_ERROR ISC_STATUS_ARRAY temp_status; diff --git a/src/dsql/btyacc_fb.ske b/src/dsql/btyacc_fb.ske index c69b1f81e7..1bb9747119 100644 --- a/src/dsql/btyacc_fb.ske +++ b/src/dsql/btyacc_fb.ske @@ -15,6 +15,11 @@ #include #include #include +#include "../dsql/Nodes.h" +#include "../dsql/DdlNodes.h" +#include "../dsql/PackageNodes.h" +#include "../dsql/StmtNodes.h" +#include "../common/classes/TriState.h" #include "dsql.tab.h" #include "Parser.h" diff --git a/src/dsql/ddl.cpp b/src/dsql/ddl.cpp index 34e264569a..6caf824c1c 100644 --- a/src/dsql/ddl.cpp +++ b/src/dsql/ddl.cpp @@ -93,6 +93,7 @@ #include "../jrd/thread_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/jrd_proto.h" +#include "../jrd/vio_proto.h" #include "../jrd/why_proto.h" #include "../common/utils_proto.h" #include "../dsql/DdlNodes.h" @@ -135,7 +136,6 @@ static void define_index(CompiledStatement*); #ifdef NOT_USED_OR_REPLACED static dsql_nod* define_insert_action(CompiledStatement*); #endif -static void define_procedure(CompiledStatement*, NOD_TYPE); static void define_rel_constraint(CompiledStatement*, dsql_nod*); static void define_relation(CompiledStatement*); static void define_set_null_trg(CompiledStatement*, const dsql_nod*, const dsql_nod*, @@ -143,7 +143,6 @@ static void define_set_null_trg(CompiledStatement*, const dsql_nod*, const dsql_ static void define_set_default_trg(CompiledStatement*, const dsql_nod*, const dsql_nod*, const dsql_nod*, const char*, const char*, bool); static void define_shadow(CompiledStatement*); -static void define_trigger(CompiledStatement*, NOD_TYPE); static void define_udf(CompiledStatement*); static void define_update_action(CompiledStatement*, dsql_nod**, dsql_nod**, dsql_nod*); static void define_upd_cascade_trg(CompiledStatement*, const dsql_nod*, const dsql_nod*, @@ -152,16 +151,13 @@ static void define_view(CompiledStatement*, NOD_TYPE); static void define_view_trigger(CompiledStatement*, dsql_nod*, dsql_nod*, dsql_nod*); static void delete_collation(CompiledStatement*); static void delete_exception(CompiledStatement*, dsql_nod*, bool); -static void delete_procedure(CompiledStatement*, dsql_nod*, bool); static void delete_relation_view(CompiledStatement*, dsql_nod*, bool); -static void delete_trigger(CompiledStatement*, dsql_nod*, bool); static const dsql_nod* find_pk_columns(const dsql_nod* def_rel_elements); static ULONG find_start_of_body(const dsql_str* string); static void fix_default_source(dsql_str* string); static void foreign_key(CompiledStatement*, dsql_nod*, const char* index_name); static void generate_dyn(CompiledStatement*, dsql_nod*); static void grant_revoke(CompiledStatement*); -static void make_comment(CompiledStatement*); static void make_index(CompiledStatement*, const dsql_nod*, const dsql_nod*, const char*); static void make_index_trg_ref_int(CompiledStatement*, dsql_nod*, dsql_nod*, dsql_nod*, const char*, const char*); @@ -178,14 +174,10 @@ static char modify_privileges(CompiledStatement*, NOD_TYPE, SSHORT, const dsql_n static void modify_relation(CompiledStatement*); static void modify_udf(CompiledStatement*); static void modify_map(CompiledStatement*); -static dsql_par* parameter_reverse_order(dsql_par* parameter, dsql_par* prev); static void process_role_nm_list(CompiledStatement*, SSHORT, const dsql_nod*, const dsql_nod*, NOD_TYPE, const dsql_nod*); static void put_descriptor(CompiledStatement*, const dsc*); static void put_dtype(CompiledStatement*, const dsql_fld*, bool); static void put_field(CompiledStatement*, dsql_fld*, bool); -static void put_local_variable(CompiledStatement*, dsql_var*, dsql_nod*, const dsql_str*); -static void put_local_variables(CompiledStatement*, dsql_nod*, SSHORT); -static void put_msg_field(CompiledStatement*, const dsql_fld*); static dsql_nod* replace_field_names(dsql_nod*, dsql_nod*, dsql_nod*, bool, const char*); static void reset_context_stack(CompiledStatement*); static void save_field(CompiledStatement*, const SCHAR*); @@ -215,30 +207,6 @@ const int DEFAULT_BUFFER = 2048; const int DEFAULT_BLOB_SEGMENT_SIZE = 80; // bytes -static const USHORT blr_dtypes[] = -{ - 0, - blr_text, // dtype_text - blr_cstring, // dtype_cstring - blr_varying, // dtype_varying - 0, - 0, - 0, // dtype_packed - 0, // dtype_byte - blr_short, // dtype_short - blr_long, // dtype_long - blr_quad, // dtype_quad - blr_float, // dtype_real - blr_double, // dtype_double - blr_double, // dtype_d_float - blr_sql_date, // dtype_sql_date - blr_sql_time, // dtype_sql_time - blr_timestamp, // dtype_timestamp - blr_blob, // dtype_blob // ASF: CAST use blr_blob2 because blr_blob doesn't fit in UCHAR - blr_short, // dtype_array - blr_int64 // dtype_int64 -}; - static const UCHAR nonnull_validation_blr[] = { blr_version5, @@ -248,22 +216,6 @@ static const UCHAR nonnull_validation_blr[] = blr_eoc }; -static inline bool hasOldContext(const int value) -{ - const int val1 = ((value + 1) >> 1) & 3; - const int val2 = ((value + 1) >> 3) & 3; - const int val3 = ((value + 1) >> 5) & 3; - return (val1 && val1 != 1) || (val2 && val2 != 1) || (val3 && val3 != 1); -} - -static inline bool hasNewContext(const int value) -{ - const int val1 = ((value + 1) >> 1) & 3; - const int val2 = ((value + 1) >> 3) & 3; - const int val3 = ((value + 1) >> 5) & 3; - return (val1 && val1 != 3) || (val2 && val2 != 3) || (val3 && val3 != 3); -} - void CompiledStatement::append_raw_string(const char* string, USHORT len) { @@ -342,16 +294,6 @@ void DDL_execute(dsql_req* request) METD_drop_relation(request, string); break; - case nod_mod_procedure: - case nod_del_procedure: - case nod_replace_procedure: - case nod_redef_procedure: - // for delete & modify, get rid of the cached procedure metadata - string = (dsql_str*) request->req_ddl_node->nod_arg[e_prc_name]; - sym_type = SYM_procedure; - METD_drop_procedure(request, string); - break; - case nod_del_collation: // for delete, get rid of the cached collation metadata string = (dsql_str*) request->req_ddl_node->nod_arg[e_del_coll_name]; @@ -364,7 +306,7 @@ void DDL_execute(dsql_req* request) // Signal UDF for obsolescence string = (dsql_str*) request->req_ddl_node->nod_arg[e_udf_name]; sym_type = SYM_udf; - METD_drop_function (request, string); + METD_drop_function (request, string, ""); break; } @@ -373,14 +315,23 @@ void DDL_execute(dsql_req* request) if (type == nod_class_node) { - reinterpret_cast(request->req_ddl_node->nod_arg[0])->execute(tdbb, - request->req_transaction); + // run all statements under savepoint control + { // scope + AutoSavePoint savePoint(tdbb, request->req_transaction); + + reinterpret_cast(request->req_ddl_node->nod_arg[0])->execute(tdbb, + request->req_transaction); + + savePoint.release(); // everything is ok + } + JRD_autocommit_ddl(tdbb, request->req_transaction); } else { JRD_ddl(tdbb, /*request->req_dbb->dbb_attachment,*/ request->req_transaction, - request->req_blr_data.getCount(), request->req_blr_data.begin()); + request->req_blr_data.getCount(), request->req_blr_data.begin(), + *request->req_sql_text); } } @@ -405,9 +356,13 @@ void DDL_generate(CompiledStatement* statement, dsql_nod* node) return; } - statement->append_uchar(isc_dyn_version_1); + if (node->nod_type != nod_class_node) + statement->append_uchar(isc_dyn_version_1); + generate_dyn(statement, node); - statement->append_uchar(isc_dyn_eoc); + + if (node->nod_type != nod_class_node) + statement->append_uchar(isc_dyn_eoc); } @@ -505,12 +460,7 @@ void DDL_resolve_intl_type2(CompiledStatement* statement, if (field->fld_type_of_name.hasData()) { - if (ENCODE_ODS(statement->req_dbb->dbb_ods_version, statement->req_dbb->dbb_minor_version) < ODS_11_1) - { - // Feature not supported on ODS version older than %d.%d - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) << - Arg::Gds(isc_dsql_feature_not_supported_ods) << Arg::Num(11) << Arg::Num(1)); - } + statement->req_dbb->dbb_database->checkOdsForDsql(ODS_11_1); if (field->fld_type_of_table) { @@ -2320,459 +2270,6 @@ static void define_index(CompiledStatement* statement) } -static void define_procedure(CompiledStatement* statement, NOD_TYPE op) -{ -/************************************** - * - * d e f i n e _ p r o c e d u r e - * - ************************************** - * - * Function - * Create DYN to store a procedure - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - - SSHORT inputs = 0, defaults = 0; - SSHORT outputs = 0; - SSHORT locals = 0; - const dsql_nod* procedure_node = statement->req_ddl_node; - const dsql_str* procedure_name = (dsql_str*) procedure_node->nod_arg[e_prc_name]; - - switch (op) - { - case nod_replace_procedure: - if (METD_get_procedure(statement, procedure_name)) - define_procedure(statement, nod_mod_procedure); - else - define_procedure(statement, nod_def_procedure); - return; - - case nod_def_procedure: - case nod_redef_procedure: - statement->append_cstring(isc_dyn_def_procedure, procedure_name->str_data); - statement->append_number(isc_dyn_rel_sql_protection, 1); - break; - - default: // op == nod_mod_procedure - { - statement->append_cstring(isc_dyn_mod_procedure, procedure_name->str_data); - const dsql_prc* procedure = METD_get_procedure(statement, procedure_name); - if (procedure) - { - const dsql_fld* field; - for (field = procedure->prc_inputs; field; field = field->fld_next) - { - statement->append_string(isc_dyn_delete_parameter, field->fld_name); - statement->append_uchar(isc_dyn_end); - } - for (field = procedure->prc_outputs; field; field = field->fld_next) - { - statement->append_string(isc_dyn_delete_parameter, field->fld_name); - statement->append_uchar(isc_dyn_end); - } - } - } - } - - statement->begin_debug(); - - const dsql_str* source = (dsql_str*) procedure_node->nod_arg[e_prc_source]; - if (source) - { - fb_assert(source->str_length <= MAX_USHORT); - const ULONG j = find_start_of_body(source); - if (j < source->str_length) - { - statement->append_string(isc_dyn_prc_source, source->str_data + j, source->str_length - j); - } - } - - // fill req_procedure to allow procedure to self reference - - MemoryPool& pool = *tdbb->getDefaultPool(); - dsql_prc* procedure = FB_NEW(pool) dsql_prc(pool); - procedure->prc_name = procedure_name->str_data; - statement->req_procedure = procedure; - - // now do the input parameters - - dsql_fld** field_ptr = &procedure->prc_inputs; - - dsql_nod* parameters = procedure_node->nod_arg[e_prc_inputs]; - if (parameters) - { - SSHORT position = 0; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - dsql_nod* parameter = *ptr; - dsql_fld* field = (dsql_fld*) parameter->nod_arg[e_dfl_field]; - - statement->append_string(isc_dyn_def_parameter, field->fld_name); - statement->append_number(isc_dyn_prm_number, position); - statement->append_number(isc_dyn_prm_type, 0); - - DDL_resolve_intl_type(statement, field, - reinterpret_cast(parameter->nod_arg[e_dfl_collate])); - put_field(statement, field, false); - - statement->put_debug_argument(fb_dbg_arg_input, position, field->fld_name.c_str()); - - // check for a parameter default value - dsql_nod* node = parameter->nod_arg[e_dfl_default]; - if (node) - { - define_default(statement, node); - defaults++; - } - else if (defaults) { - // parameter without default value after parameters with default - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << - Arg::Gds(isc_bad_default_value) << - Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last")); - } - - *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_input, 0, - (USHORT) (2 * position), locals); - // put the field in a field list which will be stored to allow - // procedure self referencing - *field_ptr = field; - field_ptr = &field->fld_next; - position++; - - statement->append_uchar(isc_dyn_end); - statement->append_number(isc_dyn_prc_inputs, position); - } - inputs = position; - } - - // terminate the input list - - *field_ptr = NULL; - - // now do the output parameters - - field_ptr = &procedure->prc_outputs; - - if (parameters = procedure_node->nod_arg[e_prc_outputs]) - { - SSHORT position = 0; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ++ptr) - { - dsql_nod* parameter = *ptr; - dsql_fld* field = (dsql_fld*) parameter->nod_arg[e_dfl_field]; - statement->append_string(isc_dyn_def_parameter, field->fld_name); - statement->append_number(isc_dyn_prm_number, position); - statement->append_number(isc_dyn_prm_type, 1); - DDL_resolve_intl_type(statement, field, - reinterpret_cast(parameter->nod_arg[e_dfl_collate])); - put_field(statement, field, false); - - statement->put_debug_argument(fb_dbg_arg_output, position, field->fld_name.c_str()); - - *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_output, 1, - (USHORT) (2 * position), locals); - *field_ptr = field; - field_ptr = &field->fld_next; - position++; - locals++; - - statement->append_uchar(isc_dyn_end); - statement->append_number(isc_dyn_prc_outputs, position); - } - outputs = position; - } - - *field_ptr = NULL; - procedure->prc_out_count = outputs; - procedure->prc_in_count = inputs; - procedure->prc_def_count = defaults; - - statement->begin_blr(isc_dyn_prc_blr); - statement->append_uchar(blr_begin); - if (inputs) - { - statement->append_uchar(blr_message); - statement->append_uchar(0); - statement->append_ushort(2 * inputs); - parameters = procedure_node->nod_arg[e_prc_inputs]; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - const dsql_nod* parameter = *ptr; - const dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - const dsql_fld* field = variable->var_field; - put_msg_field(statement, field); - } - } - statement->append_uchar(blr_message); - statement->append_uchar(1); - statement->append_ushort(2 * outputs + 1); - if (outputs) - { - parameters = procedure_node->nod_arg[e_prc_outputs]; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - const dsql_nod* parameter = *ptr; - const dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - const dsql_fld* field = variable->var_field; - put_msg_field(statement, field); - } - } - - // add slot for EOS - statement->append_uchar(blr_short); - statement->append_uchar(0); - - if (inputs) - { - statement->append_uchar(blr_receive); - statement->append_uchar(0); - } - - statement->append_uchar(blr_begin); - - if (inputs) - { - parameters = procedure_node->nod_arg[e_prc_inputs]; - const dsql_nod* const* ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - const dsql_nod* parameter = *ptr; - const dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - const dsql_fld* field = variable->var_field; - - if (field->fld_full_domain || field->fld_not_nullable) - { - // ASF: To validate input parameters we need only to read his value. - // Assigning it to null is an easy way to do this. - statement->append_uchar(blr_assignment); - statement->append_uchar(blr_parameter2); - statement->append_uchar(variable->var_msg_number); - statement->append_ushort(variable->var_msg_item); - statement->append_ushort(variable->var_msg_item + 1); - statement->append_uchar(blr_null); - } - } - } - - if (outputs) - { - parameters = procedure_node->nod_arg[e_prc_outputs]; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - dsql_nod* parameter = *ptr; - dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - put_local_variable(statement, variable, 0, NULL); - } - } - - // ASF: This is here to not change the old logic (proc_flag) - // of previous calls to PASS1_node and PASS1_statement. - statement->setPsql(true); - - put_local_variables(statement, procedure_node->nod_arg[e_prc_dcls], locals); - - statement->req_loop_level = 0; - statement->req_cursor_number = 0; - - dsql_nod* stmtNode = PASS1_statement(statement, procedure_node->nod_arg[e_prc_body]); - GEN_hidden_variables(statement, false); - - statement->append_uchar(blr_stall); - // put a label before body of procedure, - // so that any EXIT statement can get out - statement->append_uchar(blr_label); - statement->append_uchar(0); - GEN_statement(statement, stmtNode); - statement->req_type = REQ_DDL; - statement->append_uchar(blr_end); - GEN_return(statement, procedure_node->nod_arg[e_prc_outputs], true); - statement->append_uchar(blr_end); - statement->end_blr(); - - const UCHAR prc_type = (statement->req_flags & REQ_selectable) ? - isc_dyn_prc_t_selectable : isc_dyn_prc_t_executable; - statement->append_number(isc_dyn_prc_type, prc_type); - - statement->append_debug_info(); - statement->append_uchar(isc_dyn_end); -} - - -void DDL_gen_block(CompiledStatement* statement, dsql_nod* node) -{ -/************************************** - * - * D D L _ g e n _ b l o c k - * - ************************************** - * - * Function - * Generate BLR for EXECUTE BLOCK statement - * - **************************************/ - SSHORT inputs = 0, outputs = 0, locals = 0; - statement->req_blk_node = node; - statement->begin_debug(); - - dsql_nod* parameters; - - // now do the input parameters - if (parameters = node->nod_arg[e_exe_blk_inputs]) - { - SSHORT position = 0; - - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - dsql_nod* parameter = (*ptr)->nod_arg[e_prm_val_fld]; - dsql_fld* field = (dsql_fld*) parameter->nod_arg[e_dfl_field]; - // parameter = (*ptr)->nod_arg[e_prm_val_val]; USELESS - - DDL_resolve_intl_type(statement, field, - reinterpret_cast(parameter->nod_arg[e_dfl_collate])); - - *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_input, 0, - (USHORT) (2 * position), locals); - // ASF: do not increment locals here - CORE-2341 - position++; - } - inputs = position; - } - - // now do the output parameters - if (parameters = node->nod_arg[e_exe_blk_outputs]) - { - SSHORT position = 0; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ++ptr) - { - dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field]; - - DDL_resolve_intl_type(statement, field, - reinterpret_cast((*ptr)->nod_arg[e_dfl_collate])); - - *ptr = MAKE_variable(field, field->fld_name.c_str(), VAR_output, 1, - (USHORT) (2 * position), locals++); - position++; - } - outputs = position; - } - - statement->append_uchar(blr_begin); - - if (inputs) { - statement->req_send->msg_parameters = - parameter_reverse_order(statement->req_send->msg_parameters, NULL); - GEN_port(statement, statement->req_send); - } - else - statement->req_send = NULL; - - if (outputs) - { - SSHORT position = 0; - parameters = node->nod_arg[e_exe_blk_outputs]; - - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - dsql_par* param = MAKE_parameter(statement->req_receive, true, true, ++position, *ptr); - param->par_node = *ptr; - MAKE_desc(statement, ¶m->par_desc, *ptr, NULL); - param->par_desc.dsc_flags |= DSC_nullable; - } - } - - // Set up parameter to handle EOF - dsql_par* param = MAKE_parameter(statement->req_receive, false, false, 0, NULL); - statement->req_eof = param; - param->par_desc.dsc_dtype = dtype_short; - param->par_desc.dsc_scale = 0; - param->par_desc.dsc_length = sizeof(SSHORT); - - statement->req_receive->msg_parameters = - parameter_reverse_order(statement->req_receive->msg_parameters, NULL); - GEN_port(statement, statement->req_receive); - - if (inputs) { - statement->append_uchar(blr_receive); - statement->append_uchar(0); - } - - statement->append_uchar(blr_begin); - - if (parameters = node->nod_arg[e_exe_blk_inputs]) - { - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - const dsql_nod* parameter = *ptr; - const dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - const dsql_fld* field = variable->var_field; - - if (field->fld_full_domain || field->fld_not_nullable) - { - // ASF: Validation of execute block input parameters is different than procedure - // parameters, because we can't generate messages using the domains due to the - // connection charset influence. So to validate, we cast them and assign to null. - statement->append_uchar(blr_assignment); - statement->append_uchar(blr_cast); - put_dtype(statement, field, true); - statement->append_uchar(blr_parameter2); - statement->append_uchar(0); - statement->append_ushort(variable->var_msg_item); - statement->append_ushort(variable->var_msg_item + 1); - statement->append_uchar(blr_null); - } - } - } - - if (outputs) - { - parameters = node->nod_arg[e_exe_blk_outputs]; - dsql_nod** ptr = parameters->nod_arg; - for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) - { - dsql_nod* parameter = *ptr; - dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; - put_local_variable(statement, variable, 0, NULL); - } - } - - statement->setPsql(true); - - put_local_variables(statement, node->nod_arg[e_exe_blk_dcls], locals); - - statement->req_loop_level = 0; - - dsql_nod* stmtNode = PASS1_statement(statement, node->nod_arg[e_exe_blk_body]); - GEN_hidden_variables(statement, false); - - statement->append_uchar(blr_stall); - // Put a label before body of procedure, so that - // any exit statement can get out - statement->append_uchar(blr_label); - statement->append_uchar(0); - GEN_statement(statement, stmtNode); - if (outputs) - statement->req_type = REQ_SELECT_BLOCK; - else - statement->req_type = REQ_EXEC_BLOCK; - statement->append_uchar(blr_end); - GEN_return(statement, node->nod_arg[e_exe_blk_outputs], true); - statement->append_uchar(blr_end); - - statement->end_debug(); -} - - // ***************************************** // d e f i n e _ r e l _ c o n s t r a i n t // ***************************************** @@ -3014,201 +2511,6 @@ static void define_shadow(CompiledStatement* statement) } -// -// Create the ddl to define or alter a trigger. -// -static void define_trigger(CompiledStatement* statement, NOD_TYPE op) -{ - thread_db* tdbb = JRD_get_thread_data(); - - dsql_nod* trigger_node = statement->req_ddl_node; - const dsql_str* trigger_name = (dsql_str*) trigger_node->nod_arg[e_trg_name]; - - USHORT trig_type; - dsql_nod* relation_node = NULL; - dsql_nod* type_node = trigger_node->nod_arg[e_trg_type]; - - switch (op) - { - case nod_replace_trigger: - if (METD_get_trigger(statement, trigger_name, NULL, &trig_type)) - define_trigger(statement, nod_mod_trigger); - else - define_trigger(statement, nod_def_trigger); - return; - - case nod_def_trigger: - case nod_redef_trigger: - fb_assert(trigger_name->str_length <= MAX_USHORT); - statement->append_string(isc_dyn_def_trigger, trigger_name->str_data, trigger_name->str_length); - relation_node = trigger_node->nod_arg[e_trg_table]; - if (relation_node) - { - if (type_node && (type_node->getSlong() & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) - { - ERRD_post(Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_incompatible_trigger_type)); - } - - const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name]; - fb_assert(relation_name->str_length <= MAX_USHORT); - statement->append_string(isc_dyn_rel_name, relation_name->str_data, relation_name->str_length); - } - else - { - if (type_node && (type_node->getSlong() & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DB) - { - ERRD_post(Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_incompatible_trigger_type)); - } - } - - statement->append_uchar(isc_dyn_sql_object); - break; - - default: // nod_mod_trigger - fb_assert(op == nod_mod_trigger); - fb_assert(trigger_name->str_length <= MAX_USHORT); - statement->append_string(isc_dyn_mod_trigger, trigger_name->str_data, trigger_name->str_length); - if (trigger_node->nod_arg[e_trg_actions]) - { - // Since we will be updating the body of the trigger, we need - // to know what relation the trigger relates to. - - dsql_str* relation_name = NULL; - bool found = METD_get_trigger(statement, trigger_name, &relation_name, &trig_type); - - if (found && relation_name) - { - if (type_node && (type_node->getSlong() & TRIGGER_TYPE_MASK) != TRIGGER_TYPE_DML) - { - ERRD_post(Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_incompatible_trigger_type)); - } - - relation_node = FB_NEW_RPT(*tdbb->getDefaultPool(), e_rln_count) dsql_nod; - trigger_node->nod_arg[e_trg_table] = relation_node; - relation_node->nod_type = nod_relation_name; - relation_node->nod_count = e_rln_count; - // Warning: implicit const cast - relation_node->nod_arg[e_rln_name] = (dsql_nod*) relation_name; - } - else if (found && type_node && (USHORT) type_node->getSlong() != trig_type) - { - ERRD_post(Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_dsql_db_trigger_type_cant_change)); - } - } - } - - statement->begin_debug(); - - const dsql_str* source = (dsql_str*) trigger_node->nod_arg[e_trg_source]; - dsql_nod* actions = (trigger_node->nod_arg[e_trg_actions]) ? - trigger_node->nod_arg[e_trg_actions]->nod_arg[e_trg_act_body] : NULL; - - if (source && actions) - { - fb_assert(source->str_length <= MAX_USHORT); - const ULONG j = find_start_of_body(source); - if (j < source->str_length) - { - statement->append_string(isc_dyn_trg_source, source->str_data + j, source->str_length - j); - } - } - - dsql_nod* constant = trigger_node->nod_arg[e_trg_active]; - if (constant) - statement->append_number(isc_dyn_trg_inactive, (SSHORT) constant->getSlong()); - - if (constant = trigger_node->nod_arg[e_trg_position]) - statement->append_number(isc_dyn_trg_sequence, (SSHORT) constant->getSlong()); - - if (constant = trigger_node->nod_arg[e_trg_type]) { - statement->append_number(isc_dyn_trg_type, (SSHORT) constant->getSlong()); - trig_type = (USHORT) constant->getSlong(); - } - else { - fb_assert(op == nod_mod_trigger); - } - - if (actions) - { - // create the "OLD" and "NEW" contexts for the trigger -- - // the new one could be a dummy place holder to avoid resolving - // fields to that context but prevent relations referenced in - // the trigger actions from referencing the predefined "1" context - - reset_context_stack(statement); - - if (relation_node) - { - dsql_nod* const temp = relation_node->nod_arg[e_rln_alias]; - if (hasOldContext(trig_type)) - { - relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT); - dsql_ctx* oldContext = PASS1_make_context(statement, relation_node); - oldContext->ctx_flags |= CTX_system; - } - else - { - statement->req_context_number++; - } - - if (hasNewContext(trig_type)) - { - relation_node->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT); - dsql_ctx* newContext = PASS1_make_context(statement, relation_node); - newContext->ctx_flags |= CTX_system; - } - else - { - statement->req_context_number++; - } - - relation_node->nod_arg[e_rln_alias] = temp; - } - - // generate the trigger blr - - statement->begin_blr(isc_dyn_trg_blr); - statement->append_uchar(blr_begin); - - statement->setPsql(true); - - put_local_variables(statement, trigger_node->nod_arg[e_trg_actions]->nod_arg[e_trg_act_dcls], 0); - - statement->req_scope_level++; - statement->req_loop_level = 0; - statement->req_cursor_number = 0; - actions = PASS1_statement(statement, actions); - // dimitr: I see no reason to deny EXIT command in triggers, - // hence I've added zero label at the beginning. - // My first suspicion regarding an obvious conflict - // with trigger messages (nod_abort) is wrong, - // although the fact that they use the same BLR code - // is still a potential danger and must be fixed. - // Hopefully, system triggers are never recompiled. - statement->append_uchar(blr_label); - statement->append_uchar(0); - GEN_hidden_variables(statement, false); - GEN_statement(statement, actions); - statement->req_scope_level--; - statement->append_uchar(blr_end); - statement->end_blr(); - - // the statement type may have been set incorrectly when parsing - // the trigger actions, so reset it to reflect the fact that this - // is a data definition statement; also reset the ddl node - - statement->req_type = REQ_DDL; - } - - statement->append_debug_info(); - statement->append_uchar(isc_dyn_end); -} - - static void define_udf(CompiledStatement* statement) { /************************************** @@ -3228,10 +2530,13 @@ static void define_udf(CompiledStatement* statement) dsql_nod** ptr = udf_node->nod_arg; const char* udf_name = ((dsql_str*) (ptr[e_udf_name]))->str_data; const dsql_str* func_entry_point_name = reinterpret_cast(ptr[e_udf_entry_pt]); - const dsql_str* func_module_name = reinterpret_cast(ptr[e_udf_module]); + const dsql_str* func_module_name = reinterpret_cast(ptr[e_udf_module]); + statement->append_cstring(isc_dyn_def_function, udf_name); statement->append_cstring(isc_dyn_func_entry_point, func_entry_point_name->str_data); - statement->append_cstring(isc_dyn_func_module_name, func_module_name->str_data); + + if (func_module_name) + statement->append_cstring(isc_dyn_func_module_name, func_module_name->str_data); dsql_nod** ret_val_ptr = ptr[e_udf_return_value]->nod_arg; @@ -3686,7 +2991,7 @@ static void define_view(CompiledStatement* statement, NOD_TYPE op) } */ - const MetaName& name = relation ? relation->rel_name : procedure->prc_name; + const MetaName& name = relation ? relation->rel_name : procedure->prc_name.identifier; statement->append_string(isc_dyn_view_relation, name); statement->append_number(isc_dyn_view_context, context->ctx_context); @@ -4165,33 +3470,6 @@ static void delete_exception (CompiledStatement* statement, dsql_nod* node, bool } -static void delete_procedure (CompiledStatement* statement, dsql_nod* node, bool silent_deletion) -{ -/************************************** - * - * d e l e t e _ p r o c e d u r e - * - ************************************** - * - * Function - * Do nothing and don't throw error if the procedure doesn't exist - * and silent_deletion is true. - * CVC: Created this function to not clutter generate_dyn(). - * - **************************************/ - const dsql_str* string = (dsql_str*) node->nod_arg[e_prc_name]; - fb_assert (string); - if (node->nod_type == nod_redef_procedure || silent_deletion) { - dsql_prc* procedure = METD_get_procedure (statement, string); - if (!procedure) { - return; - } - } - statement->append_cstring(isc_dyn_delete_procedure, string->str_data); - statement->append_uchar(isc_dyn_end); -} - - static void delete_relation_view (CompiledStatement* statement, dsql_nod* node, bool silent_deletion) { /************************************** @@ -4244,31 +3522,6 @@ static void delete_relation_view (CompiledStatement* statement, dsql_nod* node, } -static void delete_trigger(CompiledStatement* statement, dsql_nod* node, bool silent_deletion) -{ -/************************************** - * - * d e l e t e _ t r i g g e r - * - ************************************** - * - * Function - * Do nothing and don't throw error if the trigger doesn't exist - * and silent_deletion is true. - * - **************************************/ - const dsql_str* string = (dsql_str*) node->nod_arg[e_trg_name]; - fb_assert(string); - if (silent_deletion) { - USHORT trig_type; - if (!METD_get_trigger(statement, string, NULL, &trig_type)) - return; - } - statement->append_cstring(isc_dyn_delete_trigger, string->str_data); - statement->append_uchar(isc_dyn_end); -} - - // f i n d _ p k _ c o l u m n s // // @brief Starting from the elements in a table definition, locate the PK columns @@ -4487,36 +3740,10 @@ static void generate_dyn(CompiledStatement* statement, dsql_nod* node) delete_exception(statement, node, false); // no silent break; - case nod_def_procedure: - case nod_mod_procedure: - case nod_replace_procedure: - define_procedure(statement, node->nod_type); - break; - - case nod_redef_procedure: - stuff(statement, isc_dyn_begin); - delete_procedure(statement, node, true); // silent. - define_procedure(statement, node->nod_type); - stuff(statement, isc_dyn_end); - break; - case nod_def_constraint: define_constraint_trigger(statement, node); break; - case nod_def_trigger: - case nod_mod_trigger: - case nod_replace_trigger: - define_trigger(statement, node->nod_type); - break; - - case nod_redef_trigger: - stuff(statement, isc_dyn_begin); - delete_trigger(statement, node, true); // silent - define_trigger(statement, node->nod_type); - stuff(statement, isc_dyn_end); - break; - case nod_del_domain: string = (dsql_str*) node->nod_arg[0]; statement->append_cstring(isc_dyn_delete_global_fld, string->str_data); @@ -4535,14 +3762,6 @@ static void generate_dyn(CompiledStatement* statement, dsql_nod* node) delete_relation_view (statement, node, false); // no silent. break; - case nod_del_procedure: - delete_procedure(statement, node, false); // no silent. - break; - - case nod_del_trigger: - delete_trigger(statement, node, false); // no silent - break; - case nod_del_role: string = (dsql_str*) node->nod_arg[0]; statement->append_cstring(isc_dyn_del_sql_role, string->str_data); @@ -4613,10 +3832,6 @@ static void generate_dyn(CompiledStatement* statement, dsql_nod* node) set_statistics(statement); break; - case nod_comment: - make_comment(statement); - break; - case nod_mod_udf: modify_udf(statement); break; @@ -4735,117 +3950,6 @@ static void grant_revoke(CompiledStatement* statement) } -// *********************** -// m a k e _ c o m m e n t -// *********************** -// Set the description blob for objects' self documentation. -// This query -// select rdb$relation_name from rdb$relation_fields where rdb$field_name = 'RDB$DESCRIPTION'; -// gives the list of objects that accept descriptions. At FB2 time, the only -// subobjects with descriptions are relation's fields and procedure's parameters. -static void make_comment(CompiledStatement* statement) -{ - const dsql_nod* node = statement->req_ddl_node; - fb_assert(node->nod_type == nod_comment); - const bool have_subobj = node->nod_arg[e_comment_part] != 0; - - const dsql_nod* obj_type_node = node->nod_arg[e_comment_obj_type]; - fb_assert(obj_type_node->nod_type == nod_constant && obj_type_node->nod_desc.dsc_dtype == dtype_long); - const int obj_type = obj_type_node->getSlong(); - - UCHAR dyn_verb = 0; - switch (obj_type) - { - case ddl_database: - dyn_verb = isc_dyn_mod_database; - break; - case ddl_domain: - dyn_verb = isc_dyn_mod_global_fld; - break; - case ddl_relation: - dyn_verb = isc_dyn_mod_rel; - break; - case ddl_view: - dyn_verb = isc_dyn_mod_view; - break; - case ddl_procedure: - dyn_verb = isc_dyn_mod_procedure; - break; - case ddl_trigger: - dyn_verb = isc_dyn_mod_trigger; - break; - case ddl_udf: - dyn_verb = isc_dyn_mod_function; // missing - break; - case ddl_blob_filter: - dyn_verb = isc_dyn_mod_filter; // missing - break; - case ddl_exception: - dyn_verb = isc_dyn_mod_exception; - break; - case ddl_generator: - dyn_verb = isc_dyn_mod_generator; // missing - break; - case ddl_index: - dyn_verb = isc_dyn_mod_idx; - break; - case ddl_role: - dyn_verb = isc_dyn_mod_sql_role; // missing - break; - case ddl_charset: - dyn_verb = isc_dyn_mod_charset; // missing - break; - case ddl_collation: - dyn_verb = isc_dyn_mod_collation; // missing - break; -// case ddl_sec_class: -// dyn_verb = isc_dyn_mod_security_class; -// break; - default: - // Complain. - break; - } - - if (have_subobj) - { - const dsql_str* field_or_param = (dsql_str*) node->nod_arg[e_comment_part]; - UCHAR dyn_verb2 = 0; - switch (obj_type) - { - case ddl_relation: - case ddl_view: - dyn_verb2 = isc_dyn_mod_local_fld; - dyn_verb = isc_dyn_rel_name; - break; - case ddl_procedure: - dyn_verb2 = isc_dyn_mod_prc_parameter; // missing - dyn_verb = isc_dyn_prc_name; - break; - default: - // Complain. - break; - } - - statement->append_string(dyn_verb2, field_or_param->str_data, field_or_param->str_length); - } - - if (obj_type == ddl_database) - statement->append_uchar(dyn_verb); - else - { - const dsql_str* obj_name = (dsql_str*) node->nod_arg[e_comment_object]; - statement->append_cstring(dyn_verb, obj_name->str_data); - } - const dsql_str* obj_desc = (dsql_str*) node->nod_arg[e_comment_string]; - if (obj_desc) - statement->append_string(isc_dyn_description, obj_desc->str_data, obj_desc->str_length); - else - statement->append_string(isc_dyn_description, NULL, 0); - - statement->append_uchar(isc_dyn_end); -} - - static void make_index( CompiledStatement* statement, const dsql_nod* element, const dsql_nod* columns, @@ -5316,6 +4420,10 @@ static void put_user_grant(CompiledStatement* statement, const dsql_nod* user) } break; + case nod_package_obj: + statement->append_cstring(isc_dyn_grant_package, name->str_data); + break; + case nod_proc_obj: statement->append_cstring(isc_dyn_grant_proc, name->str_data); break; @@ -5386,12 +4494,12 @@ static void modify_privilege(CompiledStatement* statement, *dynsave = (UCHAR) (priv_count >> 8); const dsql_str* name = (dsql_str*) table->nod_arg[0]; - if (table->nod_type == nod_procedure_name) { + if (table->nod_type == nod_procedure_name) statement->append_cstring(isc_dyn_prc_name, name->str_data); - } - else { + else if (table->nod_type == nod_package_name) + statement->append_cstring(isc_dyn_pkg_name, name->str_data); + else statement->append_cstring(isc_dyn_rel_name, name->str_data); - } put_user_grant(statement, user); @@ -5559,6 +4667,18 @@ static void modify_relation(CompiledStatement* statement) break; } + case nod_mod_field_null_flag: + field_node = element->nod_arg[e_mod_fld_null_flag_field]; + field_name = (dsql_str*) field_node->nod_arg[e_fln_name]; + statement->append_cstring(isc_dyn_mod_local_fld, field_name->str_data); + statement->append_cstring(isc_dyn_rel_name, relation_name->str_data); + if (element->nod_arg[e_mod_fld_null_flag_value]->getSlong()) + statement->append_uchar(isc_dyn_fld_not_null); + else + statement->append_uchar(isc_dyn_fld_null); + statement->append_uchar(isc_dyn_end); + break; + case nod_mod_field_pos: { field_node = element->nod_arg[e_mod_fld_pos_orig_name]; @@ -5575,7 +4695,6 @@ static void modify_relation(CompiledStatement* statement) break; } - case nod_mod_field_type: modify_field(statement, element, relation_name); break; @@ -5585,7 +4704,6 @@ static void modify_relation(CompiledStatement* statement) break; case nod_del_field: - // Fix for bug 8054: // // [CASCADE | RESTRICT] syntax is available in IB4.5, but not @@ -5659,6 +4777,7 @@ static void modify_udf(CompiledStatement* statement) } statement->append_cstring(isc_dyn_mod_function, obj_name->str_data); + const dsql_str* entry_point_name = (dsql_str*) node->nod_arg[e_mod_udf_entry_pt]; if (entry_point_name) statement->append_cstring(isc_dyn_func_entry_point, entry_point_name->str_data); @@ -5756,30 +4875,6 @@ static void define_user(CompiledStatement* statement, UCHAR op) } -static dsql_par* parameter_reverse_order(dsql_par* parameter, dsql_par* prev) -{ -/************************************** - * - * p a r a m e t e r _ r e v e r s e _ o r d e r - * - ************************************** - * - * Function - * Reverse parameters order for EXECUTE BLOCK statement - * - **************************************/ - dsql_par* result; - - if (parameter->par_next) - result = parameter_reverse_order(parameter->par_next, parameter); - else - result = parameter; - parameter->par_next = prev; - - return result; -} - - static void process_role_nm_list(CompiledStatement* statement, SSHORT option, const dsql_nod* user_ptr, @@ -6020,9 +5115,6 @@ static void put_field( CompiledStatement* statement, dsql_fld* field, bool udf_f if (field->fld_explicit_collation) statement->append_number(isc_dyn_fld_collation, field->fld_collation_id); - if (!field->fld_full_domain) - statement->append_number(isc_dyn_prm_mechanism, prm_mech_type_of); - return; } @@ -6079,12 +5171,12 @@ static void put_field( CompiledStatement* statement, dsql_fld* field, bool udf_f } -static void put_local_variable( CompiledStatement* statement, dsql_var* variable, +void DDL_put_local_variable( CompiledStatement* statement, dsql_var* variable, dsql_nod* host_param, const dsql_str* collation_name) { /************************************** * - * p u t _ l o c a l _ v a r i a b l e + * D D L _ p u t _ l o c a l _ v a r i a b l e * ************************************** * @@ -6138,11 +5230,11 @@ static void put_local_variable( CompiledStatement* statement, dsql_var* variable } -static void put_local_variables(CompiledStatement* statement, dsql_nod* parameters, SSHORT locals) +void DDL_put_local_variables(CompiledStatement* statement, dsql_nod* parameters, SSHORT locals) { /************************************** * - * p u t _ l o c a l _ v a r i a b l e s + * D D L _ p u t _ l o c a l _ v a r i a b l e s * ************************************** * @@ -6182,11 +5274,11 @@ static void put_local_variables(CompiledStatement* statement, dsql_nod* paramete *ptr = var_node; dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable]; - put_local_variable(statement, variable, parameter, + DDL_put_local_variable(statement, variable, parameter, reinterpret_cast(parameter->nod_arg[e_dfl_collate])); // Some field attributes are calculated inside - // put_local_variable(), so we reinitialize the + // DDL_put_local_variable(), so we reinitialize the // descriptor MAKE_desc_from_field(&var_node->nod_desc, field); @@ -6202,30 +5294,6 @@ static void put_local_variables(CompiledStatement* statement, dsql_nod* paramete } -static void put_msg_field( CompiledStatement* statement, const dsql_fld* field) -{ -/************************************** - * - * p u t _ m s g _ f i e l d - * - ************************************** - * - * Function - * Write out message field data type - * - **************************************/ - - //const USHORT dtype = field->fld_dtype; - - put_dtype(statement, field, true); - //field->fld_dtype = dtype; - - // add slot for null flag (parameter2) - statement->append_uchar(blr_short); - statement->append_uchar(0); -} - - static dsql_nod* replace_field_names(dsql_nod* input, dsql_nod* search_fields, dsql_nod* replace_fields, @@ -6977,9 +6045,9 @@ void CompiledStatement::put_debug_src_info(USHORT line, USHORT col) ULONG offset = (req_blr_data.getCount() - req_base_offset); // for DDL statements we store BLR's length at the first 2 bytes - if (req_type == REQ_DDL || req_ddl_node) { + if ((req_type == REQ_DDL || req_ddl_node) && !blockNode) offset -= 2; - } + req_debug_data.add(offset); req_debug_data.add(offset >> 8); } diff --git a/src/dsql/ddl_proto.h b/src/dsql/ddl_proto.h index bbba2a05b1..acf854fe79 100644 --- a/src/dsql/ddl_proto.h +++ b/src/dsql/ddl_proto.h @@ -38,13 +38,38 @@ namespace Jrd { class dsql_str; }; +const USHORT blr_dtypes[] = { + 0, + blr_text, // dtype_text + blr_cstring, // dtype_cstring + blr_varying, // dtype_varying + 0, + 0, + 0, // dtype_packed + 0, // dtype_byte + blr_short, // dtype_short + blr_long, // dtype_long + blr_quad, // dtype_quad + blr_float, // dtype_real + blr_double, // dtype_double + blr_double, // dtype_d_float + blr_sql_date, // dtype_sql_date + blr_sql_time, // dtype_sql_time + blr_timestamp, // dtype_timestamp + blr_blob, // dtype_blob // ASF: CAST use blr_blob2 because blr_blob don't fit in UCHAR + blr_short, // dtype_array + blr_int64 // dtype_int64 +}; + void DDL_execute(Jrd::dsql_req*); void DDL_generate(Jrd::CompiledStatement*, Jrd::dsql_nod*); bool DDL_ids(const Jrd::dsql_req*); void DDL_put_field_dtype(Jrd::CompiledStatement*, const Jrd::dsql_fld*, bool); void DDL_resolve_intl_type(Jrd::CompiledStatement*, Jrd::dsql_fld*, const Jrd::dsql_str*); void DDL_resolve_intl_type2(Jrd::CompiledStatement*, Jrd::dsql_fld*, const Jrd::dsql_str*, bool); -void DDL_gen_block(Jrd::CompiledStatement*, Jrd::dsql_nod*); + +void DDL_put_local_variable(Jrd::CompiledStatement*, Jrd::dsql_var*, Jrd::dsql_nod*, const Jrd::dsql_str*); +void DDL_put_local_variables(Jrd::CompiledStatement*, Jrd::dsql_nod*, SSHORT); #endif // DSQL_DDL_PROTO_H diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 4e37232e40..9c493608b7 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -80,7 +80,7 @@ static void close_cursor(thread_db*, dsql_req*); static USHORT convert(SLONG, UCHAR*); static void execute_blob(thread_db*, dsql_req*, USHORT, const UCHAR*, USHORT, const UCHAR*, USHORT, UCHAR*, USHORT, UCHAR*); -static void execute_immediate(thread_db*, Attachment*, jrd_tra**, +static void execute_immediate(thread_db*, Jrd::Attachment*, jrd_tra**, USHORT, const TEXT*, USHORT, USHORT, const UCHAR*, /*USHORT,*/ USHORT, const UCHAR*, USHORT, UCHAR*, /*USHORT,*/ USHORT, UCHAR*); @@ -90,7 +90,7 @@ static SSHORT filter_sub_type(const dsql_nod*); static bool get_indices(SLONG*, const UCHAR**, SLONG*, SCHAR**); static USHORT get_request_info(thread_db*, dsql_req*, SLONG, UCHAR*); static bool get_rsb_item(SLONG*, const UCHAR**, SLONG*, SCHAR**, USHORT*, USHORT*); -static dsql_dbb* init(Attachment*); +static dsql_dbb* init(Jrd::Attachment*); static void map_in_out(dsql_req*, dsql_msg*, USHORT, const UCHAR*, USHORT, UCHAR*, const UCHAR* = 0); static USHORT parse_blr(USHORT, const UCHAR*, const USHORT, dsql_par*); static dsql_req* prepare(thread_db*, dsql_dbb*, jrd_tra*, USHORT, const TEXT*, USHORT, USHORT); @@ -174,7 +174,7 @@ dsql_dbb::~dsql_dbb() @param attachment **/ -dsql_req* DSQL_allocate_statement(thread_db* tdbb, Attachment* attachment) +dsql_req* DSQL_allocate_statement(thread_db* tdbb, Jrd::Attachment* attachment) { SET_TDBB(tdbb); @@ -315,7 +315,7 @@ void DSQL_execute(thread_db* tdbb, **/ void DSQL_execute_immediate(thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, jrd_tra** tra_handle, USHORT length, const TEXT* string, USHORT dialect, USHORT in_blr_length, const UCHAR* in_blr, @@ -473,7 +473,7 @@ ISC_STATUS DSQL_fetch(thread_db* tdbb, dsql_msg* message = (dsql_msg*) request->req_receive; // Set up things for tracing this call - Attachment* att = request->req_dbb->dbb_attachment; + Jrd::Attachment* att = request->req_dbb->dbb_attachment; TraceDSQLFetch trace(att, request); // Insure that the blr for the message is parsed, regardless of @@ -893,7 +893,7 @@ static void close_cursor(thread_db* tdbb, dsql_req* request) { SET_TDBB(tdbb); - Attachment* attachment = request->req_dbb->dbb_attachment; + Jrd::Attachment* attachment = request->req_dbb->dbb_attachment; if (request->req_request) { ThreadStatusGuard status_vector(tdbb); @@ -1081,7 +1081,7 @@ static void execute_blob(thread_db* tdbb, **/ static void execute_immediate(thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, jrd_tra** tra_handle, USHORT length, const TEXT* string, USHORT dialect, USHORT in_blr_length, const UCHAR* in_blr, @@ -2055,7 +2055,7 @@ static bool get_rsb_item(SLONG* explain_length_ptr, @param db_handle **/ -static dsql_dbb* init(Attachment* attachment) +static dsql_dbb* init(Jrd::Attachment* attachment) { thread_db* tdbb = JRD_get_thread_data(); @@ -2840,7 +2840,7 @@ static void release_request(thread_db* tdbb, dsql_req* request, bool drop) close_cursor(tdbb, request); } - Attachment* att = request->req_dbb->dbb_attachment; + Jrd::Attachment* att = request->req_dbb->dbb_attachment; const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att); if (need_trace_free) { diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 24f418238e..1ef94cd17e 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -72,6 +72,7 @@ namespace Jrd class blb; struct bid; + class BlockNode; class dsql_ctx; class dsql_str; class dsql_nod; @@ -103,9 +104,10 @@ class dsql_str : public pool_alloc_rpt public: enum Type { - TYPE_SIMPLE = 0, - TYPE_HEXA, - TYPE_DELIMITED + TYPE_SIMPLE = 0, // '...' + TYPE_ALTERNATE, // q'{...}' + TYPE_HEXA, // x'...' + TYPE_DELIMITED // "..." }; public: @@ -131,8 +133,10 @@ class dsql_dbb : public pool_alloc { public: class dsql_rel* dbb_relations; // known relations in database - class dsql_prc* dbb_procedures; // known procedures in database - class dsql_udf* dbb_functions; // known functions in database + Firebird::GenericMap > > dbb_procedures; // known procedures in database + Firebird::GenericMap > > dbb_functions; // known functions in database MemoryPool& dbb_pool; // The current pool for the dbb Database* dbb_database; Attachment* dbb_attachment; @@ -149,7 +153,10 @@ public: Firebird::Mutex dbb_cache_mutex; // mutex protecting the DSQL metadata cache explicit dsql_dbb(MemoryPool& p) : - dbb_pool(p), dbb_charsets_by_id(p, 16) + dbb_procedures(p), + dbb_functions(p), + dbb_pool(p), + dbb_charsets_by_id(p, 16) {} ~dsql_dbb(); @@ -268,17 +275,16 @@ public: { } - dsql_prc* prc_next; // Next relation in database - dsql_sym* prc_symbol; // Hash symbol for procedure dsql_fld* prc_inputs; // Input parameters dsql_fld* prc_outputs; // Output parameters - Firebird::MetaName prc_name; // Name of procedure + Firebird::QualifiedName prc_name; // Name of procedure Firebird::MetaName prc_owner; // Owner of procedure SSHORT prc_in_count; SSHORT prc_def_count; // number of inputs with default values SSHORT prc_out_count; USHORT prc_id; // Procedure id USHORT prc_flags; + bool prc_private; // Packaged private procedure }; // prc_flags bits @@ -297,8 +303,6 @@ public: { } - dsql_udf* udf_next; - dsql_sym* udf_symbol; // Hash symbol for udf USHORT udf_dtype; SSHORT udf_scale; SSHORT udf_sub_type; @@ -306,8 +310,9 @@ public: SSHORT udf_character_set_id; //USHORT udf_character_length; USHORT udf_flags; - Firebird::MetaName udf_name; + Firebird::QualifiedName udf_name; Firebird::Array udf_arguments; + bool udf_private; // Packaged private function }; // udf_flags bits @@ -397,6 +402,7 @@ public: dsql_dbb* req_dbb; // DSQL attachment jrd_tra* req_transaction; // JRD transaction dsql_nod* req_ddl_node; // Store metadata request + BlockNode* blockNode; class dsql_blb* req_blob; // Blob info for blob requests jrd_req* req_request; // JRD request //dsql_str* req_blr_string; // String block during BLR generation @@ -451,6 +457,7 @@ public: req_labels(p), req_cursors(p), req_hidden_vars(p), + req_package(p), req_curr_ctes(p), req_ctes(p), req_cte_aliases(p) @@ -544,7 +551,6 @@ public: psql = value; } - dsql_nod* req_blk_node; // exec_block node dsql_rel* req_relation; // relation created by this request (for DDL) dsql_prc* req_procedure; // procedure created by this request (for DDL) Firebird::HalfStaticArray req_debug_data; @@ -573,6 +579,7 @@ public: dsql_str* req_alias_relation_prefix; // prefix for every relation-alias. DsqlNodStack req_hidden_vars; // hidden variables USHORT req_hidden_vars_number; // next hidden variable number + Firebird::MetaName req_package; // package being defined DsqlNodStack req_curr_ctes; // current processing CTE's class dsql_ctx* req_recursive_ctx; // context of recursive CTE @@ -793,9 +800,24 @@ enum { ddl_database, ddl_domain, ddl_relation, ddl_view, ddl_procedure, ddl_trigger, ddl_udf, ddl_blob_filter, ddl_exception, ddl_generator, ddl_index, ddl_role, - ddl_charset, ddl_collation//, ddl_sec_class + ddl_charset, ddl_collation, ddl_package//, ddl_sec_class }; +class CStrCmp +{ +public: + static int greaterThan(const char* s1, const char* s2) + { + return strcmp(s1, s2) > 0; + } +}; + +typedef Firebird::SortedArray, const char*, + Firebird::DefaultKeyValue, + CStrCmp> + StrArray; + } // namespace // macros for error generation diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index d510d5723c..e4bc89575c 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -38,6 +38,7 @@ #include #include "../dsql/dsql.h" #include "../dsql/node.h" +#include "../dsql/DdlNodes.h" #include "../dsql/StmtNodes.h" #include "../jrd/ibase.h" #include "../jrd/align.h" @@ -92,7 +93,6 @@ static void stuff_context(CompiledStatement*, const dsql_ctx*); static void stuff_cstring(CompiledStatement*, const char*); static void stuff_meta_string(CompiledStatement*, const char*); static void stuff_string(CompiledStatement*, const char*, int); -static void stuff_string(CompiledStatement* statement, const Firebird::MetaName& name); static void stuff_word(CompiledStatement*, USHORT); // STUFF is defined in dsql.h for use in common with ddl.c @@ -360,6 +360,10 @@ void GEN_expr(CompiledStatement* statement, dsql_nod* node) blr_operator = blr_agg_max; break; + case nod_window: + GEN_expr(statement, node->nod_arg[0]); + return; + case nod_agg_average: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_average_distinct : blr_agg_average; @@ -708,7 +712,7 @@ void GEN_expr(CompiledStatement* statement, dsql_nod* node) void GEN_port(CompiledStatement* statement, dsql_msg* message) { thread_db* tdbb = JRD_get_thread_data(); - Attachment* att = tdbb->getAttachment(); + Jrd::Attachment* att = tdbb->getAttachment(); // if (statement->req_blr_string) { stuff(statement, blr_message); @@ -726,10 +730,10 @@ void GEN_port(CompiledStatement* statement, dsql_msg* message) const USHORT fromCharSet = parameter->par_desc.getCharSet(); const USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? - fromCharSet : att->att_charset; + fromCharSet : tdbb->getCharSet(); if (parameter->par_desc.dsc_dtype <= dtype_any_text && - att->att_charset != CS_NONE && att->att_charset != CS_BINARY) + tdbb->getCharSet() != CS_NONE && tdbb->getCharSet() != CS_BINARY) { USHORT adjust = 0; if (parameter->par_desc.dsc_dtype == dtype_varying) @@ -754,7 +758,7 @@ void GEN_port(CompiledStatement* statement, dsql_msg* message) statement->req_dbb->dbb_minor_version) >= ODS_11_1 && parameter->par_desc.dsc_dtype == dtype_blob && parameter->par_desc.dsc_sub_type == isc_blob_text && - att->att_charset != CS_NONE && att->att_charset != CS_BINARY) + tdbb->getCharSet() != CS_NONE && tdbb->getCharSet() != CS_BINARY) { if (fromCharSet != toCharSet) parameter->par_desc.setTextType(toCharSet); @@ -1093,13 +1097,12 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node) stuff(statement, blr_end); return; - case nod_exec_block: - DDL_gen_block(statement, node); - return; - case nod_class_node: - reinterpret_cast(node->nod_arg[0])->genBlr(); + { + StmtNode* stmtNode = reinterpret_cast(node->nod_arg[0]); + stmtNode->genBlr(); return; + } case nod_for_select: gen_for_select(statement, node); @@ -1198,13 +1201,8 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node) return; case nod_return: - if ( (temp = node->nod_arg[e_rtn_procedure]) ) - { - if (temp->nod_type == nod_exec_block) - GEN_return(statement, temp->nod_arg[e_exe_blk_outputs], false); - else - GEN_return(statement, temp->nod_arg[e_prc_outputs], false); - } + if (node->nod_arg[e_rtn_procedure]) + ((BlockNode*) node->nod_arg[e_rtn_procedure])->genReturn(); return; case nod_exit: @@ -1214,7 +1212,12 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node) case nod_breakleave: stuff(statement, blr_leave); - stuff(statement, (int) (IPTR) node->nod_arg[e_breakleave_label]->nod_arg[e_label_number]); + stuff(statement, (int)(IPTR) node->nod_arg[e_breakleave_label]->nod_arg[e_label_number]); + return; + + case nod_continue: + stuff(statement, blr_continue_loop); + stuff(statement, (int)(IPTR) node->nod_arg[e_continue_label]->nod_arg[e_label_number]); return; case nod_abort: @@ -1400,26 +1403,35 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node) **/ static void gen_aggregate( CompiledStatement* statement, const dsql_nod* node) { - const dsql_ctx* context = (dsql_ctx*) node->nod_arg[e_agg_context]; - stuff(statement, blr_aggregate); + dsql_ctx* context = (dsql_ctx*) node->nod_arg[e_agg_context]; + bool window = (node->nod_flags & NOD_AGG_WINDOW); + stuff(statement, (window ? blr_window : blr_aggregate)); stuff_context(statement, context); gen_rse(statement, node->nod_arg[e_agg_rse]); - // Handle GROUP BY clause + // Handle PARTITION BY and GROUP BY clause - stuff(statement, blr_group_by); + if (window) + { + stuff(statement, blr_partition_by); + stuff(statement, 0); // partition by expression count + } + else + { + stuff(statement, blr_group_by); - dsql_nod* list = node->nod_arg[e_agg_group]; - if (list != NULL) { - stuff(statement, list->nod_count); - dsql_nod** ptr = list->nod_arg; - for (const dsql_nod* const* end = ptr + list->nod_count; ptr < end; ptr++) - { - GEN_expr(statement, *ptr); + dsql_nod* list = node->nod_arg[e_agg_group]; + if (list != NULL) { + stuff(statement, list->nod_count); + dsql_nod** ptr = list->nod_arg; + for (const dsql_nod* const* end = ptr + list->nod_count; ptr < end; ptr++) + { + GEN_expr(statement, *ptr); + } } + else + stuff(statement, 0); } - else - stuff(statement, 0); // Generate value map @@ -2297,13 +2309,24 @@ static void gen_relation( CompiledStatement* statement, dsql_ctx* context) } else if (procedure) { - if (DDL_ids(statement)) { + if (DDL_ids(statement)) + { stuff(statement, blr_pid); stuff_word(statement, procedure->prc_id); } - else { - stuff(statement, blr_procedure); - stuff_meta_string(statement, procedure->prc_name.c_str()); + else + { + if (procedure->prc_name.qualifier.hasData()) + { + stuff(statement, blr_procedure2); + stuff_meta_string(statement, procedure->prc_name.qualifier.c_str()); + stuff_meta_string(statement, procedure->prc_name.identifier.c_str()); + } + else + { + stuff(statement, blr_procedure); + stuff_meta_string(statement, procedure->prc_name.identifier.c_str()); + } } stuff_context(statement, context); @@ -2919,7 +2942,14 @@ static void gen_statement(CompiledStatement* statement, const dsql_nod* node) break; case nod_exec_procedure: - stuff(statement, blr_exec_proc); + if (node->nod_arg[e_exe_package]) + { + stuff(statement, blr_exec_proc2); + stuff_meta_string(statement, ((dsql_str*) node->nod_arg[e_exe_package])->str_data); + } + else + stuff(statement, blr_exec_proc); + name = (dsql_str*) node->nod_arg[e_exe_procedure]; stuff_meta_string(statement, name->str_data); @@ -3054,11 +3084,19 @@ static void gen_table_lock( CompiledStatement* statement, const dsql_nod* tbl_lo static void gen_udf( CompiledStatement* statement, const dsql_nod* node) { const dsql_udf* userFunc = (dsql_udf*) node->nod_arg[0]; - stuff(statement, blr_function); - stuff_string(statement, userFunc->udf_name); + + if (userFunc->udf_name.qualifier.isEmpty()) + stuff(statement, blr_function); + else + { + stuff(statement, blr_function2); + stuff_meta_string(statement, userFunc->udf_name.qualifier.c_str()); + } + + stuff_meta_string(statement, userFunc->udf_name.identifier.c_str()); const dsql_nod* list; - if ((node->nod_count == 2) && (list = node->nod_arg[1])) + if ((node->nod_count == 3) && (list = node->nod_arg[2])) { stuff(statement, list->nod_count); dsql_nod* const* ptr = list->nod_arg; @@ -3210,12 +3248,6 @@ static void stuff_string(CompiledStatement* statement, const char* string, int l } -static void stuff_string(CompiledStatement* statement, const Firebird::MetaName& name) -{ - stuff_string(statement, name.c_str(), name.length()); -} - - /** stuff_word diff --git a/src/dsql/hsh.cpp b/src/dsql/hsh.cpp index 067f110e8e..221a0f39f6 100644 --- a/src/dsql/hsh.cpp +++ b/src/dsql/hsh.cpp @@ -315,8 +315,6 @@ void HSHD_set_flag(const void* database, switch (type) { case SYM_relation: - case SYM_procedure: - case SYM_udf: break; default: return; @@ -351,20 +349,6 @@ void HSHD_set_flag(const void* database, sym_rel->rel_flags |= flag; break; } - - case SYM_procedure: - { - dsql_prc* sym_prc = (dsql_prc*) homonym->sym_object; - sym_prc->prc_flags |= flag; - break; - } - - case SYM_udf: - { - dsql_udf* sym_udf = (dsql_udf*) homonym->sym_object; - sym_udf->udf_flags |= flag; - break; - } } } } diff --git a/src/dsql/keywords.cpp b/src/dsql/keywords.cpp index fb7aa443fa..e015c8d26a 100644 --- a/src/dsql/keywords.cpp +++ b/src/dsql/keywords.cpp @@ -30,6 +30,11 @@ #include #endif +#include "../dsql/Nodes.h" +#include "../dsql/DdlNodes.h" +#include "../dsql/PackageNodes.h" +#include "../dsql/StmtNodes.h" +#include "../common/classes/TriState.h" #include "dsql.tab.h" #include "keywords.h" @@ -92,6 +97,7 @@ static const TOK tokens[] = {BIT_LENGTH, "BIT_LENGTH", 2, false}, {BLOB, "BLOB", 1, false}, {BLOCK, "BLOCK", 2, true}, + {BODY, "BODY", 2, true}, {BOTH, "BOTH", 2, false}, {KW_BREAK, "BREAK", 2, true}, {BY, "BY", 1, false}, @@ -121,6 +127,7 @@ static const TOK tokens[] = {CONNECT, "CONNECT", 2, false}, {CONSTRAINT, "CONSTRAINT", 1, false}, {CONTAINING, "CONTAINING", 1, false}, + {CONTINUE, "CONTINUE", 2, true}, {COS, "COS", 2, false}, {COSH, "COSH", 2, false}, {COT, "COT", 2, false}, @@ -143,6 +150,7 @@ static const TOK tokens[] = {DATEADD, "DATEADD", 2, false}, {DATEDIFF, "DATEDIFF", 2, false}, {DAY, "DAY", 2, false}, + {DDL, "DDL", 2, false}, {KW_DEC, "DEC", 1, false}, {DECIMAL, "DECIMAL", 1, false}, {DECLARE, "DECLARE", 1, false}, @@ -162,6 +170,7 @@ static const TOK tokens[] = {DROP, "DROP", 1, false}, {ELSE, "ELSE", 1, false}, {END, "END", 1, false}, + {ENGINE, "ENGINE", 2, false}, {ENTRY_POINT, "ENTRY_POINT", 1, false}, {ESCAPE, "ESCAPE", 1, false}, {EXCEPTION, "EXCEPTION", 1, false}, @@ -247,6 +256,7 @@ static const TOK tokens[] = {MOD, "MOD", 2, false}, {MODULE_NAME, "MODULE_NAME", 1, false}, {MONTH, "MONTH", 2, false}, + {NAME, "NAME", 2, false}, {NAMES, "NAMES", 1, false}, {NATIONAL, "NATIONAL", 1, false}, {NATURAL, "NATURAL", 1, false}, @@ -269,8 +279,10 @@ static const TOK tokens[] = {OS_NAME, "OS_NAME", 2, false}, {OUTER, "OUTER", 1, false}, {OUTPUT_TYPE, "OUTPUT_TYPE", 1, false}, + {OVER, "OVER", 2, false}, {OVERFLOW, "OVERFLOW", 1, false}, {OVERLAY, "OVERLAY", 2, false}, + {PACKAGE, "PACKAGE", 2, true}, {PAD, "PAD", 2, true}, {PAGE, "PAGE", 1, false}, {PAGES, "PAGES", 1, false}, @@ -291,6 +303,8 @@ static const TOK tokens[] = {PROTECTED, "PROTECTED", 1, false}, {RAND, "RAND", 2, false}, {DB_KEY, "RDB$DB_KEY", 1, false}, + {RDB_GET_CONTEXT, "RDB$GET_CONTEXT", 2, false}, + {RDB_SET_CONTEXT, "RDB$SET_CONTEXT", 2, false}, {READ, "READ", 1, false}, {REAL, "REAL", 1, false}, {VERSION, "RECORD_VERSION", 1, false}, diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index 7fb2e1189a..f96c055832 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -443,6 +443,7 @@ void MAKE_desc(CompiledStatement* statement, dsc* desc, dsql_nod* node, dsql_nod case nod_agg_min: case nod_agg_max: + case nod_window: MAKE_desc(statement, desc, node->nod_arg[0], null_replacement); desc->dsc_flags = DSC_nullable; return; @@ -1804,27 +1805,6 @@ dsql_str* MAKE_tagged_string(const char* strvar, size_t length, const char* char } -/** - - MAKE_trigger_type - - @brief Make a trigger type - - - @param prefix_node - @param suffix_node - - **/ -dsql_nod* MAKE_trigger_type(dsql_nod* prefix_node, dsql_nod* suffix_node) -{ - const SLONG prefix = prefix_node->getSlong(); - const SLONG suffix = suffix_node->getSlong(); - delete prefix_node; - delete suffix_node; - return MAKE_const_slong(prefix + suffix - 1); -} - - /** MAKE_variable @@ -2024,7 +2004,7 @@ static void make_parameter_names(dsql_par* parameter, const dsql_nod* item) case nod_udf: { dsql_udf* userFunc = (dsql_udf*) item->nod_arg[0]; - name_alias = userFunc->udf_name.c_str(); + name_alias = userFunc->udf_name.identifier.c_str(); break; } case nod_sys_function: @@ -2196,7 +2176,7 @@ static void make_parameter_names(dsql_par* parameter, const dsql_nod* item) } else if (context->ctx_procedure) { - parameter->par_rel_name = context->ctx_procedure->prc_name.c_str(); + parameter->par_rel_name = context->ctx_procedure->prc_name.identifier.c_str(); parameter->par_owner_name = context->ctx_procedure->prc_owner.c_str(); } diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index 96b8b7812e..69c28101d3 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -71,7 +71,6 @@ Jrd::dsql_par* MAKE_parameter(Jrd::dsql_msg*, bool, bool, USHORT, const Jrd::dsq Jrd::dsql_str* MAKE_string(const char*, int); Jrd::dsql_sym* MAKE_symbol(Jrd::dsql_dbb*, const TEXT*, USHORT, Jrd::sym_type, Jrd::dsql_req*); Jrd::dsql_str* MAKE_tagged_string(const char* str, size_t length, const char* charset); -Jrd::dsql_nod* MAKE_trigger_type(Jrd::dsql_nod*, Jrd::dsql_nod*); Jrd::dsql_nod* MAKE_variable(Jrd::dsql_fld*, const TEXT*, const Jrd::dsql_var_type type, USHORT, USHORT, USHORT); diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 3271197b5e..afe9689c6b 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -72,7 +72,6 @@ static const UCHAR blr_bpb[] = }; static void convert_dtype(dsql_fld*, SSHORT); -static void free_procedure(dsql_prc*); static void free_relation(dsql_rel*); static void insert_symbol(dsql_sym*); static dsql_sym* lookup_symbol(dsql_dbb*, USHORT, const char*, const SYM_TYPE, USHORT = 0); @@ -172,7 +171,7 @@ void METD_drop_collation(dsql_req* request, const dsql_str* name) } -void METD_drop_function(dsql_req* request, const dsql_str* name) +void METD_drop_function(dsql_req* request, const dsql_str* name, const MetaName& packageName) { /************************************** * @@ -190,25 +189,24 @@ void METD_drop_function(dsql_req* request, const dsql_str* name) * accessing it. * **************************************/ + thread_db* tdbb = JRD_get_thread_data(); + MutexHolder holder(request); - // If the symbol wasn't defined, we've got nothing to do + QualifiedName metaName(MetaName(name->str_data, name->str_length), packageName); + dsql_udf* function; - dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_udf); - - if (symbol) { - dsql_udf* userFunc = (dsql_udf*) symbol->sym_object; - userFunc->udf_flags |= UDF_dropped; + if (request->req_dbb->dbb_functions.get(metaName, function)) + { + MET_dsql_cache_use(tdbb, SYM_udf, metaName.identifier, metaName.qualifier); + function->udf_flags |= UDF_dropped; + request->req_dbb->dbb_functions.remove(metaName); } - // mark other potential candidates as maybe dropped - - HSHD_set_flag(request->req_dbb, name->str_data, name->str_length, SYM_udf, UDF_dropped); - } -void METD_drop_procedure(dsql_req* request, const dsql_str* name) +void METD_drop_procedure(dsql_req* request, const dsql_str* name, const MetaName& packageName) { /************************************** * @@ -226,21 +224,19 @@ void METD_drop_procedure(dsql_req* request, const dsql_str* name) * accessing it. * **************************************/ + thread_db* tdbb = JRD_get_thread_data(); + MutexHolder holder(request); - // If the symbol wasn't defined, we've got nothing to do + QualifiedName metaName(MetaName(name->str_data, name->str_length), packageName); + dsql_prc* procedure; - dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_procedure); - - if (symbol) { - dsql_prc* procedure = (dsql_prc*) symbol->sym_object; + if (request->req_dbb->dbb_procedures.get(metaName, procedure)) + { + MET_dsql_cache_use(tdbb, SYM_procedure, metaName.identifier, metaName.qualifier); procedure->prc_flags |= PRC_dropped; + request->req_dbb->dbb_procedures.remove(metaName); } - - // mark other potential candidates as maybe dropped - - HSHD_set_flag(request->req_dbb, name->str_data, name->str_length, SYM_procedure, PRC_dropped); - } @@ -575,7 +571,7 @@ USHORT METD_get_charset_bpc(dsql_req* request, SSHORT charset_id) dsql_dbb* dbb = request->req_dbb; if (charset_id == CS_dynamic) - charset_id = tdbb->getAttachment()->att_charset; + charset_id = tdbb->getCharSet(); dsql_intlsym* cs_sym = 0; size_t pos = 0; @@ -610,10 +606,10 @@ MetaName METD_get_charset_name(dsql_req* request, SSHORT charset_id) **************************************/ thread_db* tdbb = JRD_get_thread_data(); - dsql_dbb* dbb = request->req_dbb; + dsql_dbb* dbb = request->req_dbb; - if (charset_id == CS_dynamic) - charset_id = tdbb->getAttachment()->att_charset; + if (charset_id == CS_dynamic) + charset_id = tdbb->getCharSet(); size_t pos = 0; if (dbb->dbb_charsets_by_id.find(charset_id, pos)) @@ -907,7 +903,8 @@ bool METD_get_exception(dsql_req* request, const dsql_str* name) } -dsql_udf* METD_get_function(dsql_req* request, const dsql_str* name) +dsql_udf* METD_get_function(CompiledStatement* statement, const dsql_str* name, + const dsql_str* package) { /************************************** * @@ -921,48 +918,69 @@ dsql_udf* METD_get_function(dsql_req* request, const dsql_str* name) * **************************************/ thread_db* tdbb = JRD_get_thread_data(); + dsql_dbb* dbb = statement->req_dbb; + QualifiedName metaName(MetaName(name->str_data, name->str_length), + (package ? MetaName(package->str_data, package->str_length) : NULL)); - MutexHolder holder(request); + MutexHolder holder(statement); + + bool maybeUnqualified = statement->req_package.hasData() && metaName.qualifier.isEmpty(); + if (maybeUnqualified) + metaName.qualifier = statement->req_package; // Start by seeing if symbol is already defined - dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_udf); - if (symbol) - return (dsql_udf*) symbol->sym_object; + dsql_udf* userFunc = NULL; + if (dbb->dbb_functions.get(metaName, userFunc)) + { + if (userFunc->udf_private && metaName.qualifier != statement->req_package) + return NULL; + + if (MET_dsql_cache_use(tdbb, SYM_udf, metaName.identifier, metaName.qualifier)) + userFunc->udf_flags |= UDF_dropped; + + return userFunc; + } // Now see if it is in the database - validateTransaction(request); + validateTransaction(statement); - dsql_dbb* dbb = request->req_dbb; - dsql_udf* userFunc = NULL; USHORT return_arg = 0; - jrd_req* handle1 = CMP_find_request(tdbb, irq_function, IRQ_REQUESTS); + while (!userFunc) + { + jrd_req* handle1 = CMP_find_request(tdbb, irq_function, IRQ_REQUESTS); - FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE request->req_transaction) - X IN RDB$FUNCTIONS WITH - X.RDB$FUNCTION_NAME EQ name->str_data + FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE statement->req_transaction) + X IN RDB$FUNCTIONS WITH + X.RDB$FUNCTION_NAME EQ name->str_data AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.qualifier.c_str(), '') + + if (!DSQL_REQUEST(irq_function)) + DSQL_REQUEST(irq_function) = handle1; + + userFunc = FB_NEW(dbb->dbb_pool) dsql_udf(dbb->dbb_pool); + userFunc->udf_name = metaName; + userFunc->udf_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0; + + return_arg = X.RDB$RETURN_ARGUMENT; + + END_FOR if (!DSQL_REQUEST(irq_function)) DSQL_REQUEST(irq_function) = handle1; - userFunc = FB_NEW(dbb->dbb_pool) dsql_udf(dbb->dbb_pool); - // Moved below as still can't say for sure it will be stored. - // Following the same logic for MET_get_procedure and MET_get_relation - // userFunc->udf_next = dbb->dbb_functions; - // dbb->dbb_functions = userFunc; - - userFunc->udf_name = name->str_data; - return_arg = X.RDB$RETURN_ARGUMENT; - - END_FOR - - if (!DSQL_REQUEST(irq_function)) - DSQL_REQUEST(irq_function) = handle1; - - if (!userFunc) { - return NULL; + if (!userFunc) + { + if (maybeUnqualified) + { + maybeUnqualified = false; + metaName.qualifier = ""; + } + else + return NULL; + } } // Note: The following two requests differ in the fields which are @@ -973,9 +991,10 @@ dsql_udf* METD_get_function(dsql_req* request, const dsql_str* name) jrd_req* handle2 = CMP_find_request(tdbb, irq_func_return, IRQ_REQUESTS); - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE request->req_transaction) + FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE statement->req_transaction) X IN RDB$FUNCTION_ARGUMENTS WITH - X.RDB$FUNCTION_NAME EQ name->str_data + X.RDB$FUNCTION_NAME EQ name->str_data AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.qualifier.c_str(), '') SORTED BY X.RDB$ARGUMENT_POSITION if (!DSQL_REQUEST(irq_func_return)) @@ -1059,27 +1078,12 @@ dsql_udf* METD_get_function(dsql_req* request, const dsql_str* name) else if (userFunc->udf_dtype == dtype_varying) userFunc->udf_length += sizeof(USHORT); - if ((symbol = lookup_symbol(request->req_dbb, name, SYM_udf))) - { - // Get rid of all the stuff we just read in. Use existing one - delete userFunc; - return (dsql_udf*) symbol->sym_object; - } + dbb->dbb_functions.put(userFunc->udf_name, userFunc); - // Add udf in the front of the list. - userFunc->udf_next = dbb->dbb_functions; - dbb->dbb_functions = userFunc; + if (userFunc->udf_private && metaName.qualifier != statement->req_package) + return NULL; - // Store in the symbol table - // The UDF_new_udf flag is not used, so nothing extra to check. - - symbol = userFunc->udf_symbol = FB_NEW_RPT(dbb->dbb_pool, 0) dsql_sym; - symbol->sym_object = userFunc; - symbol->sym_string = userFunc->udf_name.c_str(); - symbol->sym_length = userFunc->udf_name.length(); - symbol->sym_type = SYM_udf; - symbol->sym_dbb = dbb; - insert_symbol(symbol); + MET_dsql_cache_use(tdbb, SYM_udf, userFunc->udf_name.identifier, userFunc->udf_name.qualifier); return userFunc; } @@ -1133,7 +1137,8 @@ dsql_nod* METD_get_primary_key(dsql_req* request, const dsql_str* relation_name) } -dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name) +dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name, + const dsql_str* package) { /************************************** * @@ -1150,53 +1155,102 @@ dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name) thread_db* tdbb = JRD_get_thread_data(); dsql_dbb* dbb = statement->req_dbb; + QualifiedName metaName(MetaName(name->str_data, name->str_length), + (package ? MetaName(package->str_data, package->str_length) : "")); - // see if the procedure is the one currently being defined in this statement - - dsql_prc* temp = statement->req_procedure; - if (temp != NULL && temp->prc_name == name->str_data) - { - return temp; - } + // ASF: I've removed the code where we verify if the procedure being looked up is the one being + // defined (statement->req_procedure). This code is totally incorrect, not considering + // transactions and savepoints, hence being incompatible with packages). + // Example (with autocommit off): + // + // SQL> create procedure p1 as begin end! + // SQL> create procedure p2 as begin execute procedure p1; end! + // SQL> rollback! + // SQL> execute procedure p2! + // Statement failed, SQLSTATE = 42000 + // Dynamic SQL Error + // -SQL error code = -204 + // -Procedure unknown + // -P2 + // SQL> execute procedure p1! + // Statement failed, SQLSTATE = 42000 + // invalid request BLR at offset 5 + // -procedure P1 is not defined + // + // The side effect is that this occur in more cases now: + // + // SQL> create procedure p as begin execute procedure p; execute procedure p2; end! + // Statement failed, SQLSTATE = 42000 + // Dynamic SQL Error + // -SQL error code = -204 + // -Procedure unknown + // -P2 + // SQL> execute procedure p! + // Statement failed, SQLSTATE = 42000 + // invalid request BLR at offset 4 + // -procedure P is not defined + // + // I hope for a solution, involving savepoint logic. MutexHolder holder(statement); + bool maybeUnqualified = statement->req_package.hasData() && metaName.qualifier.isEmpty(); + if (maybeUnqualified) + metaName.qualifier = statement->req_package; + // Start by seeing if symbol is already defined - dsql_sym* symbol = lookup_symbol(statement->req_dbb, name, SYM_procedure); - if (symbol) - return (dsql_prc*) symbol->sym_object; + dsql_prc* procedure = NULL; + if (dbb->dbb_procedures.get(metaName, procedure)) + { + if (procedure->prc_private && metaName.qualifier != statement->req_package) + return NULL; + + if (MET_dsql_cache_use(tdbb, SYM_procedure, metaName.identifier, metaName.qualifier)) + procedure->prc_flags |= PRC_dropped; + + return procedure; + } // now see if it is in the database validateTransaction(statement); - dsql_prc* procedure = NULL; + while (!procedure) + { + jrd_req* handle1 = CMP_find_request(tdbb, irq_procedure, IRQ_REQUESTS); - jrd_req* handle1 = CMP_find_request(tdbb, irq_procedure, IRQ_REQUESTS); + FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE statement->req_transaction) + X IN RDB$PROCEDURES + WITH X.RDB$PROCEDURE_NAME EQ name->str_data AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.qualifier.c_str(), '') - FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE statement->req_transaction) - X IN RDB$PROCEDURES WITH - X.RDB$PROCEDURE_NAME EQ name->str_data + if (!DSQL_REQUEST(irq_procedure)) + DSQL_REQUEST(irq_procedure) = handle1; + + fb_utils::exact_name(X.RDB$OWNER_NAME); + + procedure = FB_NEW(dbb->dbb_pool) dsql_prc(dbb->dbb_pool); + procedure->prc_id = X.RDB$PROCEDURE_ID; + procedure->prc_name = metaName; + procedure->prc_owner = X.RDB$OWNER_NAME; + procedure->prc_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0; + + END_FOR if (!DSQL_REQUEST(irq_procedure)) DSQL_REQUEST(irq_procedure) = handle1; - fb_utils::exact_name(X.RDB$OWNER_NAME); - - procedure = FB_NEW(dbb->dbb_pool) dsql_prc(dbb->dbb_pool); - procedure->prc_id = X.RDB$PROCEDURE_ID; - - procedure->prc_name = name->str_data; - procedure->prc_owner = X.RDB$OWNER_NAME; - - END_FOR - - if (!DSQL_REQUEST(irq_procedure)) - DSQL_REQUEST(irq_procedure) = handle1; - - if (!procedure) { - return NULL; + if (!procedure) + { + if (maybeUnqualified) + { + maybeUnqualified = false; + metaName.qualifier = ""; + } + else + return NULL; + } } // Lookup parameter stuff @@ -1213,12 +1267,13 @@ dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name) jrd_req* handle2 = CMP_find_request(tdbb, irq_parameters, IRQ_REQUESTS); - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE statement->req_transaction) - PR IN RDB$PROCEDURE_PARAMETERS CROSS - FLD IN RDB$FIELDS - WITH FLD.RDB$FIELD_NAME EQ PR.RDB$FIELD_SOURCE - AND PR.RDB$PROCEDURE_NAME EQ name->str_data - AND PR.RDB$PARAMETER_TYPE = type + FOR (REQUEST_HANDLE handle2 TRANSACTION_HANDLE statement->req_transaction) + PR IN RDB$PROCEDURE_PARAMETERS + CROSS FLD IN RDB$FIELDS + WITH FLD.RDB$FIELD_NAME EQ PR.RDB$FIELD_SOURCE AND + PR.RDB$PROCEDURE_NAME EQ name->str_data AND + PR.RDB$PARAMETER_TYPE = type AND + PR.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.qualifier.c_str(), '') SORTED BY DESCENDING PR.RDB$PARAMETER_NUMBER if (!DSQL_REQUEST(irq_parameters)) @@ -1235,9 +1290,12 @@ dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name) { jrd_req* handle3 = CMP_find_request(tdbb, irq_parameters2, IRQ_REQUESTS); + MetaName packageName(PR.RDB$PACKAGE_NAME.NULL ? NULL : PR.RDB$PACKAGE_NAME); + FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE statement->req_transaction) PR2 IN RDB$PROCEDURE_PARAMETERS WITH PR2.RDB$PROCEDURE_NAME EQ PR.RDB$PROCEDURE_NAME AND + PR2.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') AND PR2.RDB$PARAMETER_NAME EQ PR.RDB$PARAMETER_NAME if (!DSQL_REQUEST(irq_parameters2)) @@ -1322,34 +1380,13 @@ dsql_prc* METD_get_procedure(CompiledStatement* statement, const dsql_str* name) } } - // Since we could give up control due to the THREAD_EXIT and THEAD_ENTER - // calls, another thread may have added the same procedure in the mean time + dbb->dbb_procedures.put(procedure->prc_name, procedure); - if ((symbol = lookup_symbol(statement->req_dbb, name, SYM_procedure))) - { - // Get rid of all the stuff we just read in. Use existing one - free_procedure(procedure); - return (dsql_prc*) symbol->sym_object; - } + if (procedure->prc_private && metaName.qualifier != statement->req_package) + return NULL; - // store in the symbol table unless the procedure is not yet committed - // CVC: This is strange, because PRC_new_procedure is never set. - - if (!(procedure->prc_flags & PRC_new_procedure)) - { - // add procedure in the front of the list - - procedure->prc_next = dbb->dbb_procedures; - dbb->dbb_procedures = procedure; - - symbol = procedure->prc_symbol = FB_NEW_RPT(dbb->dbb_pool, 0) dsql_sym; - symbol->sym_object = procedure; - symbol->sym_string = procedure->prc_name.c_str(); - symbol->sym_length = procedure->prc_name.length(); - symbol->sym_type = SYM_procedure; - symbol->sym_dbb = dbb; - insert_symbol(symbol); - } + MET_dsql_cache_use(tdbb, SYM_procedure, procedure->prc_name.identifier, + procedure->prc_name.qualifier); return procedure; } @@ -1911,6 +1948,7 @@ static void convert_dtype(dsql_fld* field, SSHORT field_type) } +#ifdef NOT_USED_OR_REPLACED static void free_procedure(dsql_prc* procedure) { /************************************** @@ -1943,6 +1981,7 @@ static void free_procedure(dsql_prc* procedure) delete procedure; } +#endif // NOT_USED_OR_REPLACED static void free_relation(dsql_rel* relation) @@ -2036,21 +2075,11 @@ static dsql_sym* lookup_symbol(dsql_dbb* dbb, USHORT length, const char* name, { break; } - else if (type == SYM_procedure && (procedure = (dsql_prc*) symbol->sym_object) && - !(procedure->prc_flags & PRC_dropped)) - { - break; - } else if (type == SYM_relation && (relation = (dsql_rel*) symbol->sym_object) && !(relation->rel_flags & REL_dropped)) { break; } - else if (type == SYM_udf && (userFunc = (dsql_udf*) symbol->sym_object) && - !(userFunc->udf_flags & UDF_dropped)) - { - break; - } } } @@ -2067,18 +2096,10 @@ static dsql_sym* lookup_symbol(dsql_dbb* dbb, USHORT length, const char* name, intlSym->intlsym_flags |= INTLSYM_dropped; break; - case SYM_procedure: - procedure->prc_flags |= PRC_dropped; - break; - case SYM_relation: relation->rel_flags |= REL_dropped; break; - case SYM_udf: - userFunc->udf_flags |= UDF_dropped; - break; - default: return symbol; } diff --git a/src/dsql/metd_proto.h b/src/dsql/metd_proto.h index 12fe697f10..745c358a50 100644 --- a/src/dsql/metd_proto.h +++ b/src/dsql/metd_proto.h @@ -30,11 +30,11 @@ #include "../common/classes/MetaName.h" #include "../common/classes/fb_pair.h" -typedef Firebird::Pair > MetaNamePair; -typedef Firebird::GenericMap MetaNamePairMap; - // forward declarations namespace Jrd { + typedef Firebird::Pair > MetaNamePair; + typedef Firebird::GenericMap MetaNamePairMap; + class dsql_req; class dsql_str; class CompiledStatement; @@ -42,8 +42,8 @@ namespace Jrd { void METD_drop_charset(Jrd::dsql_req*, const Firebird::MetaName&); void METD_drop_collation(Jrd::dsql_req*, const Jrd::dsql_str*); -void METD_drop_function(Jrd::dsql_req*, const Jrd::dsql_str*); -void METD_drop_procedure(Jrd::dsql_req*, const Jrd::dsql_str*); +void METD_drop_function(Jrd::dsql_req*, const Jrd::dsql_str*, const Firebird::MetaName&); +void METD_drop_procedure(Jrd::dsql_req*, const Jrd::dsql_str*, const Firebird::MetaName&); void METD_drop_relation(Jrd::dsql_req*, const Jrd::dsql_str*); Jrd::dsql_intlsym* METD_get_charset(Jrd::dsql_req*, USHORT, const char* name); // UTF-8 @@ -55,15 +55,15 @@ Jrd::dsql_str* METD_get_default_charset(Jrd::dsql_req*); bool METD_get_domain(Jrd::dsql_req*, class Jrd::dsql_fld*, const char* name); // UTF-8 USHORT METD_get_domain_default(Jrd::dsql_req*, const TEXT*, bool*, UCHAR*, USHORT); bool METD_get_exception(Jrd::dsql_req*, const Jrd::dsql_str*); -Jrd::dsql_udf* METD_get_function(Jrd::dsql_req*, const Jrd::dsql_str*); +Jrd::dsql_udf* METD_get_function(Jrd::CompiledStatement*, const Jrd::dsql_str*, const Jrd::dsql_str*); Jrd::dsql_nod* METD_get_primary_key(Jrd::dsql_req*, const Jrd::dsql_str*); -Jrd::dsql_prc* METD_get_procedure(Jrd::CompiledStatement*, const Jrd::dsql_str*); +Jrd::dsql_prc* METD_get_procedure(Jrd::CompiledStatement*, const Jrd::dsql_str*, const Jrd::dsql_str*); Jrd::dsql_rel* METD_get_relation(Jrd::CompiledStatement*, const Jrd::dsql_str*); bool METD_get_trigger(Jrd::dsql_req*, const Jrd::dsql_str*, Jrd::dsql_str**, USHORT*); bool METD_get_type(Jrd::dsql_req*, const Jrd::dsql_str*, const char*, SSHORT*); Jrd::dsql_rel* METD_get_view_base(Jrd::CompiledStatement* request, const char* view_name, // UTF-8 - MetaNamePairMap& fields); + Jrd::MetaNamePairMap& fields); Jrd::dsql_rel* METD_get_view_relation(Jrd::CompiledStatement* request, const char* view_name, // UTF-8 const char* relation_or_alias); // UTF-8 diff --git a/src/dsql/node.h b/src/dsql/node.h index 4b33ac041a..f11895d694 100644 --- a/src/dsql/node.h +++ b/src/dsql/node.h @@ -47,6 +47,9 @@ namespace Dsql { enum nod_t { + // ASF: 1) These commented numbers are not all correct now; + // 2) They are going to continue been deleted; + // 3) There is no sense to numerate them. nod_unknown_type = 0, nod_commit = 1, // Commands, not executed. nod_rollback, @@ -68,15 +71,6 @@ enum nod_t nod_del_index, nod_def_view, nod_def_constraint, // 20 - nod_def_trigger, - nod_mod_trigger, - nod_del_trigger, -// nod_def_trigger_msg, -// nod_mod_trigger_msg, -// nod_del_trigger_msg, - nod_def_procedure, - nod_mod_procedure, - nod_del_procedure, nod_def_exception, nod_mod_exception, nod_del_exception, @@ -302,15 +296,12 @@ enum nod_t nod_redef_relation, // allows silent creation/overwriting of a relation. nod_udf_param, // there should be a way to signal a param by descriptor! nod_limit, // limit support - nod_redef_procedure, // allows silent creation/overwriting of a procedure. nod_exec_sql, // EXECUTE STATEMENT nod_internal_info, // internal engine info nod_searched_case, // 230 // searched CASE function nod_simple_case, // simple CASE function nod_coalesce, // COALESCE function nod_mod_view, // ALTER VIEW - nod_replace_procedure, // CREATE OR ALTER PROCEDURE - nod_replace_trigger, // CREATE OR ALTER TRIGGER nod_replace_view, // CREATE OR ALTER VIEW nod_redef_view, // allows silent creation/overwriting of a view nod_for_update, // FOR UPDATE clause @@ -330,14 +321,12 @@ enum nod_t nod_cursor_fetch, nod_cursor_close, nod_fetch_seek, - nod_exec_block, // EXECUTE BLOCK support nod_param_val, // default value for SP parameters support nod_rows, // ROWS support nod_query_spec, nod_equiv, // IS DISTINCT FROM nod_redef_exception, // RECREATE EXCEPTION nod_replace_exception, // 260 // CREATE OR ALTER EXCEPTION - nod_comment, nod_mod_udf, nod_def_collation, nod_del_collation, @@ -348,7 +337,6 @@ enum nod_t nod_strlen, nod_trim, // 270 nod_returning, - nod_redef_trigger, nod_tra_misc, nod_lock_timeout, nod_agg_list, @@ -375,8 +363,15 @@ enum nod_t nod_tran_params, nod_named_param, nod_dfl_collate, + nod_trg_act, + nod_trg_ext, nod_class_node, - nod_hidden_var // 300 + nod_hidden_var, // 300 + nod_package_name, + nod_package_obj, + nod_window, + nod_mod_field_null_flag, + nod_continue }; /* enumerations of the arguments to a node, offsets @@ -520,26 +515,12 @@ enum node_args { e_cur_number, e_cur_count, - e_prc_name = 0, // nod_procedure - e_prc_inputs, - e_prc_outputs, - e_prc_dcls, - e_prc_body, - e_prc_source, - e_prc_count, - e_exe_procedure = 0, // nod_exec_procedure e_exe_inputs, e_exe_outputs, + e_exe_package, e_exe_count, - e_exe_blk = 0, // nod_exec_block - e_exe_blk_inputs = 0, - e_exe_blk_outputs, - e_exe_blk_dcls, - e_exe_blk_body, - e_exe_blk_count, - e_prm_val_fld = 0, e_prm_val_val, e_prm_val_count, @@ -667,6 +648,7 @@ enum node_args { e_rpn_name = 0, // nod_rel_proc_name e_rpn_alias, e_rpn_inputs, + e_rpn_package, e_rpn_count, e_join_left_rel = 0, // nod_join @@ -763,19 +745,6 @@ enum node_args { e_cnstr_source, e_cnstr_count, - e_trg_name = 0, // nod_mod_trigger and nod_def_trigger - e_trg_table, - e_trg_active, - e_trg_type, - e_trg_position, - e_trg_actions, - e_trg_source, - e_trg_count, - - e_trg_act_dcls = 0, - e_trg_act_body, - e_trg_act_count, - e_abrt_number = 0, // nod_abort e_abrt_count, @@ -952,12 +921,6 @@ enum node_args { e_agg_function_scope_level, e_agg_function_count, - e_comment_obj_type = 0, - e_comment_object, - e_comment_part, - e_comment_string, - e_comment_count, - e_mod_udf_name = 0, // nod_mod_udf e_mod_udf_entry_pt, e_mod_udf_module, @@ -1012,7 +975,17 @@ enum node_args { e_hidden_var_expr = 0, // nod_hidden_var e_hidden_var_var, - e_hidden_var_count + e_hidden_var_count, + + e_window_expr = 0, + e_window_count, + + e_mod_fld_null_flag_field = 0, // nod_mod_field_null_flag + e_mod_fld_null_flag_value, + e_mod_fld_null_flag_count, + + e_continue_label = 0, // nod_continue + e_continue_count, }; } // namespace @@ -1109,7 +1082,9 @@ enum nod_flags_vals { NOD_TRAN_AUTONOMOUS = 1, // nod_exec_stmt NOD_TRAN_COMMON = 2, NOD_TRAN_2PC = 3, - NOD_TRAN_DEFAULT = NOD_TRAN_COMMON + NOD_TRAN_DEFAULT = NOD_TRAN_COMMON, + + NOD_AGG_WINDOW = 1 // nod_aggregate }; } // namespace diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 7a69ee4ec8..8eee8b9e32 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -132,7 +132,7 @@ const int UNSIGNED = 2; static const char INTERNAL_FIELD_NAME[] = "DSQL internal"; /* NTX: placeholder */ -inline SLONG trigger_type_suffix(const int slot1, const int slot2, const int slot3) +inline unsigned trigger_type_suffix(const unsigned slot1, const unsigned slot2, const unsigned slot3) { return ((slot1 << 1) | (slot2 << 3) | (slot3 << 5)); } @@ -156,6 +156,7 @@ static bool short_int(dsql_nod*, SLONG*, SSHORT); #endif static void stack_nodes (dsql_nod*, DsqlNodStack&); static Firebird::MetaName toName(dsql_nod* node); +static Firebird::string toString(dsql_str* node); static void yyabandon (SLONG, ISC_STATUS); @@ -181,384 +182,399 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string) /* Tokens in v4.0 -- not separated into v3 and v4 tokens */ -%token ACTIVE -%token ADD -%token AFTER -%token ALL -%token ALTER -%token AND -%token ANY -%token AS -%token ASC -%token AT -%token AVG -%token AUTO -%token BEFORE -%token BEGIN -%token BETWEEN -%token BLOB -%token BY -%token CAST -%token CHARACTER -%token CHECK -%token COLLATE -%token COMMA -%token COMMIT -%token COMMITTED -%token COMPUTED -%token CONCATENATE -%token CONDITIONAL -%token CONSTRAINT -%token CONTAINING -%token COUNT -%token CREATE -%token CSTRING -%token CURRENT -%token CURSOR -%token DATABASE -%token DATE -%token DB_KEY -%token DECIMAL -%token DECLARE -%token DEFAULT -%token KW_DELETE -%token DESC -%token DISTINCT -%token DO -%token DOMAIN -%token DROP -%token ELSE -%token END -%token ENTRY_POINT -%token EQL -%token ESCAPE -%token EXCEPTION -%token EXECUTE -%token EXISTS -%token EXIT -%token EXTERNAL -%token FILTER -%token FOR -%token FOREIGN -%token FROM -%token FULL -%token FUNCTION -%token GDSCODE -%token GEQ -%token GENERATOR -%token GEN_ID -%token GRANT -%token GROUP -%token GTR -%token HAVING -%token IF -%token KW_IN -%token INACTIVE -%token INNER -%token INPUT_TYPE -%token INDEX -%token INSERT -%token INTEGER -%token INTO -%token IS -%token ISOLATION -%token JOIN -%token KEY -%token KW_CHAR -%token KW_DEC -%token KW_DOUBLE -%token KW_FILE -%token KW_FLOAT -%token KW_INT -%token KW_LONG -%token KW_NULL -%token KW_NUMERIC -%token KW_UPPER -%token KW_VALUE -%token LENGTH -%token LPAREN -%token LEFT -%token LEQ -%token LEVEL -%token LIKE -%token LSS -%token MANUAL -%token MAXIMUM -%token MAX_SEGMENT -%token MERGE -%token MINIMUM -%token MODULE_NAME -%token NAMES -%token NATIONAL -%token NATURAL -%token NCHAR -%token NEQ -%token NO -%token NOT -%token NOT_GTR -%token NOT_LSS -%token OF -%token ON -%token ONLY -%token OPTION -%token OR -%token ORDER -%token OUTER -%token OUTPUT_TYPE -%token OVERFLOW -%token PAGE -%token PAGES -%token KW_PAGE_SIZE -%token PARAMETER -%token PASSWORD -%token PLAN -%token POSITION -%token POST_EVENT -%token PRECISION -%token PRIMARY -%token PRIVILEGES -%token PROCEDURE -%token PROTECTED -%token READ -%token REAL -%token REFERENCES -%token RESERVING -%token RETAIN -%token RETURNING_VALUES -%token RETURNS -%token REVOKE -%token RIGHT -%token RPAREN -%token ROLLBACK -%token SEGMENT -%token SELECT -%token SET -%token SHADOW -%token KW_SHARED -%token SINGULAR -%token KW_SIZE -%token SMALLINT -%token SNAPSHOT -%token SOME -%token SORT -%token SQLCODE -%token STABILITY -%token STARTING -%token STATISTICS -%token SUB_TYPE -%token SUSPEND -%token SUM -%token TABLE -%token THEN -%token TO -%token TRANSACTION -%token TRIGGER -%token UNCOMMITTED -%token UNION -%token UNIQUE -%token UPDATE -%token USER -%token VALUES -%token VARCHAR -%token VARIABLE -%token VARYING -%token VERSION -%token VIEW -%token WAIT -%token WHEN -%token WHERE -%token WHILE -%token WITH -%token WORK -%token WRITE +%token ACTIVE +%token ADD +%token AFTER +%token ALL +%token ALTER +%token AND +%token ANY +%token AS +%token ASC +%token AT +%token AVG +%token AUTO +%token BEFORE +%token BEGIN +%token BETWEEN +%token BLOB +%token BY +%token CAST +%token CHARACTER +%token CHECK +%token COLLATE +%token COMMA +%token COMMIT +%token COMMITTED +%token COMPUTED +%token CONCATENATE +%token CONDITIONAL +%token CONSTRAINT +%token CONTAINING +%token COUNT +%token CREATE +%token CSTRING +%token CURRENT +%token CURSOR +%token DATABASE +%token DATE +%token DB_KEY +%token DECIMAL +%token DECLARE +%token DEFAULT +%token KW_DELETE +%token DESC +%token DISTINCT +%token DO +%token DOMAIN +%token DROP +%token ELSE +%token END +%token ENTRY_POINT +%token EQL +%token ESCAPE +%token EXCEPTION +%token EXECUTE +%token EXISTS +%token EXIT +%token EXTERNAL +%token FILTER +%token FOR +%token FOREIGN +%token FROM +%token FULL +%token FUNCTION +%token GDSCODE +%token GEQ +%token GENERATOR +%token GEN_ID +%token GRANT +%token GROUP +%token GTR +%token HAVING +%token IF +%token KW_IN +%token INACTIVE +%token INNER +%token INPUT_TYPE +%token INDEX +%token INSERT +%token INTEGER +%token INTO +%token IS +%token ISOLATION +%token JOIN +%token KEY +%token KW_CHAR +%token KW_DEC +%token KW_DOUBLE +%token KW_FILE +%token KW_FLOAT +%token KW_INT +%token KW_LONG +%token KW_NULL +%token KW_NUMERIC +%token KW_UPPER +%token KW_VALUE +%token LENGTH +%token LPAREN +%token LEFT +%token LEQ +%token LEVEL +%token LIKE +%token LSS +%token MANUAL +%token MAXIMUM +%token MAX_SEGMENT +%token MERGE +%token MINIMUM +%token MODULE_NAME +%token NAMES +%token NATIONAL +%token NATURAL +%token NCHAR +%token NEQ +%token NO +%token NOT +%token NOT_GTR +%token NOT_LSS +%token OF +%token ON +%token ONLY +%token OPTION +%token OR +%token ORDER +%token OUTER +%token OUTPUT_TYPE +%token OVERFLOW +%token PAGE +%token PAGES +%token KW_PAGE_SIZE +%token PARAMETER +%token PASSWORD +%token PLAN +%token POSITION +%token POST_EVENT +%token PRECISION +%token PRIMARY +%token PRIVILEGES +%token PROCEDURE +%token PROTECTED +%token READ +%token REAL +%token REFERENCES +%token RESERVING +%token RETAIN +%token RETURNING_VALUES +%token RETURNS +%token REVOKE +%token RIGHT +%token RPAREN +%token ROLLBACK +%token SEGMENT +%token SELECT +%token SET +%token SHADOW +%token KW_SHARED +%token SINGULAR +%token KW_SIZE +%token SMALLINT +%token SNAPSHOT +%token SOME +%token SORT +%token SQLCODE +%token STABILITY +%token STARTING +%token STATISTICS +%token SUB_TYPE +%token SUSPEND +%token SUM +%token TABLE +%token THEN +%token TO +%token TRANSACTION +%token TRIGGER +%token UNCOMMITTED +%token UNION +%token UNIQUE +%token UPDATE +%token USER +%token VALUES +%token VARCHAR +%token VARIABLE +%token VARYING +%token VERSION +%token VIEW +%token WAIT +%token WHEN +%token WHERE +%token WHILE +%token WITH +%token WORK +%token WRITE -%token FLOAT_NUMBER NUMBER NUMERIC SYMBOL STRING INTRODUCER +%token FLOAT_NUMBER NUMBER NUMERIC SYMBOL + +%token STRING +%token INTRODUCER /* New tokens added v5.0 */ -%token ACTION -%token ADMIN -%token CASCADE -%token FREE_IT /* ISC SQL extension */ -%token RESTRICT -%token ROLE +%token ACTION +%token ADMIN +%token CASCADE +%token FREE_IT /* ISC SQL extension */ +%token RESTRICT +%token ROLE /* New tokens added v6.0 */ -%token COLUMN -%token KW_TYPE -%token EXTRACT -%token YEAR -%token MONTH -%token DAY -%token HOUR -%token MINUTE -%token SECOND -%token WEEKDAY /* ISC SQL extension */ -%token YEARDAY /* ISC SQL extension */ -%token TIME -%token TIMESTAMP -%token CURRENT_DATE -%token CURRENT_TIME -%token CURRENT_TIMESTAMP +%token COLUMN +%token KW_TYPE +%token EXTRACT +%token YEAR +%token MONTH +%token DAY +%token HOUR +%token MINUTE +%token SECOND +%token WEEKDAY /* ISC SQL extension */ +%token YEARDAY /* ISC SQL extension */ +%token TIME +%token TIMESTAMP +%token CURRENT_DATE +%token CURRENT_TIME +%token CURRENT_TIMESTAMP /* special aggregate token types returned by lex in v6.0 */ -%token NUMBER64BIT SCALEDINT +%token NUMBER64BIT SCALEDINT /* CVC: Special Firebird additions. */ -%token CURRENT_USER -%token CURRENT_ROLE -%token KW_BREAK -%token SUBSTRING -%token RECREATE -%token KW_DESCRIPTOR -%token FIRST -%token SKIP +%token CURRENT_USER +%token CURRENT_ROLE +%token KW_BREAK +%token SUBSTRING +%token RECREATE +%token KW_DESCRIPTOR +%token FIRST +%token SKIP /* tokens added for Firebird 1.5 */ -%token CURRENT_CONNECTION -%token CURRENT_TRANSACTION -%token BIGINT -%token CASE -%token NULLIF -%token COALESCE -%token USING -%token NULLS -%token LAST -%token ROW_COUNT -%token LOCK -%token SAVEPOINT -%token RELEASE -%token STATEMENT -%token LEAVE -%token INSERTING -%token UPDATING -%token DELETING +%token CURRENT_CONNECTION +%token CURRENT_TRANSACTION +%token BIGINT +%token CASE +%token NULLIF +%token COALESCE +%token USING +%token NULLS +%token LAST +%token ROW_COUNT +%token LOCK +%token SAVEPOINT +%token RELEASE +%token STATEMENT +%token LEAVE +%token INSERTING +%token UPDATING +%token DELETING /* tokens added for Firebird 2.0 */ -%token BACKUP -%token KW_DIFFERENCE -%token OPEN -%token CLOSE -%token FETCH -%token ROWS -%token BLOCK -%token IIF -%token SCALAR_ARRAY -%token CROSS -%token NEXT -%token SEQUENCE -%token RESTART -%token BOTH -%token COLLATION -%token COMMENT -%token BIT_LENGTH -%token CHAR_LENGTH -%token CHARACTER_LENGTH -%token LEADING -%token KW_LOWER -%token OCTET_LENGTH -%token TRAILING -%token TRIM -%token RETURNING -%token KW_IGNORE -%token LIMBO -%token UNDO -%token REQUESTS -%token TIMEOUT +%token BACKUP +%token KW_DIFFERENCE +%token OPEN +%token CLOSE +%token FETCH +%token ROWS +%token BLOCK +%token IIF +%token SCALAR_ARRAY +%token CROSS +%token NEXT +%token SEQUENCE +%token RESTART +%token BOTH +%token COLLATION +%token COMMENT +%token BIT_LENGTH +%token CHAR_LENGTH +%token CHARACTER_LENGTH +%token LEADING +%token KW_LOWER +%token OCTET_LENGTH +%token TRAILING +%token TRIM +%token RETURNING +%token KW_IGNORE +%token LIMBO +%token UNDO +%token REQUESTS +%token TIMEOUT /* tokens added for Firebird 2.1 */ -%token ABS -%token ACCENT -%token ACOS -%token ALWAYS -%token ASCII_CHAR -%token ASCII_VAL -%token ASIN -%token ATAN -%token ATAN2 -%token BIN_AND -%token BIN_OR -%token BIN_SHL -%token BIN_SHR -%token BIN_XOR -%token CEIL -%token CONNECT -%token COS -%token COSH -%token COT -%token DATEADD -%token DATEDIFF -%token DECODE -%token DISCONNECT -%token EXP -%token FLOOR -%token GEN_UUID -%token GENERATED -%token GLOBAL -%token HASH -%token INSENSITIVE -%token LIST -%token LN -%token LOG -%token LOG10 -%token LPAD -%token MATCHED -%token MATCHING -%token MAXVALUE -%token MILLISECOND -%token MINVALUE -%token MOD -%token OVERLAY -%token PAD -%token PI -%token PLACING -%token POWER -%token PRESERVE -%token RAND -%token RECURSIVE -%token REPLACE -%token REVERSE -%token ROUND -%token RPAD -%token SENSITIVE -%token SIGN -%token SIN -%token SINH -%token SPACE -%token SQRT -%token START -%token TAN -%token TANH -%token TEMPORARY -%token TRUNC -%token WEEK +%token ABS +%token ACCENT +%token ACOS +%token ALWAYS +%token ASCII_CHAR +%token ASCII_VAL +%token ASIN +%token ATAN +%token ATAN2 +%token BIN_AND +%token BIN_OR +%token BIN_SHL +%token BIN_SHR +%token BIN_XOR +%token CEIL +%token CONNECT +%token COS +%token COSH +%token COT +%token DATEADD +%token DATEDIFF +%token DECODE +%token DISCONNECT +%token EXP +%token FLOOR +%token GEN_UUID +%token GENERATED +%token GLOBAL +%token HASH +%token INSENSITIVE +%token LIST +%token LN +%token LOG +%token LOG10 +%token LPAD +%token MATCHED +%token MATCHING +%token MAXVALUE +%token MILLISECOND +%token MINVALUE +%token MOD +%token OVERLAY +%token PAD +%token PI +%token PLACING +%token POWER +%token PRESERVE +%token RAND +%token RECURSIVE +%token REPLACE +%token REVERSE +%token ROUND +%token RPAD +%token SENSITIVE +%token SIGN +%token SIN +%token SINH +%token SPACE +%token SQRT +%token START +%token TAN +%token TANH +%token TEMPORARY +%token TRUNC +%token WEEK // tokens added for Firebird 2.5 -%token AUTONOMOUS -%token CHAR_TO_UUID -%token FIRSTNAME -%token GRANTED -%token LASTNAME -%token MIDDLENAME -%token MAPPING -%token OS_NAME -%token SIMILAR -%token UUID_TO_CHAR +%token AUTONOMOUS +%token CHAR_TO_UUID +%token FIRSTNAME +%token GRANTED +%token LASTNAME +%token MIDDLENAME +%token MAPPING +%token OS_NAME +%token SIMILAR +%token UUID_TO_CHAR // new execute statement -%token CALLER -%token COMMON -%token DATA -%token SOURCE -%token TWO_PHASE -%token BIND_PARAM -%token BIN_NOT +%token CALLER +%token COMMON +%token DATA +%token SOURCE +%token TWO_PHASE +%token BIND_PARAM +%token BIN_NOT + +// tokens added for Firebird 3.0 + +%token BODY +%token CONTINUE +%token DDL +%token ENGINE +%token NAME +%token OVER +%token PACKAGE +%token RDB_GET_CONTEXT +%token RDB_SET_CONTEXT /* precedence declarations for expression evaluation */ @@ -584,6 +600,209 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string) %nonassoc ALTER %nonassoc COLUMN +%union +{ + TriStateRawType triIntVal; + TriStateRawType triBoolVal; + int intVal; + unsigned uintVal; + FB_UINT64 uint64Val; + TriStateRawType triUintVal; + TriStateRawType triUint64Val; + Jrd::dsql_nod* legacyNode; + Jrd::dsql_str* legacyStr; + Jrd::dsql_fld* legacyField; + TEXT* textPtr; + Jrd::ExternalClause* externalClause; + Jrd::StmtNode* stmtNode; + Jrd::DdlNode* ddlNode; + Jrd::CreateAlterFunctionNode* createAlterFunctionNode; + Jrd::CreateAlterProcedureNode* createAlterProcedureNode; + Jrd::CreateAlterTriggerNode* createAlterTriggerNode; + Jrd::CreateAlterPackageNode* createAlterPackageNode; + Firebird::Array* packageItems; + Jrd::CreateAlterPackageNode::Item packageItem; + Jrd::CreatePackageBodyNode* createPackageBodyNode; +} + +%type access_mode access_type aggregate_function alias_list all_noise +%type alter alter_clause alter_column_name +%type alter_data_type_or_domain alter_db alter_domain_op alter_domain_ops +%type alter_exception_clause alter_index_clause alter_op alter_ops +%type alter_role_clause alter_role_enable alter_sequence_clause +%type alter_udf_clause alter_user_clause alter_view_clause +%type arg_desc arg_desc_list arg_desc_list1 array_element array_range +%type array_spec array_type as_noise as_opt assignment assignments + +%type begin_string begin_trigger between_predicate bit_length_expression +%type blob_filter_subtype blob_io blob_segsize blob_subtype blob_subtype_io +%type blob_subtype_value_io blob_type block_input_params block_parameter +%type block_parameters breakleave + +%type case_abbreviation case_expression case_operand case_result case_specification +%type cast_specification char_length_expression character_keyword character_type +%type charset_clause check_constraint check_opt close_cursor col_opt collate_clause +%type collation_accent_attribute collation_attribute collation_attribute_list +%type collation_attribute_list_opt collation_case_attribute collation_clause +%type collation_pad_attribute collation_sequence_definition +%type collation_specific_attribute_opt column_constraint column_constraint_clause +%type column_constraint_def column_constraint_list column_def + +%type column_list column_name column_parens column_parens_opt column_select +%type column_singleton comment commit comparison_predicate complex_proc_statement +%type computed_by computed_clause conditional constant continue constraint_index_opt +%type constraint_name_opt containing_predicate correlation_name create +%type create_clause create_or_alter create_user_clause cross_join current_role +%type current_user cursor_clause cursor_declaration_item cursor_def +%type cursor_statement + +%type data_type data_type_or_domain datetime_value_expression +%type db_alter_clause db_clause db_file db_file_list db_initial_desc db_initial_desc1 +%type db_initial_option db_name db_rem_desc db_rem_desc1 db_rem_option ddl_subname +%type decimal_keyword declare declare_clause +%type decode_pairs def_computed default_par_opt default_value delete delete_positioned +%type delete_rule delete_searched delimiter_opt derived_column_list derived_table +%type distinct_clause distinct_noise distinct_predicate domain_clause domain_constraint +%type domain_constraint_clause domain_constraint_def domain_constraint_list +%type domain_default domain_default_opt domain_or_non_array_type +%type domain_or_non_array_type_name domain_type drop drop_behaviour +%type drop_clause drop_user_clause +%type ddl_desc + +%type end_default equals err errors event_argument_opt exception_clause +%type excp_hndl_statement excp_hndl_statements excp_statement +%type exec_function exec_into exec_procedure exec_sql exec_stmt_inputs +%type exec_stmt_option exec_stmt_options exec_stmt_options_list exists_predicate +%type ext_datasrc ext_privs ext_pwd ext_role ext_tran ext_user extra_indices_opt +%type extract_expression +%type end_trigger entry_op external_file + +%type fetch_cursor fetch_opt file1 file_clause file_clause_noise file_desc file_desc1 +%type filter_clause_io filter_decl_clause first_clause first_file_length +%type float_type for_exec_into for_select for_update_clause for_update_list from_clause +%type from_list full_proc_block full_proc_block_body function +%type firstname_opt + +%type generator_clause grant grant_option granted_by granted_by_text grantee grantee_list +%type grantor group_by_item group_by_list group_clause gtt_recreate_clause gtt_scope +%type gtt_table_clause + +%type having_clause + +%type if_then_else in_predicate in_predicate_value +%type index_definition index_list init_alter_db input_parameters +%type input_proc_parameter input_proc_parameters ins_column_list ins_column_parens +%type ins_column_parens_opt insert integer_keyword internal_info +%type iso_mode isolation_mode + +%type join_condition join_specification join_type joined_table + +%type keyword_or_column + +%type label_opt length_expression like_predicate limit_clause +%type local_declaration local_declaration_item local_declaration_list local_declarations +%type lock_clause lock_mode lock_type lock_wait long_integer +%type lastname_opt + +%type manual_auto merge merge_insert_specification merge_update_specification +%type merge_when_clause merge_when_matched_clause merge_when_not_matched_clause +%type middlename_opt module_op + +%type named_columns_join named_param named_params_list national_character_keyword +%type national_character_type natural_join neg_short_integer +%type next_value_expression non_aggregate_function non_array_type +%type non_charset_simple_type non_reserved_word non_role_grantee_list nonneg_short_integer +%type not_named_param not_named_params_list null_constraint null_predicate +%type null_value nulls_clause nulls_placement numeric_type numeric_value_function + +%type octet_length_expression open_cursor opt_snapshot optional_retain +%type optional_savepoint optional_work order_clause order_direction order_item order_list +%type outer_noise output_parameters output_proc_parameters + +%type page_noise param_mechanism parameter plan_clause +%type plan_expression plan_item plan_item_list plan_type pos_short_integer +%type post_event prec_scale precision_opt predicate primary_constraint privilege +%type privilege_list privileges proc_block proc_inputs proc_outputs_opt proc_parameter +%type proc_privileges proc_statement proc_statements +%type passwd_clause passwd_opt + +%type qualified_join quantified_predicate query_spec query_term + +%type raise_statement recreate recreate_clause referential_action referential_constraint +%type referential_trigger_action release_only_opt release_savepoint replace_clause +%type replace_exception_clause +%type replace_view_clause restr_list restr_option return_mechanism return_value +%type return_value1 returning_clause rev_admin_option rev_grant_option revoke +%type rexception_clause role_admin_option role_clause role_grantee role_grantee_list +%type role_name role_name_list rollback rows_clause rtable_clause +%type rview_clause + +%type savepoint search_condition searched_case searched_when_clause sec_precision_opt +%type sec_shadow_files segment_clause_io segment_length_io select select_expr +%type select_expr_body select_item select_items select_list set set_generator +%type set_savepoint set_statistics set_transaction shadow_clause signed_long_integer +%type signed_short_integer similar_predicate simple_case +%type simple_column_name simple_package_name simple_proc_name simple_proc_statement simple_table_name +%type simple_type simple_when_clause singleton_select singular_predicate skip_clause +%type snap_shot some starting_predicate statement stmt_start_column +%type stmt_start_line string_length_opt string_value_function substring_function +%type symbol_UDF_call_name symbol_UDF_name symbol_blob_subtype_name symbol_character_set_name +%type symbol_collation_name symbol_column_name symbol_constraint_name symbol_cursor_name +%type symbol_ddl_name symbol_domain_name symbol_exception_name symbol_filter_name +%type symbol_gdscode_name symbol_generator_name symbol_index_name symbol_item_alias_name +%type symbol_label_name symbol_procedure_name symbol_role_name symbol_savepoint_name +%type symbol_table_alias_name symbol_table_name symbol_trigger_name symbol_user_name +%type symbol_variable_name symbol_view_name system_function_expression +%type system_function_special_syntax system_function_std_syntax +%type sql_string + +%type table_clause table_constraint table_constraint_definition table_element table_elements +%type table_list table_lock table_name table_noise table_or_alias_list table_primary +%type table_proc table_proc_inputs table_reference table_subquery tbl_reserve_options +%type timestamp_part top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m +%type trigger_action_predicate +%type trim_function +%type trim_specification + +%type u_constant u_numeric_constant udf udf_data_type udf_decl_clause undo_savepoint +%type unique_constraint unique_opt unsigned_short_integer update update_column_name +%type update_or_insert update_or_insert_matching_opt update_positioned update_rule +%type update_searched user_grantee user_grantee_list + +%type valid_symbol_name value value_list value_list_opt var_decl_opt var_declaration_item +%type variable variable_list varying_keyword version_mode view_clause + +%type when_operand where_clause while window_function +%type with_clause with_item with_list + +%type external_body_clause_opt + +%type alter_col_name column_def_name data_type_descriptor init_data_type simple_column_def_name + +// New nodes + +%type ddl_type0 ddl_type1 ddl_type2 + +%type alter_charset_clause +%type in_autonomous_transaction exec_block + +%type alter_function_clause function_clause function_clause_start replace_function_clause +%type alter_procedure_clause procedure_clause procedure_clause_start replace_procedure_clause +%type external_clause + +%type trigger_active +%type trigger_db_type trigger_ddl_type trigger_ddl_type_items trigger_type +%type trigger_type_prefix trigger_type_suffix +%type trigger_type_opt +%type alter_trigger_clause replace_trigger_clause trigger_clause +%type trigger_position + +%type symbol_package_name +%type alter_package_clause package_clause replace_package_clause +%type package_body_clause +%type package_items_opt package_items package_body_items_opt package_body_items +%type package_item package_body_item + %% /* list of possible statements */ @@ -608,6 +827,9 @@ statement : alter | merge | exec_procedure | exec_block + { + $$ = makeClassNode($1); + } | recreate | revoke | rollback @@ -629,14 +851,19 @@ grant : GRANT privileges ON table_noise simple_table_name TO non_role_grantee_list grant_option granted_by { $$ = make_node (nod_grant, (int) e_grant_count, $2, $5, make_list($7), $8, $9); } + | GRANT proc_privileges ON PACKAGE simple_package_name + TO non_role_grantee_list grant_option granted_by + { $$ = make_node (nod_grant, (int) e_grant_count, + $2, $5, make_list($7), $8, $9); } | GRANT role_name_list TO role_grantee_list role_admin_option granted_by { $$ = make_node (nod_grant, (int) e_grant_count, make_list($2), make_list($4), NULL, $5, $6); } ; -table_noise : TABLE - | - ; +table_noise + : TABLE + | { $$ = NULL; } + ; privileges : ALL { $$ = make_node (nod_all, (int) 0, NULL); } @@ -693,9 +920,15 @@ grantor : role_grantee { $$ = $1; } ; -simple_proc_name: symbol_procedure_name - { $$ = make_node (nod_procedure_name, (int) 1, $1); } - ; +simple_package_name + : symbol_package_name + { $$ = make_node(nod_package_name, (int) 1, $1); } + ; + +simple_proc_name + : symbol_procedure_name + { $$ = make_node(nod_procedure_name, (int) 1, $1); } + ; /* REVOKE statement */ @@ -708,6 +941,10 @@ revoke : REVOKE rev_grant_option privileges ON table_noise simple_table_name FROM non_role_grantee_list granted_by { $$ = make_node (nod_revoke, (int) e_grant_count, $3, $6, make_list($8), $2, $9); } + | REVOKE rev_grant_option proc_privileges ON PACKAGE simple_package_name + FROM non_role_grantee_list granted_by + { $$ = make_node (nod_revoke, (int) e_grant_count, + $3, $6, make_list($8), $2, $9); } | REVOKE rev_admin_option role_name_list FROM role_grantee_list granted_by { $$ = make_node (nod_revoke, (int) e_grant_count, make_list($3), make_list($5), NULL, $2, $6); } @@ -741,8 +978,11 @@ grantee_list : grantee { $$ = make_node (nod_list, (int) 2, $1, $3); } ; -grantee : PROCEDURE symbol_procedure_name +grantee + : PROCEDURE symbol_procedure_name { $$ = make_node (nod_proc_obj, (int) 1, $2); } + | PACKAGE symbol_package_name + { $$ = make_node (nod_package_obj, (int) 1, $2); } | TRIGGER symbol_trigger_name { $$ = make_node (nod_trig_obj, (int) 1, $2); } | VIEW symbol_view_name @@ -794,18 +1034,21 @@ declare : DECLARE declare_clause { $$ = $2;} ; -declare_clause : FILTER filter_decl_clause - { $$ = $2; } - | EXTERNAL FUNCTION udf_decl_clause - { $$ = $3; } - ; +declare_clause + : FILTER filter_decl_clause + { $$ = $2; } + | EXTERNAL FUNCTION udf_decl_clause + { $$ = $3; } + ; - -udf_decl_clause : symbol_UDF_name arg_desc_list1 RETURNS return_value1 +udf_decl_clause + : symbol_UDF_name arg_desc_list1 RETURNS return_value1 ENTRY_POINT sql_string MODULE_NAME sql_string - { $$ = make_node (nod_def_udf, (int) e_udf_count, - $1, $6, $8, make_list ($2), $4); } - ; + { + $$ = make_node (nod_def_udf, (int) e_udf_count, + $1, $6, $8, make_list ($2), $4); + } + ; udf_data_type : simple_type | BLOB @@ -896,14 +1139,16 @@ create_clause : EXCEPTION exception_clause | unique_opt order_direction INDEX symbol_index_name ON simple_table_name index_definition { $$ = make_node (nod_def_index, (int) e_idx_count, $1, $2, $4, $6, $7); } + | FUNCTION function_clause + { $$ = makeClassNode($2); } | PROCEDURE procedure_clause - { $$ = $2; } + { $$ = makeClassNode($2); } | TABLE table_clause { $$ = $2; } | GLOBAL TEMPORARY TABLE gtt_table_clause { $$ = $4; } | TRIGGER trigger_clause - { $$ = $2; } + { $$ = makeClassNode($2); } | VIEW view_clause { $$ = $2; } | GENERATOR generator_clause @@ -922,6 +1167,10 @@ create_clause : EXCEPTION exception_clause { $$ = $2; } | USER create_user_clause { $$ = $2; } + | PACKAGE package_clause + { $$ = makeClassNode($2); } + | PACKAGE BODY package_body_clause + { $$ = makeClassNode($3); } ; @@ -929,37 +1178,47 @@ recreate : RECREATE recreate_clause { $$ = $2; } ; -recreate_clause : PROCEDURE rprocedure_clause - { $$ = $2; } - | TABLE rtable_clause - { $$ = $2; } - | GLOBAL TEMPORARY TABLE gtt_recreate_clause - { $$ = $4; } - | VIEW rview_clause - { $$ = $2; } - | TRIGGER rtrigger_clause - { $$ = $2; } +recreate_clause + : PROCEDURE procedure_clause + { $$ = makeClassNode(FB_NEW(getPool()) RecreateProcedureNode(getPool(), compilingText, $2)); } + | TABLE rtable_clause + { $$ = $2; } + | GLOBAL TEMPORARY TABLE gtt_recreate_clause + { $$ = $4; } + | VIEW rview_clause + { $$ = $2; } + | TRIGGER trigger_clause + { $$ = makeClassNode(FB_NEW(getPool()) RecreateTriggerNode(getPool(), compilingText, $2)); } + | PACKAGE package_clause + { $$ = makeClassNode(FB_NEW(getPool()) RecreatePackageNode(getPool(), compilingText, $2)); } + | PACKAGE BODY package_body_clause + { $$ = makeClassNode(FB_NEW(getPool()) RecreatePackageBodyNode(getPool(), compilingText, $3)); } /* - | DOMAIN rdomain_clause - { $$ = $2; } + | DOMAIN rdomain_clause + { $$ = $2; } */ - | EXCEPTION rexception_clause - { $$ = $2; } - ; + | EXCEPTION rexception_clause + { $$ = $2; } + ; create_or_alter : CREATE OR ALTER replace_clause { $$ = $4; } ; -replace_clause : PROCEDURE replace_procedure_clause - { $$ = $2; } - | TRIGGER replace_trigger_clause - { $$ = $2; } - | VIEW replace_view_clause - { $$ = $2; } - | EXCEPTION replace_exception_clause - { $$ = $2; } - ; +replace_clause + : PROCEDURE replace_procedure_clause + { $$ = makeClassNode($2); } + | FUNCTION replace_function_clause + { $$ = makeClassNode($2); } + | TRIGGER replace_trigger_clause + { $$ = makeClassNode($2); } + | PACKAGE replace_package_clause + { $$ = makeClassNode($2); } + | VIEW replace_view_clause + { $$ = $2; } + | EXCEPTION replace_exception_clause + { $$ = $2; } + ; /* CREATE EXCEPTION */ @@ -1177,8 +1436,7 @@ collation_specific_attribute_opt : alter_charset_clause : symbol_character_set_name SET DEFAULT COLLATION symbol_collation_name { - $$ = makeClassNode(FB_NEW(getPool()) - AlterCharSetNode(getPool(), toName($1), toName($5))); + $$ = FB_NEW(getPool()) AlterCharSetNode(getPool(), compilingText, toName($1), toName($5)); } ; @@ -1189,9 +1447,10 @@ db_clause : db_name db_initial_desc1 db_rem_desc1 $1, make_list($2), make_list ($3));} ; -equals : - | '=' - ; +equals + : { $$ = NULL; } + | '=' { $$ = NULL; } + ; db_name : sql_string { $$ = (dsql_nod*) $1; } @@ -1242,7 +1501,7 @@ db_rem_option : db_file ; db_file : file1 sql_string file_desc1 - { lex.g_file->fil_name = (dsql_str*) $2; + { lex.g_file->fil_name = $2; $$ = (dsql_nod*) make_node (nod_file_desc, (int) 1, (dsql_nod*) lex.g_file); } ; @@ -1251,9 +1510,10 @@ file1 : KW_FILE { lex.g_file = make_file();} ; -file_desc1 : - | file_desc - ; +file_desc1 + : { $$ = NULL; } + | file_desc + ; file_desc : file_clause | file_desc file_clause @@ -1265,15 +1525,17 @@ file_clause : STARTING file_clause_noise long_integer { lex.g_file->fil_length = (IPTR) $3;} ; -file_clause_noise : - | AT - | AT PAGE - ; +file_clause_noise + : { $$ = NULL; } + | AT + | AT PAGE + ; -page_noise : - | PAGE - | PAGES - ; +page_noise + : { $$ = NULL; } + | PAGE + | PAGES + ; /* CREATE TABLE */ @@ -1375,14 +1637,14 @@ column_def_name : simple_column_name { lex.g_field_name = $1; lex.g_field = make_field ($1); - $$ = (dsql_nod*) lex.g_field; + $$ = lex.g_field; } ; simple_column_def_name : simple_column_name { lex.g_field = make_field ($1); - $$ = (dsql_nod*) lex.g_field; + $$ = lex.g_field; } ; @@ -1391,7 +1653,7 @@ data_type_descriptor : init_data_type data_type { $$ = $1; } | KW_TYPE OF column_def_name { - ((dsql_fld*) $3)->fld_type_of_name = ((dsql_fld*) $3)->fld_name; + $3->fld_type_of_name = $3->fld_name; $$ = $3; } | KW_TYPE OF COLUMN symbol_column_name '.' symbol_column_name @@ -1399,20 +1661,23 @@ data_type_descriptor : init_data_type data_type lex.g_field = make_field(NULL); lex.g_field->fld_type_of_table = ((dsql_str*) $4); lex.g_field->fld_type_of_name = ((dsql_str*) $6)->str_data; - $$ = (dsql_nod*) lex.g_field; + $$ = lex.g_field; } | column_def_name { - ((dsql_fld*) $1)->fld_type_of_name = ((dsql_fld*) $1)->fld_name; - ((dsql_fld*) $1)->fld_full_domain = true; + $1->fld_type_of_name = $1->fld_name; + $1->fld_full_domain = true; $$ = $1; } ; -init_data_type : - { lex.g_field = make_field (NULL); - $$ = (dsql_nod*) lex.g_field; } - ; +init_data_type + : + { + lex.g_field = make_field(NULL); + $$ = lex.g_field; + } + ; default_value : constant @@ -1536,49 +1801,55 @@ referential_action: CASCADE /* PROCEDURE */ -procedure_clause : symbol_procedure_name input_parameters - output_parameters - AS begin_string - local_declaration_list - full_proc_block - end_trigger - { $$ = make_node (nod_def_procedure, - (int) e_prc_count, $1, $2, $3, $6, $7, $8); } - ; +procedure_clause + : procedure_clause_start AS begin_string local_declaration_list full_proc_block end_trigger + { + $$ = $1; + $$->source = toString($6); + $$->localDeclList = $4; + $$->body = $5; + } + | procedure_clause_start external_clause external_body_clause_opt + { + $$ = $1; + $$->external = $2; + if ($3) + $$->source = toString($3); + } + ; +procedure_clause_start + : symbol_procedure_name input_parameters output_parameters + { + $$ = FB_NEW(getPool()) CreateAlterProcedureNode(getPool(), compilingText, toName($1)); + ParameterClause::fromLegacyParameterList($$->parameters, $2); + ParameterClause::fromLegacyParameterList($$->returns, $3); + $$->legacyParameters = $2; + $$->legacyReturns = $3; + } + ; -rprocedure_clause : symbol_procedure_name input_parameters - output_parameters - AS begin_string - local_declaration_list - full_proc_block - end_trigger - { $$ = make_node (nod_redef_procedure, - (int) e_prc_count, $1, $2, $3, $6, $7, $8); } - ; +alter_procedure_clause + : procedure_clause + { + $$ = $1; + $$->alter = true; + $$->create = false; + } + ; -replace_procedure_clause : symbol_procedure_name input_parameters - output_parameters - AS begin_string - local_declaration_list - full_proc_block - end_trigger - { $$ = make_node (nod_replace_procedure, - (int) e_prc_count, $1, $2, $3, $6, $7, $8); } - ; - -alter_procedure_clause : symbol_procedure_name input_parameters - output_parameters - AS begin_string - local_declaration_list - full_proc_block - end_trigger - { $$ = make_node (nod_mod_procedure, - (int) e_prc_count, $1, $2, $3, $6, $7, $8); } - ; +replace_procedure_clause + : procedure_clause + { + $$ = $1; + $$->alter = true; + } + ; input_parameters : '(' input_proc_parameters ')' { $$ = make_list ($2); } + | '(' ')' + { $$ = NULL; } | { $$ = NULL; } ; @@ -1618,6 +1889,190 @@ default_par_opt : DEFAULT begin_trigger default_value end_default { $$ = NULL; } ; + +// FUNCTION + +function_clause + : function_clause_start external_clause external_body_clause_opt + { + $$ = $1; + $$->external = $2; + if ($3) + $$->source = toString($3); + } + ; + +function_clause_start + : symbol_UDF_name input_parameters + RETURNS + { $$ = lex.g_field = make_field(NULL); } + domain_or_non_array_type collate_clause + { + $$ = FB_NEW(getPool()) CreateAlterFunctionNode(getPool(), compilingText, toName($1), + TypeClause($4, (dsql_str*) $6)); + ParameterClause::fromLegacyParameterList($$->parameters, $2); + } + ; + +external_clause + : EXTERNAL NAME sql_string ENGINE valid_symbol_name + { + $$ = FB_NEW(getPool()) ExternalClause(getPool()); + $$->name = toString($3); + $$->engine = toName($5); + } + | EXTERNAL ENGINE valid_symbol_name + { + $$ = FB_NEW(getPool()) ExternalClause(getPool()); + $$->engine = toName($3); + } + ; + +external_body_clause_opt + : AS sql_string + { $$ = $2; } + | + { $$ = NULL; } + ; + +alter_function_clause + : function_clause + { + $$ = $1; + $$->alter = true; + $$->create = false; + } + ; + +replace_function_clause + : function_clause + { + $$ = $1; + $$->alter = true; + } + ; + + +// PACKAGE + +package_clause + : symbol_package_name AS begin_string stmt_start_line stmt_start_column + BEGIN package_items_opt END end_trigger + { + CreateAlterPackageNode* node = FB_NEW(getPool()) CreateAlterPackageNode( + getPool(), compilingText, toName($1)); + node->source = toString($9); + node->items = $7; + + $$ = node; + } + ; + +package_items_opt + : package_items + | + { $$ = FB_NEW(getPool()) Array(getPool()); } + ; + +package_items + : package_item + { + $$ = FB_NEW(getPool()) Array(getPool()); + $$->add($1); + } + | package_items package_item + { + $$ = $1; + $$->add($2); + } + ; + +package_item + : FUNCTION function_clause_start ';' + { + $$ = CreateAlterPackageNode::Item::create($2); + } + | PROCEDURE procedure_clause_start ';' + { + $$ = CreateAlterPackageNode::Item::create($2); + } + ; + +alter_package_clause + : package_clause + { + $$ = $1; + $$->alter = true; + $$->create = false; + } + ; + +replace_package_clause + : package_clause + { + $$ = $1; + $$->alter = true; + } + ; + + +// PACKAGE BODY + +package_body_clause + : symbol_package_name AS begin_string stmt_start_line stmt_start_column + BEGIN package_items package_body_items_opt END end_trigger + { + CreatePackageBodyNode* node = FB_NEW(getPool()) CreatePackageBodyNode( + getPool(), compilingText, toName($1)); + node->source = toString($10); + node->declaredItems = $7; + node->items = $8; + + $$ = node; + } + | symbol_package_name AS begin_string stmt_start_line stmt_start_column + BEGIN package_body_items_opt END end_trigger + { + CreatePackageBodyNode* node = FB_NEW(getPool()) CreatePackageBodyNode( + getPool(), compilingText, toName($1)); + node->source = toString($9); + node->items = $7; + + $$ = node; + } + ; + +package_body_items_opt + : package_body_items + | + { $$ = FB_NEW(getPool()) Array(getPool()); } + ; + +package_body_items + : package_body_item + { + $$ = FB_NEW(getPool()) Array(getPool()); + $$->add($1); + } + | package_body_items package_body_item + { + $$ = $1; + $$->add($2); + } + ; + +package_body_item + : FUNCTION function_clause ';' + { + $$ = CreateAlterPackageNode::Item::create($2); + } + | PROCEDURE procedure_clause ';' + { + $$ = CreateAlterPackageNode::Item::create($2); + } + ; + + local_declaration_list : local_declarations { $$ = make_list ($1); } | @@ -1715,6 +2170,7 @@ simple_proc_statement : assignment | post_event | cursor_statement | breakleave + | continue | SUSPEND { $$ = make_node (nod_return, (int) e_rtn_count, NULL); } | EXIT @@ -1723,6 +2179,9 @@ simple_proc_statement : assignment complex_proc_statement : in_autonomous_transaction + { + $$ = makeClassNode($1); + } | if_then_else | while | for_select @@ -1735,7 +2194,7 @@ in_autonomous_transaction InAutonomousTransactionNode* node = FB_NEW(getPool()) InAutonomousTransactionNode(getPool()); node->dsqlAction = $5; - $$ = makeClassNode(node); + $$ = node; } ; @@ -1934,6 +2393,16 @@ breakleave : KW_BREAK make_node (nod_label, (int) e_label_count, $2, NULL)); } ; +continue + : CONTINUE + { $$ = make_node(nod_continue, (int) e_continue_count, NULL); } + | CONTINUE symbol_label_name + { + $$ = make_node(nod_continue, (int) e_continue_count, + make_node(nod_label, (int) e_label_count, $2, NULL)); + } + ; + cursor_def : AS CURSOR symbol_cursor_name { $$ = make_flag_node (nod_cursor, NOD_CURSOR_FOR, (int) e_cur_count, $3, NULL, NULL, NULL); } @@ -2026,10 +2495,12 @@ fetch_seek_opt : /* EXECUTE PROCEDURE */ -exec_procedure : EXECUTE PROCEDURE symbol_procedure_name proc_inputs proc_outputs_opt - { $$ = make_node (nod_exec_procedure, (int) e_exe_count, - $3, $4, $5); } - ; +exec_procedure + : EXECUTE PROCEDURE symbol_procedure_name proc_inputs proc_outputs_opt + { $$ = make_node (nod_exec_procedure, (int) e_exe_count, $3, $4, $5, NULL); } + | EXECUTE PROCEDURE symbol_package_name '.' symbol_procedure_name proc_inputs proc_outputs_opt + { $$ = make_node (nod_exec_procedure, (int) e_exe_count, $5, $6, $7, $3); } + ; proc_inputs : value_list { $$ = make_list ($1); } @@ -2049,13 +2520,20 @@ proc_outputs_opt : RETURNING_VALUES variable_list /* EXECUTE BLOCK */ -exec_block : EXECUTE BLOCK block_input_params output_parameters AS +exec_block + : EXECUTE BLOCK block_input_params output_parameters AS local_declaration_list full_proc_block - { $$ = make_node (nod_exec_block, - (int) e_exe_blk_count, - $3, $4, $6, $7, make_node (nod_all, (int) 0, NULL)); } - ; + { + ExecBlockNode* node = FB_NEW(getPool()) ExecBlockNode(getPool()); + node->legacyParameters = $3; + node->legacyReturns = $4; + node->localDeclList = $6; + node->body = $7; + $$ = node; + } + ; + block_input_params : '(' block_parameters ')' { $$ = make_list ($2); } @@ -2123,7 +2601,7 @@ end_trigger : const TEXT* start = lex.beginnings.pop(); string str; transformString(start, lex_position() - start, str); - $$ = (dsql_nod*) MAKE_string(str.c_str(), str.length()); + $$ = MAKE_string(str.c_str(), str.length()); } ; @@ -2147,182 +2625,248 @@ check_opt : WITH CHECK OPTION /* CREATE TRIGGER */ trigger_clause - : symbol_trigger_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger + : symbol_trigger_name + trigger_active + trigger_type + trigger_position + AS begin_trigger + local_declaration_list + full_proc_block + end_trigger { - $$ = make_node (nod_def_trigger, (int) e_trg_count, - $1, NULL, $2, $3, $4, $5, $6); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->source = toString($9); + $$->localDeclList = $7; + $$->body = $8; } - | symbol_trigger_name FOR simple_table_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger + | symbol_trigger_name + trigger_active + trigger_type + trigger_position + external_clause external_body_clause_opt { - $$ = make_node (nod_def_trigger, (int) e_trg_count, - $1, $3, $4, $5, $6, $7, $8); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->external = $5; + if ($6) + $$->source = toString($6); } - | symbol_trigger_name - trigger_active - trigger_type - trigger_position - ON simple_table_name - trigger_action - end_trigger + | symbol_trigger_name + trigger_active + trigger_type + trigger_position + ON symbol_table_name + AS begin_trigger + local_declaration_list + full_proc_block + end_trigger { - $$ = make_node (nod_def_trigger, (int) e_trg_count, - $1, $6, $2, $3, $4, $7, $8); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->relationName = toName($6); + $$->source = toString($11); + $$->localDeclList = $9; + $$->body = $10; } - ; - -rtrigger_clause - : symbol_trigger_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger + | symbol_trigger_name + trigger_active + trigger_type + trigger_position + ON symbol_table_name + external_clause external_body_clause_opt { - $$ = make_node (nod_redef_trigger, (int) e_trg_count, - $1, NULL, $2, $3, $4, $5, $6); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->relationName = toName($6); + $$->external = $7; + if ($8) + $$->source = toString($8); } - | symbol_trigger_name FOR simple_table_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger + | symbol_trigger_name + FOR symbol_table_name + trigger_active + trigger_type + trigger_position + AS begin_trigger + local_declaration_list + full_proc_block + end_trigger { - $$ = make_node (nod_redef_trigger, (int) e_trg_count, - $1, $3, $4, $5, $6, $7, $8); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $4; + $$->type = $5; + $$->position = $6; + $$->relationName = toName($3); + $$->source = toString($11); + $$->localDeclList = $9; + $$->body = $10; } - | symbol_trigger_name - trigger_active - trigger_type - trigger_position - ON simple_table_name - trigger_action - end_trigger + | symbol_trigger_name + FOR symbol_table_name + trigger_active + trigger_type + trigger_position + external_clause external_body_clause_opt { - $$ = make_node (nod_redef_trigger, (int) e_trg_count, - $1, $6, $2, $3, $4, $7, $8); + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->active = $4; + $$->type = $5; + $$->position = $6; + $$->relationName = toName($3); + $$->external = $7; + if ($8) + $$->source = toString($8); } ; replace_trigger_clause - : symbol_trigger_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger + : trigger_clause { - $$ = make_node (nod_replace_trigger, (int) e_trg_count, - $1, NULL, $2, $3, $4, $5, $6); - } - | symbol_trigger_name FOR simple_table_name - trigger_active - trigger_type - trigger_position - trigger_action - end_trigger - { - $$ = make_node (nod_replace_trigger, (int) e_trg_count, - $1, $3, $4, $5, $6, $7, $8); - } - | symbol_trigger_name - trigger_active - trigger_type - trigger_position - ON simple_table_name - trigger_action - end_trigger - { - $$ = make_node (nod_replace_trigger, (int) e_trg_count, - $1, $6, $2, $3, $4, $7, $8); + $$ = $1; + $$->alter = true; } ; -trigger_active : ACTIVE - { $$ = MAKE_const_slong (0); } - | INACTIVE - { $$ = MAKE_const_slong (1); } - | - { $$ = NULL; } - ; +trigger_active + : ACTIVE + { $$ = TriStateRawType::val(true); } + | INACTIVE + { $$ = TriStateRawType::val(false); } + | + { $$ = TriStateRawType::empty(); } + ; trigger_type - : trigger_type_prefix trigger_type_suffix - { $$ = MAKE_trigger_type ($1, $2); } - | ON trigger_db_type - { $$ = $2; } + : trigger_type_prefix trigger_type_suffix + { $$ = $1 + $2 - 1; } + | ON trigger_db_type + { $$ = $2; } + | trigger_type_prefix trigger_ddl_type + { $$ = $1 + $2; } ; trigger_db_type - : CONNECT - { $$ = MAKE_const_slong (TRIGGER_TYPE_DB | DB_TRIGGER_CONNECT); } - | DISCONNECT - { $$ = MAKE_const_slong (TRIGGER_TYPE_DB | DB_TRIGGER_DISCONNECT); } - | TRANSACTION START - { $$ = MAKE_const_slong (TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_START); } - | TRANSACTION COMMIT - { $$ = MAKE_const_slong (TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_COMMIT); } - | TRANSACTION ROLLBACK - { $$ = MAKE_const_slong (TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_ROLLBACK); } + : CONNECT + { $$ = TRIGGER_TYPE_DB | DB_TRIGGER_CONNECT; } + | DISCONNECT + { $$ = TRIGGER_TYPE_DB | DB_TRIGGER_DISCONNECT; } + | TRANSACTION START + { $$ = TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_START; } + | TRANSACTION COMMIT + { $$ = TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_COMMIT; } + | TRANSACTION ROLLBACK + { $$ = TRIGGER_TYPE_DB | DB_TRIGGER_TRANS_ROLLBACK; } ; -trigger_type_prefix : BEFORE - { $$ = MAKE_const_slong (0); } - | AFTER - { $$ = MAKE_const_slong (1); } - ; +trigger_ddl_type + : trigger_ddl_type_items + | ANY DDL STATEMENT + { + $$ = TRIGGER_TYPE_DDL | (0x7FFFFFFFFFFFFFFFULL & ~(FB_UINT64) TRIGGER_TYPE_MASK & ~1ULL); + } + ; -trigger_type_suffix : INSERT - { $$ = MAKE_const_slong (trigger_type_suffix (1, 0, 0)); } - | UPDATE - { $$ = MAKE_const_slong (trigger_type_suffix (2, 0, 0)); } - | KW_DELETE - { $$ = MAKE_const_slong (trigger_type_suffix (3, 0, 0)); } - | INSERT OR UPDATE - { $$ = MAKE_const_slong (trigger_type_suffix (1, 2, 0)); } - | INSERT OR KW_DELETE - { $$ = MAKE_const_slong (trigger_type_suffix (1, 3, 0)); } - | UPDATE OR INSERT - { $$ = MAKE_const_slong (trigger_type_suffix (2, 1, 0)); } - | UPDATE OR KW_DELETE - { $$ = MAKE_const_slong (trigger_type_suffix (2, 3, 0)); } - | KW_DELETE OR INSERT - { $$ = MAKE_const_slong (trigger_type_suffix (3, 1, 0)); } - | KW_DELETE OR UPDATE - { $$ = MAKE_const_slong (trigger_type_suffix (3, 2, 0)); } - | INSERT OR UPDATE OR KW_DELETE - { $$ = MAKE_const_slong (trigger_type_suffix (1, 2, 3)); } - | INSERT OR KW_DELETE OR UPDATE - { $$ = MAKE_const_slong (trigger_type_suffix (1, 3, 2)); } - | UPDATE OR INSERT OR KW_DELETE - { $$ = MAKE_const_slong (trigger_type_suffix (2, 1, 3)); } - | UPDATE OR KW_DELETE OR INSERT - { $$ = MAKE_const_slong (trigger_type_suffix (2, 3, 1)); } - | KW_DELETE OR INSERT OR UPDATE - { $$ = MAKE_const_slong (trigger_type_suffix (3, 1, 2)); } - | KW_DELETE OR UPDATE OR INSERT - { $$ = MAKE_const_slong (trigger_type_suffix (3, 2, 1)); } - ; +trigger_ddl_type_items + : CREATE TABLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_TABLE); } + | ALTER TABLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_TABLE); } + | DROP TABLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_TABLE); } + | CREATE PROCEDURE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_PROCEDURE); } + | ALTER PROCEDURE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_PROCEDURE); } + | DROP PROCEDURE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_PROCEDURE); } + | CREATE FUNCTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_FUNCTION); } + | ALTER FUNCTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_FUNCTION); } + | DROP FUNCTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_FUNCTION); } + | CREATE TRIGGER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_TRIGGER); } + | ALTER TRIGGER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_TRIGGER); } + | DROP TRIGGER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_TRIGGER); } + | CREATE EXCEPTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_EXCEPTION); } + | ALTER EXCEPTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_EXCEPTION); } + | DROP EXCEPTION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_EXCEPTION); } + | CREATE VIEW { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_VIEW); } + | ALTER VIEW { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_VIEW); } + | DROP VIEW { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_VIEW); } + | CREATE DOMAIN { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_DOMAIN); } + | ALTER DOMAIN { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_DOMAIN); } + | DROP DOMAIN { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_DOMAIN); } + | CREATE ROLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_ROLE); } + | ALTER ROLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_ROLE); } + | DROP ROLE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_ROLE); } + | CREATE SEQUENCE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_SEQUENCE); } + | ALTER SEQUENCE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_SEQUENCE); } + | DROP SEQUENCE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_SEQUENCE); } + | CREATE USER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_USER); } + | ALTER USER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_USER); } + | DROP USER { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_USER); } + | CREATE INDEX { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_INDEX); } + | ALTER INDEX { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_INDEX); } + | DROP INDEX { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_INDEX); } + | CREATE COLLATION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_COLLATION); } + | DROP COLLATION { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_COLLATION); } + | ALTER CHARACTER SET { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_CHARACTER_SET); } + | CREATE PACKAGE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_PACKAGE); } + | ALTER PACKAGE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_ALTER_PACKAGE); } + | DROP PACKAGE { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_PACKAGE); } + | CREATE PACKAGE BODY { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_CREATE_PACKAGE_BODY); } + | DROP PACKAGE BODY { $$ = TRIGGER_TYPE_DDL | (1LL << DDL_TRIGGER_DROP_PACKAGE_BODY); } + | trigger_ddl_type OR + trigger_ddl_type { $$ = $1 | $3; } + ; -trigger_position : POSITION nonneg_short_integer - { $$ = MAKE_const_slong ((IPTR) $2); } - | - { $$ = NULL; } - ; +trigger_type_prefix + : BEFORE + { $$ = 0; } + | AFTER + { $$ = 1; } + ; -trigger_action : AS begin_trigger local_declaration_list full_proc_block - { $$ = make_node (nod_list, (int) e_trg_act_count, $3, $4); } - ; +trigger_type_suffix + : INSERT + { $$ = trigger_type_suffix(1, 0, 0); } + | UPDATE + { $$ = trigger_type_suffix(2, 0, 0); } + | KW_DELETE + { $$ = trigger_type_suffix(3, 0, 0); } + | INSERT OR UPDATE + { $$ = trigger_type_suffix(1, 2, 0); } + | INSERT OR KW_DELETE + { $$ = trigger_type_suffix(1, 3, 0); } + | UPDATE OR INSERT + { $$ = trigger_type_suffix(2, 1, 0); } + | UPDATE OR KW_DELETE + { $$ = trigger_type_suffix(2, 3, 0); } + | KW_DELETE OR INSERT + { $$ = trigger_type_suffix(3, 1, 0); } + | KW_DELETE OR UPDATE + { $$ = trigger_type_suffix(3, 2, 0); } + | INSERT OR UPDATE OR KW_DELETE + { $$ = trigger_type_suffix(1, 2, 3); } + | INSERT OR KW_DELETE OR UPDATE + { $$ = trigger_type_suffix(1, 3, 2); } + | UPDATE OR INSERT OR KW_DELETE + { $$ = trigger_type_suffix(2, 1, 3); } + | UPDATE OR KW_DELETE OR INSERT + { $$ = trigger_type_suffix(2, 3, 1); } + | KW_DELETE OR INSERT OR UPDATE + { $$ = trigger_type_suffix(3, 1, 2); } + | KW_DELETE OR UPDATE OR INSERT + { $$ = trigger_type_suffix(3, 2, 1); } + ; + +trigger_position + : POSITION nonneg_short_integer + { $$ = TriStateRawType::val((IPTR) $2); } + | + { $$ = TriStateRawType::empty(); } + ; /* ALTER statement */ @@ -2338,9 +2882,11 @@ alter_clause : EXCEPTION alter_exception_clause | VIEW alter_view_clause { $$ = $2; } | TRIGGER alter_trigger_clause - { $$ = $2; } + { $$ = makeClassNode($2); } | PROCEDURE alter_procedure_clause - { $$ = $2; } + { $$ = makeClassNode($2); } + | PACKAGE alter_package_clause + { $$ = makeClassNode($2); } | DATABASE init_alter_db alter_db { $$ = make_node (nod_mod_database, (int) e_adb_count, make_list ($3)); } @@ -2351,14 +2897,16 @@ alter_clause : EXCEPTION alter_exception_clause { $$ = make_node (nod_mod_index, (int) e_mod_idx_count, $2); } | SEQUENCE alter_sequence_clause { $$ = $2; } - | EXTERNAL FUNCTION alter_udf_clause - { $$ = $3; } + | EXTERNAL FUNCTION alter_udf_clause + { $$ = $3; } + | FUNCTION alter_function_clause + { $$ = makeClassNode($2); } | ROLE alter_role_clause { $$ = $2; } | USER alter_user_clause { $$ = $2; } | CHARACTER SET alter_charset_clause - { $$ = $3; } + { $$ = makeClassNode($3); } ; alter_domain_ops : alter_domain_op @@ -2404,6 +2952,16 @@ alter_op : DROP simple_column_name drop_behaviour MAKE_const_slong((IPTR) $4)); } | col_opt alter_column_name TO simple_column_name { $$ = make_node(nod_mod_field_name, 2, $2, $4); } + | col_opt alter_column_name KW_NULL + { + $$ = make_node(nod_mod_field_null_flag, e_mod_fld_null_flag_count, + $2, MAKE_const_slong(0)); + } + | col_opt alter_column_name NOT KW_NULL + { + $$ = make_node(nod_mod_field_null_flag, e_mod_fld_null_flag_count, + $2, MAKE_const_slong(1)); + } | col_opt alter_col_name KW_TYPE alter_data_type_or_domain { $$ = make_node(nod_mod_field_type, e_mod_fld_type_count, $2, $4, NULL, NULL); } | col_opt alter_col_name KW_TYPE non_array_type def_computed @@ -2494,7 +3052,8 @@ alter_data_type_or_domain : non_array_type alter_col_name : simple_column_name { lex.g_field_name = $1; lex.g_field = make_field ($1); - $$ = (dsql_nod*) lex.g_field; } + $$ = lex.g_field; + } ; drop_behaviour : RESTRICT @@ -2522,9 +3081,10 @@ alter_sequence_clause : symbol_generator_name RESTART WITH signed_long_integer make_node(nod_negate, 1, MAKE_constant((dsql_str*) $5, CONSTANT_SINT64))); } ; -alter_udf_clause : symbol_UDF_name entry_op module_op - { $$ = make_node(nod_mod_udf, e_mod_udf_count, $1, $2, $3); } - ; +alter_udf_clause + : symbol_UDF_name entry_op module_op + { $$ = make_node(nod_mod_udf, e_mod_udf_count, $1, $2, $3); } + ; /* alter_role_clause : symbol_role_name alter_role_action OS_NAME os_security_name @@ -2591,25 +3151,63 @@ db_alter_clause : ADD db_file_list /* ALTER TRIGGER */ -alter_trigger_clause : symbol_trigger_name trigger_active - new_trigger_type - trigger_position - begin_trigger - new_trigger_action - end_trigger - { $$ = make_node (nod_mod_trigger, (int) e_trg_count, - $1, NULL, $2, $3, $4, $6, $7); } - ; +alter_trigger_clause + : symbol_trigger_name + trigger_active + trigger_type_opt + trigger_position + AS begin_trigger + local_declaration_list + full_proc_block + end_trigger + { + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->alter = true; + $$->create = false; + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->source = toString($9); + $$->localDeclList = $7; + $$->body = $8; + } + | symbol_trigger_name + trigger_active + trigger_type_opt + trigger_position + external_clause external_body_clause_opt + { + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->alter = true; + $$->create = false; + $$->active = $2; + $$->type = $3; + $$->position = $4; + $$->external = $5; + if ($6) + $$->source = toString($6); + } + | symbol_trigger_name + trigger_active + trigger_type_opt + trigger_position + { + $$ = FB_NEW(getPool()) CreateAlterTriggerNode(getPool(), compilingText, toName($1)); + $$->alter = true; + $$->create = false; + $$->active = $2; + $$->type = $3; + $$->position = $4; + } + ; -new_trigger_type : trigger_type - | - { $$ = NULL; } - ; +trigger_type_opt // we do not allow alter database triggers, hence we do not use trigger_type here + : trigger_type_prefix trigger_type_suffix + { $$ = TriStateRawType::val($1 + $2 - 1); } + | + { $$ = TriStateRawType::empty(); } + ; -new_trigger_action : trigger_action - | - { $$ = NULL; } - ; /* DROP metadata operations */ @@ -2622,11 +3220,11 @@ drop_clause : EXCEPTION symbol_exception_name | INDEX symbol_index_name { $$ = make_node (nod_del_index, (int) 1, $2); } | PROCEDURE symbol_procedure_name - { $$ = make_node (nod_del_procedure, (int) 1, $2); } + { $$ = makeClassNode(FB_NEW(getPool()) DropProcedureNode(getPool(), compilingText, toName($2))); } | TABLE symbol_table_name { $$ = make_node (nod_del_relation, (int) 1, $2); } | TRIGGER symbol_trigger_name - { $$ = make_node (nod_del_trigger, (int) 1, $2); } + { $$ = makeClassNode(FB_NEW(getPool()) DropTriggerNode(getPool(), compilingText, toName($2))); } | VIEW symbol_view_name { $$ = make_node (nod_del_view, (int) 1, $2); } | FILTER symbol_filter_name @@ -2634,7 +3232,9 @@ drop_clause : EXCEPTION symbol_exception_name | DOMAIN symbol_domain_name { $$ = make_node (nod_del_domain, (int) 1, $2); } | EXTERNAL FUNCTION symbol_UDF_name - { $$ = make_node (nod_del_udf, (int) 1, $3); } + { $$ = makeClassNode(FB_NEW(getPool()) DropFunctionNode(getPool(), compilingText, toName($3))); } + | FUNCTION symbol_UDF_name + { $$ = makeClassNode(FB_NEW(getPool()) DropFunctionNode(getPool(), compilingText, toName($2))); } | SHADOW pos_short_integer { $$ = make_node (nod_del_shadow, (int) 1, $2); } | ROLE symbol_role_name @@ -2647,6 +3247,10 @@ drop_clause : EXCEPTION symbol_exception_name { $$ = make_node (nod_del_collation, (int) 1, $2); } | USER drop_user_clause { $$ = $2; } + | PACKAGE symbol_package_name + { $$ = makeClassNode(FB_NEW(getPool()) DropPackageNode(getPool(), compilingText, toName($2))); } + | PACKAGE BODY symbol_package_name + { $$ = makeClassNode(FB_NEW(getPool()) DropPackageBodyNode(getPool(), compilingText, toName($3))); } ; @@ -2848,12 +3452,13 @@ blob_subtype : SUB_TYPE signed_short_integer } ; -charset_clause : CHARACTER SET symbol_character_set_name - { +charset_clause + : CHARACTER SET symbol_character_set_name + { lex.g_field->fld_character_set = $3; - } - | - ; + } + | { $$ = NULL; } + ; /* character type */ @@ -2864,18 +3469,21 @@ national_character_type : national_character_keyword '(' pos_short_integer ')' lex.g_field->fld_dtype = dtype_text; lex.g_field->fld_character_length = (USHORT)(IPTR) $3; lex.g_field->fld_flags |= FLD_national; + $$ = NULL; } | national_character_keyword { lex.g_field->fld_dtype = dtype_text; lex.g_field->fld_character_length = 1; lex.g_field->fld_flags |= FLD_national; + $$ = NULL; } | national_character_keyword VARYING '(' pos_short_integer ')' { lex.g_field->fld_dtype = dtype_varying; lex.g_field->fld_character_length = (USHORT)(IPTR) $4; lex.g_field->fld_flags |= FLD_national; + $$ = NULL; } ; @@ -3124,9 +3732,10 @@ undo_savepoint : ROLLBACK optional_work TO optional_savepoint symbol_savepoint_n { $$ = make_node (nod_undo_savepoint, 1, $5); } ; -optional_savepoint : SAVEPOINT - | - ; +optional_savepoint + : SAVEPOINT + | { $$ = NULL; } + ; commit : COMMIT optional_work optional_retain { $$ = make_node (nod_commit, e_commit_count, $3); } @@ -3136,9 +3745,10 @@ rollback : ROLLBACK optional_work optional_retain { $$ = make_node (nod_rollback, e_rollback_count, $3); } ; -optional_work : WORK - | - ; +optional_work + : WORK + | { $$ = NULL; } + ; optional_retain : RETAIN opt_snapshot { $$ = make_node (nod_retain, 0, NULL); } @@ -3269,66 +3879,85 @@ set_statistics : SET STATISTICS INDEX symbol_index_name { $$ = make_node (nod_set_statistics, (int) e_stat_count, $4); } ; -comment : COMMENT ON ddl_type0 IS ddl_desc - { $$ = make_node(nod_comment, e_comment_count, $3, NULL, NULL, $5); } - | COMMENT ON ddl_type1 symbol_ddl_name IS ddl_desc - { $$ = make_node(nod_comment, e_comment_count, $3, $4, NULL, $6); } - | COMMENT ON ddl_type2 symbol_ddl_name ddl_subname IS ddl_desc - { $$ = make_node(nod_comment, e_comment_count, $3, $4, $5, $7); } - ; +comment + : COMMENT ON ddl_type0 IS ddl_desc + { + $$ = makeClassNode(FB_NEW(getPool()) CommentOnNode(getPool(), compilingText, $3, + "", "", ($5 ? toString($5) : ""))); + } + | COMMENT ON ddl_type1 symbol_ddl_name IS ddl_desc + { + $$ = makeClassNode(FB_NEW(getPool()) CommentOnNode(getPool(), compilingText, $3, + toName($4), "", ($6 ? toString($6) : ""))); + } + | COMMENT ON ddl_type2 symbol_ddl_name ddl_subname IS ddl_desc + { + $$ = makeClassNode(FB_NEW(getPool()) CommentOnNode(getPool(), compilingText, $3, + toName($4), toName($5), ($7 ? toString($7) : ""))); + } + ; -ddl_type0 : DATABASE - { $$ = MAKE_const_slong(ddl_database); } - ; +ddl_type0 + : DATABASE + { $$ = ddl_database; } + ; -ddl_type1 : DOMAIN - { $$ = MAKE_const_slong(ddl_domain); } - | TABLE - { $$ = MAKE_const_slong(ddl_relation); } - | VIEW - { $$ = MAKE_const_slong(ddl_view); } - | PROCEDURE - { $$ = MAKE_const_slong(ddl_procedure); } - | TRIGGER - { $$ = MAKE_const_slong(ddl_trigger); } - | EXTERNAL FUNCTION - { $$ = MAKE_const_slong(ddl_udf); } - | FILTER - { $$ = MAKE_const_slong(ddl_blob_filter); } - | EXCEPTION - { $$ = MAKE_const_slong(ddl_exception); } - | GENERATOR - { $$ = MAKE_const_slong(ddl_generator); } - | SEQUENCE - { $$ = MAKE_const_slong(ddl_generator); } - | INDEX - { $$ = MAKE_const_slong(ddl_index); } - | ROLE - { $$ = MAKE_const_slong(ddl_role); } - | CHARACTER SET - { $$ = MAKE_const_slong(ddl_charset); } - | COLLATION - { $$ = MAKE_const_slong(ddl_collation); } +ddl_type1 + : DOMAIN + { $$ = ddl_domain; } + | TABLE + { $$ = ddl_relation; } + | VIEW + { $$ = ddl_view; } + | PROCEDURE + { $$ = ddl_procedure; } + | TRIGGER + { $$ = ddl_trigger; } + | EXTERNAL FUNCTION + { $$ = ddl_udf; } + | FUNCTION + { $$ = ddl_udf; } + | FILTER + { $$ = ddl_blob_filter; } + | EXCEPTION + { $$ = ddl_exception; } + | GENERATOR + { $$ = ddl_generator; } + | SEQUENCE + { $$ = ddl_generator; } + | INDEX + { $$ = ddl_index; } + | ROLE + { $$ = ddl_role; } + | CHARACTER SET + { $$ = ddl_charset; } + | COLLATION + { $$ = ddl_collation; } + | PACKAGE + { $$ = ddl_package; } /* - | SECURITY CLASS - { $$ = MAKE_const_slong(ddl_sec_class); } + | SECURITY CLASS + { $$ = ddl_sec_class; } */ - ; + ; -ddl_type2 : COLUMN - { $$ = MAKE_const_slong(ddl_relation); } - | PARAMETER - { $$ = MAKE_const_slong(ddl_procedure); } - ; +ddl_type2 + : COLUMN + { $$ = ddl_relation; } + | PARAMETER + { $$ = ddl_procedure; } + ; -ddl_subname : '.' symbol_ddl_name - { $$ = $2; } - ; - -ddl_desc : sql_string - | KW_NULL - { $$ = NULL; } - ; +ddl_subname + : '.' symbol_ddl_name + { $$ = $2; } + ; + +ddl_desc + : sql_string + | KW_NULL + { $$ = NULL; } + ; /* SELECT statement */ @@ -3459,20 +4088,23 @@ select_item : value { $$ = make_node (nod_alias, 2, $1, $3); } ; -as_noise : AS - | - ; +as_noise + : AS + | { $$ = NULL; } + ; /* FROM clause */ -from_clause : FROM from_list - { $$ = make_list ($2); } - ; +from_clause + : FROM from_list + { $$ = make_list ($2); } + ; -from_list : table_reference - | from_list ',' table_reference - { $$ = make_node (nod_list, 2, $1, $3); } - ; +from_list + : table_reference + | from_list ',' table_reference + { $$ = make_node (nod_list, 2, $1, $3); } + ; table_reference : joined_table | table_primary @@ -3537,13 +4169,16 @@ named_columns_join : USING '(' column_list ')' { $$ = make_list ($3); } ; -table_proc : symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name - { $$ = make_node (nod_rel_proc_name, - (int) e_rpn_count, $1, $4, $2); } - | symbol_procedure_name table_proc_inputs - { $$ = make_node (nod_rel_proc_name, - (int) e_rpn_count, $1, NULL, $2); } - ; +table_proc + : symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name + { $$ = make_node (nod_rel_proc_name, (int) e_rpn_count, $1, $4, $2, NULL); } + | symbol_procedure_name table_proc_inputs + { $$ = make_node (nod_rel_proc_name, (int) e_rpn_count, $1, NULL, $2, NULL); } + | symbol_package_name '.' symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name + { $$ = make_node (nod_rel_proc_name, (int) e_rpn_count, $3, $6, $4, $1); } + | symbol_package_name '.' symbol_procedure_name table_proc_inputs + { $$ = make_node (nod_rel_proc_name, (int) e_rpn_count, $3, NULL, $4, $1); } + ; table_proc_inputs : '(' value_list ')' { $$ = make_list ($2); } @@ -3574,9 +4209,10 @@ join_type : INNER { $$ = make_node (nod_join_inner, (int) 0, NULL); } ; -outer_noise : OUTER - | - ; +outer_noise + : OUTER + | { $$ = NULL; } + ; /* other clauses in the select expression */ @@ -4341,7 +4977,7 @@ u_numeric_constant : NUMERIC u_constant : u_numeric_constant | sql_string - { $$ = MAKE_str_constant ((dsql_str*) $1, lex.att_charset); } + { $$ = MAKE_str_constant ($1, lex.att_charset); } | DATE STRING { if (client_dialect < SQL_DIALECT_V6_TRANSITION) @@ -4356,7 +4992,7 @@ u_constant : u_numeric_constant Arg::Gds(isc_sql_db_dialect_dtype_unsupport) << Arg::Num(db_dialect) << Arg::Str("DATE")); } - $$ = MAKE_constant ((dsql_str*) $2, CONSTANT_DATE); + $$ = MAKE_constant ($2, CONSTANT_DATE); } | TIME STRING { @@ -4372,10 +5008,10 @@ u_constant : u_numeric_constant Arg::Gds(isc_sql_db_dialect_dtype_unsupport) << Arg::Num(db_dialect) << Arg::Str("TIME")); } - $$ = MAKE_constant ((dsql_str*) $2, CONSTANT_TIME); + $$ = MAKE_constant ($2, CONSTANT_TIME); } | TIMESTAMP STRING - { $$ = MAKE_constant ((dsql_str*) $2, CONSTANT_TIMESTAMP); } + { $$ = MAKE_constant ($2, CONSTANT_TIMESTAMP); } ; parameter : '?' @@ -4414,17 +5050,25 @@ sql_string { $$ = $1; } | INTRODUCER STRING // string in specific charset { - dsql_str* str = (dsql_str*) $2; - str->str_charset = (TEXT*) $1; - if (str->type == dsql_str::TYPE_SIMPLE) + dsql_str* str = $2; + $2->str_charset = $1; + if ($2->type == dsql_str::TYPE_SIMPLE || $2->type == dsql_str::TYPE_ALTERNATE) { IntroducerMark mark; mark.pos = lex.last_token - lex.start; mark.length = lex.ptr - lex.last_token; - mark.textLength = str->str_length; + mark.textLength = $2->str_length; - fb_assert(mark.length - mark.textLength == 2); - mark.textPos = mark.pos + 1; + if ($2->type == dsql_str::TYPE_SIMPLE) + { + fb_assert(mark.length - mark.textLength == 2); + mark.textPos = mark.pos + 1; + } + else + { + fb_assert(mark.length - mark.textLength == 5); + mark.textPos = mark.pos + 3; + } introducerMarks.push(mark); } @@ -4480,6 +5124,7 @@ long_integer : NUMBER function : aggregate_function | non_aggregate_function + | window_function ; non_aggregate_function @@ -4541,6 +5186,11 @@ aggregate_function : COUNT '(' '*' ')' { $$ = make_flag_node (nod_agg_list, NOD_AGG_DISTINCT, 2, $4, $5); } ; +window_function + : aggregate_function OVER '(' ')' + { $$ = make_node(nod_window, e_window_count, $1); } + ; + delimiter_opt : ',' value { $$ = $2; } @@ -4618,9 +5268,10 @@ system_function_std_syntax | MINVALUE | MOD | PI - | POSITION | POWER | RAND + | RDB_GET_CONTEXT + | RDB_SET_CONTEXT | REPLACE | REVERSE | RIGHT @@ -4672,6 +5323,8 @@ system_function_special_syntax $$ = make_flag_node(nod_sys_function, NOD_SPECIAL_SYNTAX, e_sysfunc_count, $1, make_node(nod_list, 2, $3, $5)); } + | POSITION '(' value_list_opt ')' + { $$ = make_node(nod_sys_function, e_sysfunc_count, $1, make_list($3)); } ; string_value_function : substring_function @@ -4717,11 +5370,16 @@ trim_specification : BOTH { $$ = MAKE_const_slong (blr_trim_leading); } ; -udf : symbol_UDF_call_name '(' value_list ')' - { $$ = make_node (nod_udf, 2, $1, $3); } - | symbol_UDF_call_name '(' ')' - { $$ = make_node (nod_udf, 1, $1); } - ; +udf + : symbol_UDF_call_name '(' value_list ')' + { $$ = make_node (nod_udf, 3, $1, NULL, $3); } + | symbol_UDF_call_name '(' ')' + { $$ = make_node (nod_udf, 2, $1, NULL); } + | symbol_package_name '.' symbol_UDF_name '(' value_list ')' + { $$ = make_node (nod_udf, 3, $3, $1, $5); } + | symbol_package_name '.' symbol_UDF_name '(' ')' + { $$ = make_node (nod_udf, 2, $3, $1); } + ; cast_specification : CAST '(' value AS data_type_descriptor ')' { $$ = make_node (nod_cast, (int) e_cast_count, $5, $3); } @@ -4835,20 +5493,21 @@ timestamp_part : YEAR { $$ = MAKE_const_slong (blr_extract_yearday); } ; -all_noise : ALL - | - ; +all_noise + : ALL + | { $$ = NULL; } + ; -distinct_noise : DISTINCT - | - ; +distinct_noise + : DISTINCT + | { $$ = NULL; } + ; null_value : KW_NULL { $$ = make_node (nod_null, 0, NULL); } ; - /* Performs special mapping of keywords into symbols */ symbol_UDF_call_name : SYMBOL @@ -4929,6 +5588,10 @@ symbol_view_name : valid_symbol_name symbol_savepoint_name : valid_symbol_name ; +symbol_package_name + : valid_symbol_name + ; + /* symbols */ valid_symbol_name : SYMBOL @@ -5121,6 +5784,14 @@ non_reserved_word : // | WHILE | WORK | WRITE // end of old keywords, that were reserved pre-Firebird.2.5 + | BODY // added in FB 3.0 + | CONTINUE + | DDL + | ENGINE + | NAME + | PACKAGE + | RDB_GET_CONTEXT + | RDB_SET_CONTEXT ; %% @@ -5542,7 +6213,17 @@ static void stack_nodes (dsql_nod* node, static Firebird::MetaName toName(dsql_nod* node) { - return Firebird::MetaName(((dsql_str*) node)->str_data); + dsql_str* str = (dsql_str*) node; + + if (str->str_length > MAX_SQL_IDENTIFIER_LEN) + Firebird::status_exception::raise(Firebird::Arg::Gds(isc_dyn_name_longer)); + + return Firebird::MetaName(str->str_data); +} + +static Firebird::string toString(dsql_str* node) +{ + return Firebird::string(node->str_data); } int Parser::yylex() @@ -5657,7 +6338,7 @@ int Parser::yylexAux() /* make a string value to hold the name, the name * is resolved in pass1_constant */ - yylval = (dsql_nod*) (MAKE_string(string, p - string))->str_data; + yylval.textPtr = MAKE_string(string, p - string)->str_data; return INTRODUCER; } @@ -5727,15 +6408,15 @@ int Parser::yylexAux() gds__free (buffer); yyabandon (-104, isc_token_too_long); } - yylval = (dsql_nod*) MAKE_string(buffer, p - buffer); - dsql_str* delimited_id_str = (dsql_str*) yylval; + yylval.legacyNode = (dsql_nod*) MAKE_string(buffer, p - buffer); + dsql_str* delimited_id_str = (dsql_str*) yylval.legacyNode; delimited_id_str->type = dsql_str::TYPE_DELIMITED; if (buffer != string) gds__free (buffer); return SYMBOL; } } - yylval = (dsql_nod*) MAKE_string(buffer, p - buffer); + yylval.legacyStr = MAKE_string(buffer, p - buffer); if (buffer != string) gds__free (buffer); return STRING; @@ -5859,7 +6540,7 @@ int Parser::yylexAux() dsql_str* string = MAKE_string(temp.c_str(), temp.length()); string->type = dsql_str::TYPE_HEXA; string->str_charset = "BINARY"; - yylval = (dsql_nod*) string; + yylval.legacyStr = string; return STRING; } // if (!hexerror)... @@ -5872,6 +6553,44 @@ int Parser::yylexAux() lex.ptr = lex.last_token + 1; } + if ((c == 'q' || c == 'Q') && lex.ptr + 3 < lex.end && *lex.ptr == '\'') + { + char endChar = *++lex.ptr; + switch (endChar) + { + case '{': + endChar = '}'; + break; + case '(': + endChar = ')'; + break; + case '[': + endChar = ']'; + break; + case '<': + endChar = '>'; + break; + } + + while (++lex.ptr + 1 < lex.end) + { + if (*lex.ptr == endChar && *++lex.ptr == '\'') + { + yylval.legacyStr = MAKE_string(lex.last_token + 3, lex.ptr - lex.last_token - 4); + yylval.legacyStr->type = dsql_str::TYPE_ALTERNATE; + lex.ptr++; + return STRING; + } + } + + // If we got here, there was a parsing error. Set the + // position back to where it was before we messed with + // it. Then fall through to the next thing we might parse. + + c = *lex.last_token; + lex.ptr = lex.last_token + 1; + } + // Hexadecimal numeric constants - 0xBBBBBB // // where the '0' and the 'X' (or 'x') are literal, followed @@ -5942,7 +6661,7 @@ int Parser::yylexAux() p++; } - yylval = (dsql_nod*) MAKE_string(cbuff, strlen(cbuff)); + yylval.legacyNode = (dsql_nod*) MAKE_string(cbuff, strlen(cbuff)); return NUMBER64BIT; } else @@ -5991,7 +6710,7 @@ int Parser::yylexAux() } } - yylval = (dsql_nod*)(long) value; + yylval.legacyNode = (dsql_nod*)(long) value; return NUMBER; } // integer value } // if (!hexerror)... @@ -6091,7 +6810,7 @@ int Parser::yylexAux() if (have_exp_digit) { - yylval = (dsql_nod*) MAKE_string(lex.last_token, lex.ptr - lex.last_token); + yylval.legacyNode = (dsql_nod*) MAKE_string(lex.last_token, lex.ptr - lex.last_token); lex.last_token_bk = lex.last_token; lex.line_start_bk = lex.line_start; lex.lines_bk = lex.lines; @@ -6106,8 +6825,8 @@ int Parser::yylexAux() if (!have_decimal && (number <= MAX_SLONG)) { - yylval = (dsql_nod*) (IPTR) number; - //printf ("parse.y %p %d\n", yylval, number); + yylval.legacyNode = (dsql_nod*) (IPTR) number; + //printf ("parse.y %p %d\n", yylval.legacyNode, number); return NUMBER; } else @@ -6131,7 +6850,7 @@ int Parser::yylexAux() ERRD_post_warning(Arg::Warning(isc_dsql_warning_number_ambiguous1)); } - yylval = (dsql_nod*) MAKE_string(lex.last_token, lex.ptr - lex.last_token); + yylval.legacyNode = (dsql_nod*) MAKE_string(lex.last_token, lex.ptr - lex.last_token); lex.last_token_bk = lex.last_token; lex.line_start_bk = lex.line_start; @@ -6178,13 +6897,13 @@ int Parser::yylexAux() HSHD_lookup (NULL, string, (SSHORT)(p - string), SYM_keyword, parser_version); if (sym && (sym->sym_keyword != COMMENT || lex.prev_keyword == -1)) { - yylval = (dsql_nod*) sym->sym_object; + yylval.legacyNode = (dsql_nod*) sym->sym_object; lex.last_token_bk = lex.last_token; lex.line_start_bk = lex.line_start; lex.lines_bk = lex.lines; return sym->sym_keyword; } - yylval = (dsql_nod*) MAKE_string(string, p - string); + yylval.legacyNode = (dsql_nod*) MAKE_string(string, p - string); lex.last_token_bk = lex.last_token; lex.line_start_bk = lex.line_start; lex.lines_bk = lex.lines; @@ -6253,7 +6972,7 @@ void Parser::yyerror_detailed(const TEXT* /*error_string*/, int yychar, YYSTYPE& // "syntax error" and "yacc stack overflow" are never seen. void Parser::yyerror(const TEXT* error_string) { - YYSTYPE errt_value = 0; + YYSTYPE errt_value; YYPOSN errt_posn = -1; yyerror_detailed(error_string, -1, errt_value, errt_posn); } diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 8da71dd9a5..075c8c9105 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -150,6 +150,7 @@ #include "../jrd/jrd.h" #include "../jrd/constants.h" #include "../jrd/intl_classes.h" +#include "../dsql/DdlNodes.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/hsh_proto.h" @@ -179,28 +180,11 @@ static void DSQL_pretty(const dsql_nod*, int); #endif -class CStrCmp -{ -public: - static int greaterThan(const char* s1, const char* s2) - { - return strcmp(s1, s2) > 0; - } -}; - -typedef Firebird::SortedArray, const char*, - Firebird::DefaultKeyValue, - CStrCmp> - StrArray; - - -static bool aggregate_found(const CompiledStatement*, const dsql_nod*); -static bool aggregate_found2(const CompiledStatement*, const dsql_nod*, USHORT*, USHORT*, bool); +static bool aggregate_found(const CompiledStatement*, const dsql_nod*, bool); +static bool aggregate_found2(const CompiledStatement*, const dsql_nod*, bool, USHORT*, USHORT*, bool); static dsql_nod* ambiguity_check(CompiledStatement*, dsql_nod*, const dsql_str*, const DsqlContextStack&); static void assign_fld_dtype_from_dsc(dsql_fld*, const dsc*); -static void check_unique_fields_names(StrArray& names, const dsql_nod* fields); static dsql_nod* compose(dsql_nod*, dsql_nod*, NOD_TYPE); static dsql_nod* explode_fields(dsql_rel*); static dsql_nod* explode_outputs(CompiledStatement*, const dsql_prc*); @@ -236,7 +220,7 @@ static dsql_nod* pass1_derived_table(CompiledStatement*, dsql_nod*, dsql_str*); static dsql_nod* pass1_expand_select_list(CompiledStatement*, dsql_nod*, dsql_nod*); static void pass1_expand_select_node(CompiledStatement*, dsql_nod*, DsqlNodStack&, bool); static dsql_nod* pass1_field(CompiledStatement*, dsql_nod*, const bool, dsql_nod*); -static bool pass1_found_aggregate(const dsql_nod*, USHORT, USHORT, bool); +static bool pass1_found_aggregate(const dsql_nod*, USHORT, USHORT, bool, bool); static bool pass1_found_field(const dsql_nod*, USHORT, USHORT, bool*); static bool pass1_found_sub_select(const dsql_nod*); static dsql_nod* pass1_group_by_list(CompiledStatement*, dsql_nod*, dsql_nod*); @@ -247,7 +231,6 @@ static dsql_nod* pass1_label(CompiledStatement*, dsql_nod*); static dsql_nod* pass1_lookup_alias(CompiledStatement*, const dsql_str*, dsql_nod*, bool); static dsql_nod* pass1_make_derived_field(CompiledStatement*, thread_db*, dsql_nod*); static dsql_nod* pass1_merge(CompiledStatement*, dsql_nod*); -static dsql_nod* pass1_node_psql(CompiledStatement*, dsql_nod*, bool); static dsql_nod* pass1_not(CompiledStatement*, const dsql_nod*, bool); static void pass1_put_args_on_stack(CompiledStatement*, dsql_nod*, DsqlNodStack&); static dsql_nod* pass1_relation(CompiledStatement*, dsql_nod*); @@ -267,13 +250,12 @@ static dsql_nod* pass1_update(CompiledStatement*, dsql_nod*, bool); static dsql_nod* pass1_update_or_insert(CompiledStatement*, dsql_nod*); static dsql_nod* pass1_variable(CompiledStatement*, dsql_nod*); static dsql_nod* post_map(dsql_nod*, dsql_ctx*); -static dsql_nod* remap_field(CompiledStatement*, dsql_nod*, dsql_ctx*, USHORT); -static dsql_nod* remap_fields(CompiledStatement*, dsql_nod*, dsql_ctx*); +static dsql_nod* remap_field(CompiledStatement*, dsql_nod*, bool, dsql_ctx*, USHORT); +static dsql_nod* remap_fields(CompiledStatement*, dsql_nod*, bool, dsql_ctx*); static void remap_streams_to_parent_context(dsql_nod*, dsql_ctx*); static dsql_fld* resolve_context(CompiledStatement*, const dsql_str*, dsql_ctx*, bool, bool); static dsql_nod* resolve_using_field(CompiledStatement* statement, dsql_str* name, DsqlNodStack& stack, const dsql_nod* flawedNode, const TEXT* side, dsql_ctx*& ctx); -static dsql_nod* resolve_variable_name(const dsql_nod* var_nodes, const dsql_str* var_name); static bool set_parameter_type(CompiledStatement*, dsql_nod*, dsql_nod*, bool); static void set_parameters_name(dsql_nod*, const dsql_nod*); static void set_parameter_name(dsql_nod*, const dsql_nod*, const dsql_rel*); @@ -355,17 +337,24 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat DEV_BLKCHK(relation_name, dsql_type_str); const dsql_nod* cte = NULL; + const dsql_str* package = NULL; + if (relation_node->nod_type == nod_derived_table) { // No processing needed here for derived tables. } - else if ((relation_node->nod_type == nod_rel_proc_name) && relation_node->nod_arg[e_rpn_inputs]) + else if ((relation_node->nod_type == nod_rel_proc_name) && + ((package = (dsql_str*) relation_node->nod_arg[e_rpn_package]) || + relation_node->nod_arg[e_rpn_inputs])) { - procedure = METD_get_procedure(statement, relation_name); + procedure = METD_get_procedure(statement, relation_name, package); + if (!procedure) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_procedure_err) << - Arg::Gds(isc_random) << Arg::Str(relation_name->str_data) << + Arg::Gds(isc_random) << + Arg::Str(QualifiedName(relation_name->str_data, + (package ? package->str_data : NULL)).toString()) << Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) << Arg::Num(relation_node->nod_column)); } @@ -379,7 +368,7 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat relation = METD_get_relation(statement, relation_name); if (!relation && (relation_node->nod_type == nod_rel_proc_name)) { - procedure = METD_get_procedure(statement, relation_name); + procedure = METD_get_procedure(statement, relation_name, NULL); } if (!relation && !procedure) { @@ -393,7 +382,7 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat if (procedure && !procedure->prc_out_count) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-84) << - Arg::Gds(isc_dsql_procedure_use_err) << Arg::Str(relation_name->str_data) << + Arg::Gds(isc_dsql_procedure_use_err) << Arg::Str(procedure->prc_name.toString()) << Arg::Gds(isc_dsql_line_col_error) << Arg::Num(relation_node->nod_line) << Arg::Num(relation_node->nod_column)); } @@ -468,7 +457,7 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat // alias %s conflicts with an alias in the same statement. } else if (conflict->ctx_procedure) { - conflict_name = conflict->ctx_procedure->prc_name.c_str(); + conflict_name = conflict->ctx_procedure->prc_name.identifier.c_str(); error_code = isc_procedure_conflict_error; // alias %s conflicts with a procedure in the same statement. } @@ -495,7 +484,7 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat if (relation_node->nod_arg[e_rpn_inputs]) { context->ctx_proc_inputs = - pass1_node_psql(statement, relation_node->nod_arg[e_rpn_inputs], false); + PASS1_node_psql(statement, relation_node->nod_arg[e_rpn_inputs], false); count = context->ctx_proc_inputs->nod_count; } @@ -504,7 +493,9 @@ dsql_ctx* PASS1_make_context(CompiledStatement* statement, const dsql_nod* relat if (count > procedure->prc_in_count || count < procedure->prc_in_count - procedure->prc_def_count) { - ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(relation_name->str_data)); + ERRD_post(Arg::Gds(isc_prcmismat) << + Arg::Str(QualifiedName(relation_name->str_data, + package ? package->str_data : NULL).toString())); } if (count) @@ -740,12 +731,14 @@ dsql_nod* PASS1_node(CompiledStatement* statement, dsql_nod* input) case nod_relation_name: case nod_rel_proc_name: { + bool couldBeCte = true; dsql_str* rel_name; dsql_str* rel_alias; if (input->nod_type == nod_rel_proc_name) { rel_name = (dsql_str*) input->nod_arg[e_rpn_name]; rel_alias = (dsql_str*) input->nod_arg[e_rpn_alias]; + couldBeCte = !input->nod_arg[e_rpn_package] && !input->nod_arg[e_rpn_inputs]; } else { // nod_relation_name rel_name = (dsql_str*) input->nod_arg[e_rln_name]; @@ -756,7 +749,8 @@ dsql_nod* PASS1_node(CompiledStatement* statement, dsql_nod* input) rel_alias = rel_name; } - dsql_nod* cte = statement->findCTE(rel_name); + //// // ASF: CORE-2699 - Common table expression context could be used with parameters + dsql_nod* cte = couldBeCte ? statement->findCTE(rel_name) : NULL; if (cte) { cte->nod_flags |= NOD_DT_CTE_USED; @@ -914,7 +908,7 @@ dsql_nod* PASS1_node(CompiledStatement* statement, dsql_nod* input) { const DsqlContextStack::iterator base(*statement->req_context); node = MAKE_node(input->nod_type, 2); - node->nod_arg[0] = pass1_node_psql(statement, input->nod_arg[0], false); + node->nod_arg[0] = PASS1_node_psql(statement, input->nod_arg[0], false); dsql_nod* temp = MAKE_node(nod_via, e_via_count); node->nod_arg[1] = temp; dsql_nod* rse = PASS1_rse(statement, sub2, NULL); @@ -1303,7 +1297,6 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) case nod_def_shadow: case nod_del_shadow: case nod_set_statistics: - case nod_comment: case nod_mod_udf: case nod_mod_role: case nod_add_user: @@ -1324,86 +1317,6 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) } return input; - case nod_def_trigger: - case nod_redef_trigger: - case nod_mod_trigger: - case nod_replace_trigger: - case nod_del_trigger: - statement->req_type = REQ_DDL; - statement->req_flags |= (REQ_block | REQ_procedure | REQ_trigger); - return input; - - case nod_del_procedure: - statement->req_type = REQ_DDL; - statement->req_flags |= (REQ_block | REQ_procedure); - return input; - - case nod_def_procedure: - case nod_redef_procedure: - case nod_mod_procedure: - case nod_replace_procedure: - { - statement->req_type = REQ_DDL; - statement->req_flags |= (REQ_block | REQ_procedure); - - const dsql_nod* variables = input->nod_arg[e_prc_dcls]; - if (variables) - { - - // Ensure that variable names do not duplicate parameter names - - const dsql_nod* const* ptr = variables->nod_arg; - for (const dsql_nod* const* const end = ptr + variables->nod_count; ptr < end; ptr++) - { - if ((*ptr)->nod_type == nod_def_field) - { - - const dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field]; - DEV_BLKCHK(field, dsql_type_fld); - - const dsql_nod* parameters = input->nod_arg[e_prc_inputs]; - if (parameters) - { - - const dsql_nod* const* ptr2 = parameters->nod_arg; - for (const dsql_nod* const* const end2 = - ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) - { - const dsql_fld* field2 = (dsql_fld*) (*ptr2)->nod_arg[e_dfl_field]; - DEV_BLKCHK(field2, dsql_type_fld); - - if (field->fld_name == field2->fld_name) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_dsql_var_conflict) << Arg::Str(field->fld_name)); - } - } - } - - parameters = input->nod_arg[e_prc_outputs]; - if (parameters) - { - - const dsql_nod* const* ptr2 = parameters->nod_arg; - for (const dsql_nod* const* const end2 = - ptr2 + parameters->nod_count; ptr2 < end2; ptr2++) - { - const dsql_fld* field2 = (dsql_fld*) (*ptr2)->nod_arg[e_dfl_field]; - DEV_BLKCHK(field2, dsql_type_fld); - - if (field->fld_name == field2->fld_name) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << - Arg::Gds(isc_dsql_var_conflict) << Arg::Str(field->fld_name)); - } - } - } - } - } - } - return input; - } - case nod_assign: node = MAKE_node(input->nod_type, input->nod_count); node->nod_arg[e_asgn_value] = PASS1_node(statement, input->nod_arg[e_asgn_value]); @@ -1437,12 +1350,15 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) case nod_exec_procedure: { const dsql_str* name = (dsql_str*) input->nod_arg[e_exe_procedure]; + const dsql_str* package = (dsql_str*) input->nod_arg[e_exe_package]; DEV_BLKCHK(name, dsql_type_str); - dsql_prc* procedure = METD_get_procedure(statement, name); + dsql_prc* procedure = METD_get_procedure(statement, name, package); if (!procedure) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_procedure_err) << - Arg::Gds(isc_random) << Arg::Str(name->str_data)); + Arg::Gds(isc_random) << + Arg::Str(QualifiedName(name->str_data, + package ? package->str_data : NULL).toString())); } if (!statement->isPsql()) @@ -1453,6 +1369,13 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) node = MAKE_node(input->nod_type, input->nod_count); node->nod_arg[e_exe_procedure] = input->nod_arg[e_exe_procedure]; + node->nod_arg[e_exe_package] = input->nod_arg[e_exe_package]; + + if (!node->nod_arg[e_exe_package] && procedure->prc_name.qualifier.hasData()) + { + node->nod_arg[e_exe_package] = (dsql_nod*) MAKE_string( + procedure->prc_name.qualifier.c_str(), procedure->prc_name.qualifier.length()); + } // handle input parameters @@ -1508,43 +1431,6 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) } break; - case nod_exec_block: - if (input->nod_arg[e_exe_blk_outputs] && input->nod_arg[e_exe_blk_outputs]->nod_count) - { - statement->req_type = REQ_SELECT_BLOCK; - } - else - statement->req_type = REQ_EXEC_BLOCK; - statement->req_flags |= REQ_block; - - node = MAKE_node(input->nod_type, input->nod_count); - node->nod_arg[e_exe_blk_inputs] = - pass1_node_psql(statement, input->nod_arg[e_exe_blk_inputs], false); - node->nod_arg[e_exe_blk_outputs] = input->nod_arg[e_exe_blk_outputs]; - - node->nod_arg[e_exe_blk_dcls] = input->nod_arg[e_exe_blk_dcls]; - node->nod_arg[e_exe_blk_body] = input->nod_arg[e_exe_blk_body]; - - { // scope - const size_t ncount = - node->nod_arg[e_exe_blk_inputs] ? - node->nod_arg[e_exe_blk_inputs]->nod_count : 0 + - node->nod_arg[e_exe_blk_outputs] ? - node->nod_arg[e_exe_blk_outputs]->nod_count : 0 + - node->nod_arg[e_exe_blk_dcls] ? - node->nod_arg[e_exe_blk_dcls]->nod_count : 0; - - if (ncount) - { - StrArray names( *getDefaultMemoryPool(), ncount); - - check_unique_fields_names(names, node->nod_arg[e_exe_blk_inputs]); - check_unique_fields_names(names, node->nod_arg[e_exe_blk_outputs]); - check_unique_fields_names(names, node->nod_arg[e_exe_blk_dcls]); - } - } // end scope - return node; - case nod_for_select: { node = MAKE_node(input->nod_type, input->nod_count); @@ -1839,6 +1725,17 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) input->nod_arg[e_breakleave_label] = pass1_label(statement, input); return input; + case nod_continue: + if (!statement->req_loop_level) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + // Token unknown + Arg::Gds(isc_token_err) << + Arg::Gds(isc_random) << Arg::Str("CONTINUE")); + } + input->nod_arg[e_continue_label] = pass1_label(statement, input); + return input; + case nod_return: if (statement->req_flags & REQ_trigger) // triggers only { @@ -1856,8 +1753,7 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) statement->req_flags |= REQ_selectable; - input->nod_arg[e_rtn_procedure] = - statement->req_ddl_node ? statement->req_ddl_node : statement->req_blk_node; + input->nod_arg[e_rtn_procedure] = (dsql_nod*) statement->blockNode; return input; case nod_select: @@ -2074,7 +1970,7 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input) @param node **/ -static bool aggregate_found( const CompiledStatement* statement, const dsql_nod* node) +static bool aggregate_found(const CompiledStatement* statement, const dsql_nod* node, bool window) { DEV_BLKCHK(statement, dsql_type_req); DEV_BLKCHK(node, dsql_type_nod); @@ -2082,7 +1978,7 @@ static bool aggregate_found( const CompiledStatement* statement, const dsql_nod* USHORT current_level = statement->req_scope_level; USHORT deepest_level = 0; - return aggregate_found2(statement, node, ¤t_level, &deepest_level, false); + return aggregate_found2(statement, node, window, ¤t_level, &deepest_level, false); } @@ -2107,7 +2003,7 @@ static bool aggregate_found( const CompiledStatement* statement, const dsql_nod* @param ignore_sub_selects **/ -static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* node, +static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* node, bool window, USHORT* current_level, USHORT* deepest_level, bool ignore_sub_selects) { @@ -2123,6 +2019,23 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* { // handle the simple case of a straightforward aggregate + case nod_window: + if (!window) + { + if (node->nod_arg[e_window_expr]->nod_count > 0) + { + aggregate |= aggregate_found2(statement, node->nod_arg[e_window_expr]->nod_arg[0], + window, current_level, deepest_level, ignore_sub_selects); + } + } + else + { + aggregate |= aggregate_found2(statement, node->nod_arg[e_window_expr], + false, current_level, deepest_level, ignore_sub_selects); + } + + return aggregate; + case nod_agg_average: case nod_agg_average2: case nod_agg_total2: @@ -2131,6 +2044,9 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* case nod_agg_total: case nod_agg_count: case nod_agg_list: + if (window) + return false; + if (!ignore_sub_selects) { if (node->nod_count) @@ -2140,7 +2056,7 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* // sub-selects and other aggregate-functions for the deepest field // used else we would have a wrong deepest_level value. aggregate_found2(statement, node->nod_arg[e_agg_function_expression], - current_level, &ldeepest_level, true); + window, current_level, &ldeepest_level, true); if (ldeepest_level == 0) { *deepest_level = *current_level; } @@ -2155,7 +2071,7 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* else { // Check also for a nested aggregate that could belong to this context aggregate |= aggregate_found2(statement, node->nod_arg[e_agg_function_expression], - current_level, &ldeepest_level, false); + window, current_level, &ldeepest_level, false); } } else { @@ -2180,7 +2096,7 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* case nod_alias: aggregate = aggregate_found2(statement, node->nod_arg[e_alias_value], - current_level, deepest_level, ignore_sub_selects); + window, current_level, deepest_level, ignore_sub_selects); return aggregate; case nod_derived_field: @@ -2196,13 +2112,16 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* case nod_map: { + if (window) + return false; + const dsql_ctx* lcontext = reinterpret_cast(node->nod_arg[e_map_context]); if (lcontext->ctx_scope_level == statement->req_scope_level) { return true; } const dsql_map* lmap = reinterpret_cast(node->nod_arg[e_map_map]); - aggregate = aggregate_found2(statement, lmap->map_node, current_level, + aggregate = aggregate_found2(statement, lmap->map_node, window, current_level, deepest_level, ignore_sub_selects); return aggregate; } @@ -2212,40 +2131,40 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* case nod_via: if (!ignore_sub_selects) { - aggregate = aggregate_found2(statement, node->nod_arg[e_via_rse], current_level, - deepest_level, ignore_sub_selects); + aggregate = aggregate_found2(statement, node->nod_arg[e_via_rse], window, + current_level, deepest_level, ignore_sub_selects); } return aggregate; case nod_exists: case nod_singular: if (!ignore_sub_selects) { - aggregate = aggregate_found2(statement, node->nod_arg[0], current_level, + aggregate = aggregate_found2(statement, node->nod_arg[0], window, current_level, deepest_level, ignore_sub_selects); } return aggregate; case nod_aggregate: if (!ignore_sub_selects) { - aggregate = aggregate_found2(statement, node->nod_arg[e_agg_rse], current_level, - deepest_level, ignore_sub_selects); + aggregate = aggregate_found2(statement, node->nod_arg[e_agg_rse], window, + current_level, deepest_level, ignore_sub_selects); } return aggregate; case nod_rse: ++*current_level; - aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_streams], current_level, - deepest_level, ignore_sub_selects); - aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_boolean], + aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_streams], window, current_level, deepest_level, ignore_sub_selects); - aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_items], + aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_boolean], window, + current_level, deepest_level, ignore_sub_selects); + aggregate |= aggregate_found2(statement, node->nod_arg[e_rse_items], window, current_level, deepest_level, ignore_sub_selects); --*current_level; return aggregate; case nod_order: - aggregate = aggregate_found2(statement, node->nod_arg[e_order_field], current_level, - deepest_level, ignore_sub_selects); + aggregate = aggregate_found2(statement, node->nod_arg[e_order_field], window, + current_level, deepest_level, ignore_sub_selects); return aggregate; case nod_or: @@ -2305,7 +2224,7 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* const dsql_nod* const* ptr = node->nod_arg; for (const dsql_nod* const* const end = ptr + node->nod_count; ptr < end; ++ptr) { - aggregate |= aggregate_found2(statement, *ptr, current_level, + aggregate |= aggregate_found2(statement, *ptr, window, current_level, deepest_level, ignore_sub_selects); } return aggregate; @@ -2314,10 +2233,16 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* case nod_cast: case nod_gen_id: case nod_gen_id2: - case nod_udf: case nod_sys_function: if (node->nod_count == 2) { - return (aggregate_found2(statement, node->nod_arg[1], current_level, + return (aggregate_found2(statement, node->nod_arg[1], window, current_level, + deepest_level, ignore_sub_selects)); + } + return false; + + case nod_udf: + if (node->nod_count == 3) { + return (aggregate_found2(statement, node->nod_arg[2], window, current_level, deepest_level, ignore_sub_selects)); } return false; @@ -2333,14 +2258,15 @@ static bool aggregate_found2(const CompiledStatement* statement, const dsql_nod* if (lrelation_context->ctx_procedure) { // Check if a aggregate is buried inside the input parameters aggregate |= aggregate_found2(statement, lrelation_context->ctx_proc_inputs, - current_level, deepest_level, ignore_sub_selects); + window, current_level, deepest_level, + ignore_sub_selects); } return aggregate; } case nod_hidden_var: - return (aggregate_found2(statement, node->nod_arg[e_hidden_var_expr], current_level, - deepest_level, ignore_sub_selects)); + return (aggregate_found2(statement, node->nod_arg[e_hidden_var_expr], window, + current_level, deepest_level, ignore_sub_selects)); default: return false; @@ -2405,7 +2331,7 @@ static dsql_nod* ambiguity_check(CompiledStatement* statement, dsql_nod* node, else if (procedure) { // Process procedure when present. strcat(b, "procedure "); - strcat(b, procedure->prc_name.c_str()); + strcat(b, procedure->prc_name.toString().c_str()); } else { // When there's no relation and no procedure it's a derived table. @@ -2478,13 +2404,13 @@ static void assign_fld_dtype_from_dsc( dsql_fld* field, const dsc* nod_desc) /** - check_unique_fields_names + PASS1_check_unique_fields_names check fields (params, variables, cursors etc) names against sorted array if success, add them into array **/ -static void check_unique_fields_names(StrArray& names, const dsql_nod* fields) +void PASS1_check_unique_fields_names(StrArray& names, const dsql_nod* fields) { if (!fields) return; @@ -2625,7 +2551,7 @@ static dsql_nod* explode_outputs( CompiledStatement* statement, const dsql_prc* p_node->nod_arg[e_par_parameter] = (dsql_nod*) parameter; MAKE_desc_from_field(¶meter->par_desc, field); parameter->par_name = parameter->par_alias = field->fld_name.c_str(); - parameter->par_rel_name = procedure->prc_name.c_str(); + parameter->par_rel_name = procedure->prc_name.identifier.c_str(); parameter->par_owner_name = procedure->prc_owner.c_str(); } @@ -2928,7 +2854,7 @@ static bool get_object_and_field(const dsql_nod* node, if (context->ctx_relation) *obj_name = context->ctx_relation->rel_name.c_str(); else if (context->ctx_procedure) - *obj_name = context->ctx_procedure->prc_name.c_str(); + *obj_name = context->ctx_procedure->prc_name.identifier.c_str(); else *obj_name = NULL; @@ -3043,6 +2969,12 @@ static bool invalid_reference(const dsql_ctx* context, const dsql_nod* node, } break; + case nod_window: + invalid |= invalid_reference(context, node->nod_arg[e_window_expr], + list, inside_own_map, true); + + return invalid; + case nod_agg_count: case nod_agg_average: case nod_agg_max: @@ -3067,7 +2999,8 @@ static bool invalid_reference(const dsql_ctx* context, const dsql_nod* node, // aggregate-functions from the same context can't // be part of each other. if (pass1_found_aggregate(node->nod_arg[e_agg_function_expression], - context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL, true)) + context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL, + true, false)) { // Nested aggregate functions are not allowed ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -3080,7 +3013,6 @@ static bool invalid_reference(const dsql_ctx* context, const dsql_nod* node, case nod_gen_id: case nod_gen_id2: case nod_cast: - case nod_udf: case nod_sys_function: // If there are no arguments given to the UDF/SDF then it's always valid if (node->nod_count == 2) { @@ -3089,6 +3021,14 @@ static bool invalid_reference(const dsql_ctx* context, const dsql_nod* node, } break; + case nod_udf: + // If there are no arguments given to the UDF/SDF then it's always valid + if (node->nod_count == 3) { + invalid |= invalid_reference(context, node->nod_arg[2], list, + inside_own_map, inside_higher_map); + } + break; + case nod_via: case nod_exists: case nod_singular: @@ -3414,7 +3354,6 @@ static bool node_match(const dsql_nod* node1, const dsql_nod* node2, case nod_gen_id: case nod_gen_id2: - case nod_udf: case nod_sys_function: case nod_cast: if (node1->nod_arg[0] != node2->nod_arg[0]) { @@ -3426,6 +3365,16 @@ static bool node_match(const dsql_nod* node1, const dsql_nod* node2, } return true; + case nod_udf: + if (node1->nod_arg[0] != node2->nod_arg[0]) { + return false; + } + + if (node1->nod_count == 3) { + return node_match(node1->nod_arg[2], node2->nod_arg[2], ignore_map_cast); + } + return true; + case nod_agg_count: case nod_agg_total: case nod_agg_total2: @@ -3608,7 +3557,7 @@ static dsql_nod* pass1_any( CompiledStatement* statement, dsql_nod* input, NOD_T // create a conjunct to be injected dsql_nod* temp = MAKE_node(input->nod_type, 2); - temp->nod_arg[0] = pass1_node_psql(statement, input->nod_arg[0], false); + temp->nod_arg[0] = PASS1_node_psql(statement, input->nod_arg[0], false); temp->nod_arg[1] = rse->nod_arg[e_rse_items]->nod_arg[0]; rse->nod_arg[e_rse_boolean] = temp; @@ -3689,10 +3638,10 @@ static void pass1_blob( CompiledStatement* statement, dsql_nod* input) dsql_nod* list = input->nod_arg[e_blb_filter]; if (list) { if (list->nod_arg[0]) { - blob->blb_from = pass1_node_psql(statement, list->nod_arg[0], false); + blob->blb_from = PASS1_node_psql(statement, list->nod_arg[0], false); } if (list->nod_arg[1]) { - blob->blb_to = pass1_node_psql(statement, list->nod_arg[1], false); + blob->blb_to = PASS1_node_psql(statement, list->nod_arg[1], false); } } if (!blob->blb_from) { @@ -4306,14 +4255,14 @@ static dsql_nod* pass1_delete( CompiledStatement* statement, dsql_nod* input) rse = MAKE_node(nod_rse, e_rse_count); dsql_nod* temp = MAKE_node(nod_list, 1); rse->nod_arg[e_rse_streams] = temp; - temp->nod_arg[0] = pass1_node_psql(statement, relation, false); + temp->nod_arg[0] = PASS1_node_psql(statement, relation, false); if ( (temp = input->nod_arg[e_del_boolean]) ) { - rse->nod_arg[e_rse_boolean] = pass1_node_psql(statement, temp, false); + rse->nod_arg[e_rse_boolean] = PASS1_node_psql(statement, temp, false); } if ( (temp = input->nod_arg[e_del_plan]) ) { - rse->nod_arg[e_rse_plan] = pass1_node_psql(statement, temp, false); + rse->nod_arg[e_rse_plan] = PASS1_node_psql(statement, temp, false); } if ( (temp = input->nod_arg[e_del_sort]) ) { @@ -4321,8 +4270,8 @@ static dsql_nod* pass1_delete( CompiledStatement* statement, dsql_nod* input) } if ( (temp = input->nod_arg[e_del_rows]) ) { - rse->nod_arg[e_rse_first] = pass1_node_psql(statement, temp->nod_arg[e_rows_length], false); - rse->nod_arg[e_rse_skip] = pass1_node_psql(statement, temp->nod_arg[e_rows_skip], false); + rse->nod_arg[e_rse_first] = PASS1_node_psql(statement, temp->nod_arg[e_rows_length], false); + rse->nod_arg[e_rse_skip] = PASS1_node_psql(statement, temp->nod_arg[e_rows_skip], false); } if (input->nod_arg[e_del_return]) @@ -4708,7 +4657,7 @@ static dsql_nod* process_returning(CompiledStatement* statement, if (!input || input->nod_type == nod_returning) node = PASS1_node(statement, input); else - node = pass1_node_psql(statement, input, false); + node = PASS1_node_psql(statement, input, false); if (input && !statement->isPsql()) statement->req_type = REQ_EXEC_PROCEDURE; @@ -5430,7 +5379,7 @@ static dsql_nod* pass1_field(CompiledStatement* statement, dsql_nod* input, } if (indices) { - indices = pass1_node_psql(statement, indices, false); + indices = PASS1_node_psql(statement, indices, false); } if (context->ctx_flags & CTX_null) @@ -5438,7 +5387,7 @@ static dsql_nod* pass1_field(CompiledStatement* statement, dsql_nod* input, else if (field) node = MAKE_field(context, field, indices); else - node = list ? using_field : pass1_node_psql(statement, using_field, false); + node = list ? using_field : PASS1_node_psql(statement, using_field, false); } } else if (is_derived_table) @@ -5586,7 +5535,7 @@ static dsql_nod* pass1_field(CompiledStatement* statement, dsql_nod* input, **/ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level, - USHORT match_type, bool current_scope_level_equal) + USHORT match_type, bool current_scope_level_equal, bool windowOnly) { DEV_BLKCHK(node, dsql_type_nod); @@ -5600,12 +5549,19 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level case nod_gen_id: case nod_gen_id2: case nod_cast: - case nod_udf: case nod_sys_function: // If arguments are given to the UDF/SDF then there's a node list if (node->nod_count == 2) { found |= pass1_found_aggregate(node->nod_arg[1], check_scope_level, - match_type, current_scope_level_equal); + match_type, current_scope_level_equal, windowOnly); + } + break; + + case nod_udf: + // If arguments are given to the UDF/SDF then there's a node list + if (node->nod_count == 3) { + found |= pass1_found_aggregate(node->nod_arg[2], check_scope_level, + match_type, current_scope_level_equal, windowOnly); } break; @@ -5672,8 +5628,8 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level const dsql_nod* const* ptr = node->nod_arg; for (const dsql_nod* const* const end = ptr + node->nod_count; ptr < end; ++ptr) { - found |= pass1_found_aggregate(*ptr, check_scope_level, - match_type, current_scope_level_equal); + found |= pass1_found_aggregate(*ptr, check_scope_level, match_type, + current_scope_level_equal, windowOnly); } } break; @@ -5681,28 +5637,34 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level case nod_via: // Pass only the rse from the nod_via found |= pass1_found_aggregate(node->nod_arg[e_via_rse], check_scope_level, - match_type, current_scope_level_equal); + match_type, current_scope_level_equal, windowOnly); break; case nod_rse: // Pass rse_boolean (where clause) and rse_items (select items) found |= pass1_found_aggregate(node->nod_arg[e_rse_boolean], check_scope_level, - match_type, false); + match_type, false, windowOnly); found |= pass1_found_aggregate(node->nod_arg[e_rse_items], check_scope_level, - match_type, false); + match_type, false, windowOnly); break; case nod_alias: found |= pass1_found_aggregate(node->nod_arg[e_alias_value], check_scope_level, - match_type, current_scope_level_equal); + match_type, current_scope_level_equal, windowOnly); break; case nod_aggregate: // Pass only rse_group (group by clause) found |= pass1_found_aggregate(node->nod_arg[e_agg_group], check_scope_level, - match_type, current_scope_level_equal); + match_type, current_scope_level_equal, windowOnly); break; + case nod_window: + found |= pass1_found_aggregate(node->nod_arg[e_window_expr], check_scope_level, + match_type, current_scope_level_equal, false); + + return found; + case nod_agg_average: case nod_agg_count: case nod_agg_max: @@ -5711,6 +5673,7 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level case nod_agg_average2: case nod_agg_total2: case nod_agg_list: + if (!windowOnly) { bool field = false; if (node->nod_count) { @@ -5747,7 +5710,7 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level { const dsql_map* map = reinterpret_cast(node->nod_arg[e_map_map]); found |= pass1_found_aggregate(map->map_node, check_scope_level, match_type, - current_scope_level_equal); + current_scope_level_equal, windowOnly); } break; @@ -5770,7 +5733,7 @@ static bool pass1_found_aggregate(const dsql_nod* node, USHORT check_scope_level case nod_hidden_var: found |= pass1_found_aggregate(node->nod_arg[e_hidden_var_expr], check_scope_level, - match_type, current_scope_level_equal); + match_type, current_scope_level_equal, windowOnly); break; default: @@ -5839,7 +5802,6 @@ static bool pass1_found_field(const dsql_nod* node, USHORT check_scope_level, case nod_gen_id: case nod_gen_id2: case nod_cast: - case nod_udf: case nod_sys_function: // If arguments are given to the UDF/SDF then there's a node list if (node->nod_count == 2) { @@ -5847,6 +5809,12 @@ static bool pass1_found_field(const dsql_nod* node, USHORT check_scope_level, } break; + case nod_udf: + // If arguments are given to the UDF/SDF then there's a node list + if (node->nod_count == 3) { + found |= pass1_found_field(node->nod_arg[2], check_scope_level, match_type, field); + } + break; case nod_exists: case nod_singular: case nod_coalesce: @@ -5963,6 +5931,11 @@ static bool pass1_found_field(const dsql_nod* node, USHORT check_scope_level, match_type, field); break; + case nod_window: + found |= pass1_found_field(node->nod_arg[e_window_expr], + check_scope_level, match_type, field); + break; + case nod_agg_average: case nod_agg_count: case nod_agg_max: @@ -6034,7 +6007,6 @@ static bool pass1_found_sub_select(const dsql_nod* node) case nod_gen_id: case nod_gen_id2: case nod_cast: - case nod_udf: case nod_sys_function: // If arguments are given to the UDF/SDF then there's a node list if (node->nod_count == 2) { @@ -6044,6 +6016,15 @@ static bool pass1_found_sub_select(const dsql_nod* node) } break; + case nod_udf: + // If arguments are given to the UDF/SDF then there's a node list + if (node->nod_count == 3) { + if (pass1_found_sub_select(node->nod_arg[2])) { + return true; + } + } + break; + case nod_exists: case nod_singular: case nod_coalesce: @@ -6214,11 +6195,11 @@ static dsql_nod* pass1_group_by_list(CompiledStatement* statement, dsql_nod* inp ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("GROUP BY")); } - frnode = pass1_node_psql(statement, selectList->nod_arg[position - 1], false); + frnode = PASS1_node_psql(statement, selectList->nod_arg[position - 1], false); } else { - frnode = pass1_node_psql(statement, *ptr, false); + frnode = PASS1_node_psql(statement, *ptr, false); } stack.push(frnode); @@ -6297,7 +6278,7 @@ static dsql_nod* pass1_insert( CompiledStatement* statement, dsql_nod* input, bo values = rse->nod_arg[e_rse_items]; } else - values = pass1_node_psql(statement, input->nod_arg[e_ins_values], false); + values = PASS1_node_psql(statement, input->nod_arg[e_ins_values], false); // Process relation @@ -6313,7 +6294,7 @@ static dsql_nod* pass1_insert( CompiledStatement* statement, dsql_nod* input, bo if (fields) { const dsql_nod* old_fields = fields; // for error reporting. - fields = pass1_node_psql(statement, fields, false); + fields = PASS1_node_psql(statement, fields, false); // We do not allow cases like INSERT INTO T(f1, f2, f1)... field_appears_once(fields, old_fields, true, "INSERT"); @@ -6358,7 +6339,7 @@ static dsql_nod* pass1_insert( CompiledStatement* statement, dsql_nod* input, bo // end IBO hack } else - fields = pass1_node_psql(statement, explode_fields(relation), false); + fields = PASS1_node_psql(statement, explode_fields(relation), false); // Match field fields and values @@ -6708,6 +6689,9 @@ static dsql_nod* pass1_label(CompiledStatement* statement, dsql_nod* input) case nod_breakleave: label = input->nod_arg[e_breakleave_label]; break; + case nod_continue: + label = input->nod_arg[e_continue_label]; + break; case nod_for_select: label = input->nod_arg[e_flp_label]; break; @@ -6750,10 +6734,10 @@ static dsql_nod* pass1_label(CompiledStatement* statement, dsql_nod* input) } USHORT number = 0; - if (input->nod_type == nod_breakleave) + if (input->nod_type == nod_breakleave || input->nod_type == nod_continue) { if (position > 0) { - // break the specified loop + // break/continue the specified loop number = position; } else if (label) { @@ -6764,7 +6748,7 @@ static dsql_nod* pass1_label(CompiledStatement* statement, dsql_nod* input) Arg::Str("is not found")); } else { - // break the current loop + // break/continue the current loop number = statement->req_loop_level; } } @@ -6853,7 +6837,7 @@ static dsql_nod* pass1_lookup_alias(CompiledStatement* statement, const dsql_str if (matchingNode) { if (process) - matchingNode = pass1_node_psql(statement, matchingNode, false); + matchingNode = PASS1_node_psql(statement, matchingNode, false); if (returnNode) { @@ -6979,7 +6963,7 @@ static dsql_nod* pass1_make_derived_field(CompiledStatement* statement, thread_d return derived_field; } - case nod_map : + case nod_map: { // Aggregate's have map on top. dsql_map* map = (dsql_map*) select_item->nod_arg[e_map_map]; @@ -7099,7 +7083,7 @@ static dsql_nod* pass1_merge(CompiledStatement* statement, dsql_nod* input) statement->req_context->push(context); // process old context values for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr) - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); // and pop the contexts statement->req_context->pop(); @@ -7111,7 +7095,7 @@ static dsql_nod* pass1_merge(CompiledStatement* statement, dsql_nod* input) // process new context values for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr) - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); statement->req_context->pop(); @@ -7195,7 +7179,7 @@ static dsql_nod* pass1_merge(CompiledStatement* statement, dsql_nod* input) // Changes statement->isPsql() value, calls PASS1_node and restore statement->isPsql(). -static dsql_nod* pass1_node_psql(CompiledStatement* statement, dsql_nod* input, bool psql) +dsql_nod* PASS1_node_psql(CompiledStatement* statement, dsql_nod* input, bool psql) { PsqlChanger changer(statement, psql); return PASS1_node(statement, input); @@ -7703,7 +7687,7 @@ static dsql_nod* pass1_returning(CompiledStatement* statement, const dsql_nod* i DEV_BLKCHK(statement, dsql_type_req); DEV_BLKCHK(input, dsql_type_nod); - dsql_nod* const source = pass1_node_psql(statement, input->nod_arg[e_ret_source], false); + dsql_nod* const source = PASS1_node_psql(statement, input->nod_arg[e_ret_source], false); statement->req_flags |= REQ_returning_into; dsql_nod* const target = PASS1_node(statement, input->nod_arg[e_ret_target]); @@ -7909,7 +7893,7 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, rse->nod_arg[e_rse_lock] = update_lock; dsql_nod* list = rse->nod_arg[e_rse_streams] = - pass1_node_psql(statement, input->nod_arg[e_qry_from], false); + PASS1_node_psql(statement, input->nod_arg[e_qry_from], false); { // scope block const dsql_rel* relation; @@ -7941,12 +7925,12 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, const int skip_index = rows ? e_rows_skip : e_limit_skip; if (node->nod_arg[length_index]) { - dsql_nod* sub = pass1_node_psql(statement, node->nod_arg[length_index], false); + dsql_nod* sub = PASS1_node_psql(statement, node->nod_arg[length_index], false); rse->nod_arg[e_rse_first] = sub; set_parameter_type(statement, sub, node, false); } if (node->nod_arg[skip_index]) { - dsql_nod* sub = pass1_node_psql(statement, node->nod_arg[skip_index], false); + dsql_nod* sub = PASS1_node_psql(statement, node->nod_arg[skip_index], false); rse->nod_arg[e_rse_skip] = sub; set_parameter_type(statement, sub, node, false); } @@ -7957,13 +7941,13 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, if ( (node = input->nod_arg[e_qry_where]) ) { ++statement->req_in_where_clause; - rse->nod_arg[e_rse_boolean] = pass1_node_psql(statement, node, false); + rse->nod_arg[e_rse_boolean] = PASS1_node_psql(statement, node, false); --statement->req_in_where_clause; // AB: An aggregate pointing to it's own parent_context isn't // allowed, HAVING should be used instead if (pass1_found_aggregate(rse->nod_arg[e_rse_boolean], - statement->req_scope_level, FIELD_MATCH_TYPE_EQUAL, true)) + statement->req_scope_level, FIELD_MATCH_TYPE_EQUAL, true, false)) { // Cannot use an aggregate in a WHERE clause, use HAVING instead ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -8012,12 +7996,10 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, if (input->nod_arg[e_qry_group] || input->nod_arg[e_qry_having] || - (rse->nod_arg[e_rse_items] && aggregate_found(statement, rse->nod_arg[e_rse_items])) || - (rse->nod_arg[e_rse_sort] && aggregate_found(statement, rse->nod_arg[e_rse_sort]))) + (rse->nod_arg[e_rse_items] && aggregate_found(statement, rse->nod_arg[e_rse_items], false)) || + (rse->nod_arg[e_rse_sort] && aggregate_found(statement, rse->nod_arg[e_rse_sort], false))) { - // dimitr: don't allow WITH LOCK for aggregates - if (update_lock) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << // Token unknown @@ -8070,8 +8052,8 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, bool field; if (pass1_found_field(aggregate->nod_arg[e_agg_group], statement->req_scope_level, FIELD_MATCH_TYPE_LOWER, &field) || - pass1_found_aggregate(aggregate->nod_arg[e_agg_group], - statement->req_scope_level, FIELD_MATCH_TYPE_LOWER_EQUAL, true)) + pass1_found_aggregate(aggregate->nod_arg[e_agg_group], + statement->req_scope_level, FIELD_MATCH_TYPE_LOWER_EQUAL, true, false)) { // Cannot use an aggregate in a GROUP BY clause ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -8080,7 +8062,7 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, } // Parse a user-specified access PLAN - rse->nod_arg[e_rse_plan] = pass1_node_psql(statement, input->nod_arg[e_qry_plan], false); + rse->nod_arg[e_rse_plan] = PASS1_node_psql(statement, input->nod_arg[e_qry_plan], false); // AB: Pass select-items for distinct operation again, because for // sub-selects a new context number should be generated @@ -8109,106 +8091,220 @@ static dsql_nod* pass1_rse_impl( CompiledStatement* statement, dsql_nod* input, } } - // Unless there was a parent, we're done - if (!parent_context) + if (parent_context) { - rse->nod_flags = flags; - return rse; - } + // Reset context of select items to point to the parent stream - // Reset context of select items to point to the parent stream + parent_rse->nod_arg[e_rse_items] = + remap_fields(statement, rse->nod_arg[e_rse_items], false, parent_context); + rse->nod_arg[e_rse_items] = NULL; - parent_rse->nod_arg[e_rse_items] = - remap_fields(statement, rse->nod_arg[e_rse_items], parent_context); - rse->nod_arg[e_rse_items] = NULL; - - // AB: Check for invalid contructions inside selected-items list - list = parent_rse->nod_arg[e_rse_items]; - { // scope block - const dsql_nod* const* ptr = list->nod_arg; - for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) - { - if (invalid_reference(parent_context, *ptr, aggregate->nod_arg[e_agg_group], false, false)) + // AB: Check for invalid contructions inside selected-items list + list = parent_rse->nod_arg[e_rse_items]; + { // scope block + const dsql_nod* const* ptr = list->nod_arg; + for (const dsql_nod* const* const end = ptr + list->nod_count; + ptr < end; ptr++) { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - // Invalid expression in the select list - // (not contained in either an aggregate or the GROUP BY clause) - Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list")); + if (invalid_reference(parent_context, *ptr, + aggregate->nod_arg[e_agg_group], false, false)) + { + // Invalid expression in the select list + // (not contained in either an aggregate or the GROUP BY clause) + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list")); + } + } + } // end scope block + + // Reset context of order items to point to the parent stream + + if (order) { + parent_rse->nod_arg[e_rse_sort] = + remap_fields(statement, rse->nod_arg[e_rse_sort], false, parent_context); + rse->nod_arg[e_rse_sort] = NULL; + + // AB: Check for invalid contructions inside the ORDER BY clause + list = target_rse->nod_arg[e_rse_sort]; + const dsql_nod* const* ptr = list->nod_arg; + for (const dsql_nod* const* const end = ptr + list->nod_count; + ptr < end; ptr++) + { + if (invalid_reference(parent_context, *ptr, + aggregate->nod_arg[e_agg_group], false, false)) + { + // Invalid expression in the ORDER BY clause + // (not contained in either an aggregate or the GROUP BY clause) + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY clause")); + } } } - } // end scope block - // Reset context of order items to point to the parent stream - - if (order) - { - parent_rse->nod_arg[e_rse_sort] = - remap_fields(statement, rse->nod_arg[e_rse_sort], parent_context); - rse->nod_arg[e_rse_sort] = NULL; - - // AB: Check for invalid contructions inside the ORDER BY clause - list = target_rse->nod_arg[e_rse_sort]; - const dsql_nod* const* ptr = list->nod_arg; - for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) + // And, of course, reduction clauses must also apply to the parent + if (input->nod_arg[e_qry_distinct]) { - if (invalid_reference(parent_context, *ptr, aggregate->nod_arg[e_agg_group], false, false)) - { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - // Invalid expression in the ORDER BY clause - // (not contained in either an aggregate or the GROUP BY clause) - Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY clause")); - } + parent_rse->nod_arg[e_rse_reduced] = + remap_fields(statement, parent_rse->nod_arg[e_rse_reduced], false, parent_context); } - } - // And, of course, reduction clauses must also apply to the parent + // Process HAVING clause, if any - if (input->nod_arg[e_qry_distinct]) - { - parent_rse->nod_arg[e_rse_reduced] = - remap_fields(statement, parent_rse->nod_arg[e_rse_reduced], parent_context); - } - - // Process HAVING clause, if any - - if ( (node = input->nod_arg[e_qry_having]) ) - { - ++statement->req_in_having_clause; - parent_rse->nod_arg[e_rse_boolean] = pass1_node_psql(statement, node, false); - --statement->req_in_having_clause; - - parent_rse->nod_arg[e_rse_boolean] = - remap_fields(statement, parent_rse->nod_arg[e_rse_boolean], parent_context); - - // AB: Check for invalid contructions inside the HAVING clause - list = parent_rse->nod_arg[e_rse_boolean]; - dsql_nod** ptr = list->nod_arg; - for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) + if ((node = input->nod_arg[e_qry_having])) { - if (invalid_reference(parent_context, *ptr, aggregate->nod_arg[e_agg_group], false, false)) + ++statement->req_in_having_clause; + parent_rse->nod_arg[e_rse_boolean] = PASS1_node_psql(statement, node, false); + --statement->req_in_having_clause; + + parent_rse->nod_arg[e_rse_boolean] = + remap_fields(statement, parent_rse->nod_arg[e_rse_boolean], false, parent_context); + + // AB: Check for invalid contructions inside the HAVING clause + list = parent_rse->nod_arg[e_rse_boolean]; + dsql_nod** ptr = list->nod_arg; + for (const dsql_nod* const* const end = ptr + list->nod_count; + ptr < end; ptr++) { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - // Invalid expression in the HAVING clause - // (neither an aggregate nor contained in the GROUP BY clause) - Arg::Gds(isc_dsql_agg_having_err) << Arg::Str("HAVING clause")); + if (invalid_reference(parent_context, *ptr, + aggregate->nod_arg[e_agg_group], false, false)) + { + // Invalid expression in the HAVING clause + // (neither an aggregate nor contained in the GROUP BY clause) + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_having_err) << Arg::Str("HAVING clause")); + } + } + + if (aggregate_found(statement, list, true)) + { + // Cannot use an aggregate in a WHERE clause, use HAVING instead + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_where_err)); } - } #ifdef CHECK_HAVING - if (aggregate) - { - if (invalid_reference(parent_rse->nod_arg[e_rse_boolean], aggregate->nod_arg[e_agg_group])) + if (aggregate) { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - // invalid field reference - Arg::Gds(isc_field_ref_err)); + if (invalid_reference(parent_rse->nod_arg[e_rse_boolean], + aggregate->nod_arg[e_agg_group])) + { + // invalid field reference + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_field_ref_err)); + } } - } #endif + } + rse = parent_rse; } - parent_rse->nod_flags = flags; - return parent_rse; + bool sortWindow = rse->nod_arg[e_rse_sort] && + aggregate_found(statement, rse->nod_arg[e_rse_sort], true); + + // WINDOW functions + if ((rse->nod_arg[e_rse_items] && aggregate_found(statement, rse->nod_arg[e_rse_items], true)) || + sortWindow) + { + // Don't allow WITH LOCK + if (update_lock) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + // Token unknown + Arg::Gds(isc_token_err) << + Arg::Gds(isc_random) << Arg::Str("WITH LOCK")); + } + + parent_context = FB_NEW(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool()); + parent_context->ctx_context = statement->req_context_number++; + parent_context->ctx_scope_level = statement->req_scope_level; + dsql_nod* window = MAKE_node(nod_aggregate, e_agg_count); + window->nod_flags = NOD_AGG_WINDOW; + window->nod_arg[e_agg_context] = (dsql_nod*) parent_context; + window->nod_arg[e_agg_rse] = rse; + parent_rse = target_rse = MAKE_node(nod_rse, e_rse_count); + parent_rse->nod_arg[e_rse_streams] = list = MAKE_node(nod_list, 1); + list->nod_arg[0] = window; + + if (rse->nod_arg[e_rse_first]) + { + parent_rse->nod_arg[e_rse_first] = rse->nod_arg[e_rse_first]; + rse->nod_arg[e_rse_first] = NULL; + } + + if (rse->nod_arg[e_rse_skip]) + { + parent_rse->nod_arg[e_rse_skip] = rse->nod_arg[e_rse_skip]; + rse->nod_arg[e_rse_skip] = NULL; + } + + statement->req_context->push(parent_context); + // replace original contexts with parent context + remap_streams_to_parent_context(rse->nod_arg[e_rse_streams], parent_context); + + if (aggregate) + { + // Check for invalid contructions inside selected-items list + list = rse->nod_arg[e_rse_items]; + const dsql_nod* const* ptr = list->nod_arg; + for (const dsql_nod* const* const end = ptr + list->nod_count; + ptr < end; ptr++) + { + if (invalid_reference(parent_context, *ptr, + aggregate->nod_arg[e_agg_group], false, false)) + { + // Invalid expression in the select list + // (not contained in either an aggregate or the GROUP BY clause) + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("select list")); + } + } + } + + parent_rse->nod_arg[e_rse_items] = + remap_fields(statement, rse->nod_arg[e_rse_items], true, parent_context); + + rse->nod_arg[e_rse_items] = NULL; + + if (order) + { + if (aggregate) + { + // Check for invalid contructions inside the order-by list + list = rse->nod_arg[e_rse_sort]; + const dsql_nod* const* ptr = list->nod_arg; + for (const dsql_nod* const* const end = ptr + list->nod_count; + ptr < end; ptr++) + { + if (invalid_reference(parent_context, *ptr, + aggregate->nod_arg[e_agg_group], false, false)) + { + // Invalid expression in the ORDER BY list + // (not contained in either an aggregate or the GROUP BY clause) + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_column_err) << Arg::Str("ORDER BY list")); + } + } + } + + parent_rse->nod_arg[e_rse_sort] = + remap_fields(statement, rse->nod_arg[e_rse_sort], true, parent_context); + + rse->nod_arg[e_rse_sort] = NULL; + } + + // And, of course, reduction clauses must also apply to the parent + if (rse->nod_arg[e_rse_reduced]) + { + parent_rse->nod_arg[e_rse_reduced] = + remap_fields(statement, rse->nod_arg[e_rse_reduced], true, parent_context); + rse->nod_arg[e_rse_reduced] = NULL; + } + + rse = parent_rse; + } + + rse->nod_flags = flags; + return rse; } @@ -8295,7 +8391,7 @@ static dsql_nod* pass1_sel_list(CompiledStatement* statement, dsql_nod* input, b statement->req_hidden_vars_number = 0; DEV_BLKCHK(*ptr, dsql_type_nod); - stack.push(pass1_node_psql(statement, *ptr, false)); + stack.push(PASS1_node_psql(statement, *ptr, false)); if (viewFields) statement->req_hidden_vars_number = 0; @@ -8503,10 +8599,10 @@ static dsql_nod* pass1_sort( CompiledStatement* statement, dsql_nod* input, dsql Arg::Gds(isc_dsql_column_pos_err) << Arg::Str("ORDER BY")); } // substitute ordinal with appropriate field - node1 = pass1_node_psql(statement, selectList->nod_arg[position - 1], false); + node1 = PASS1_node_psql(statement, selectList->nod_arg[position - 1], false); } else { - node1 = pass1_node_psql(statement, node1, false); + node1 = PASS1_node_psql(statement, node1, false); } if (collate) { @@ -8545,13 +8641,20 @@ static dsql_nod* pass1_sys_function(CompiledStatement* statement, dsql_nod* inpu if (!(input->nod_flags & NOD_SPECIAL_SYNTAX)) { const dsql_str* name = (dsql_str*) input->nod_arg[e_sysfunc_name]; - dsql_udf* userFunc = METD_get_function(statement, name); + dsql_udf* userFunc = METD_get_function(statement, name, NULL); if (userFunc) { - node = MAKE_node(nod_udf, 2); + node = MAKE_node(nod_udf, 3); node->nod_arg[0] = (dsql_nod*) name; - node->nod_arg[1] = input->nod_arg[e_sysfunc_args]; + + const MetaName& package = userFunc->udf_name.qualifier; + if (package.isEmpty()) + node->nod_arg[1] = NULL; + else + node->nod_arg[1] = (dsql_nod*) MAKE_string(package.c_str(), package.length()); + + node->nod_arg[2] = input->nod_arg[e_sysfunc_args]; return pass1_udf(statement, node); } @@ -8613,21 +8716,26 @@ static dsql_nod* pass1_udf( CompiledStatement* statement, dsql_nod* input) const dsql_str* name = (dsql_str*) input->nod_arg[0]; DEV_BLKCHK(name, dsql_type_str); - dsql_udf* userFunc = METD_get_function(statement, name); + const dsql_str* package = (dsql_str*) input->nod_arg[1]; + DEV_BLKCHK(package, dsql_type_str); + + dsql_udf* userFunc = METD_get_function(statement, name, package); if (!userFunc) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) << Arg::Gds(isc_dsql_function_err) << - Arg::Gds(isc_random) << Arg::Str(name->str_data)); + Arg::Gds(isc_random) << + Arg::Str(QualifiedName(name->str_data, + package ? package->str_data : NULL).toString())); } dsql_nod* node = MAKE_node(nod_udf, input->nod_count); node->nod_arg[0] = (dsql_nod*) userFunc; - if (input->nod_count == 2) { + if (input->nod_count == 3) { DsqlNodStack stack; USHORT arg_pos = 0; - pass1_udf_args(statement, input->nod_arg[1], userFunc, arg_pos, stack); - node->nod_arg[1] = MAKE_list(stack); + pass1_udf_args(statement, input->nod_arg[2], userFunc, arg_pos, stack); + node->nod_arg[2] = MAKE_list(stack); } return node; @@ -8878,12 +8986,12 @@ static dsql_nod* pass1_union( CompiledStatement* statement, dsql_nod* input, if (rows) { if (rows->nod_arg[e_rows_length]) { - dsql_nod* sub = pass1_node_psql(statement, rows->nod_arg[e_rows_length], false); + dsql_nod* sub = PASS1_node_psql(statement, rows->nod_arg[e_rows_length], false); union_rse->nod_arg[e_rse_first] = sub; set_parameter_type(statement, sub, rows, false); } if (rows->nod_arg[e_rows_skip]) { - dsql_nod* sub = pass1_node_psql(statement, rows->nod_arg[e_rows_skip], false); + dsql_nod* sub = PASS1_node_psql(statement, rows->nod_arg[e_rows_skip], false); union_rse->nod_arg[e_rse_skip] = sub; set_parameter_type(statement, sub, rows, false); } @@ -9143,28 +9251,28 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo statement->req_scope_level++; for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } statement->req_scope_level--; statement->req_context->pop(); } // Process relation - anode->nod_arg[e_mdc_update] = pass1_node_psql(statement, relation, false); + anode->nod_arg[e_mdc_update] = PASS1_node_psql(statement, relation, false); if (!isUpdateSqlCompliant) { // Process old context values for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } } // Process new context values for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } anode->nod_arg[e_mdc_return] = process_returning(statement, input->nod_arg[e_upd_return]); @@ -9189,7 +9297,7 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo statement->req_type = cursor ? REQ_UPDATE_CURSOR : REQ_UPDATE; dsql_nod* node = MAKE_node(nod_modify, e_mod_count); - node->nod_arg[e_mod_update] = pass1_node_psql(statement, relation, false); + node->nod_arg[e_mod_update] = PASS1_node_psql(statement, relation, false); dsql_ctx* mod_context = get_context(node->nod_arg[e_mod_update]); if (!isUpdateSqlCompliant) @@ -9197,14 +9305,14 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo // Process old context values for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } } // Process new context values for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } statement->req_context->pop(); @@ -9224,15 +9332,15 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo dsql_nod* temp = MAKE_node(nod_list, 1); rse->nod_arg[e_rse_streams] = temp; - temp->nod_arg[0] = pass1_node_psql(statement, relation, false); + temp->nod_arg[0] = PASS1_node_psql(statement, relation, false); dsql_ctx* old_context = get_context(temp->nod_arg[0]); if ( (temp = input->nod_arg[e_upd_boolean]) ) { - rse->nod_arg[e_rse_boolean] = pass1_node_psql(statement, temp, false); + rse->nod_arg[e_rse_boolean] = PASS1_node_psql(statement, temp, false); } if ( (temp = input->nod_arg[e_upd_plan]) ) { - rse->nod_arg[e_rse_plan] = pass1_node_psql(statement, temp, false); + rse->nod_arg[e_rse_plan] = PASS1_node_psql(statement, temp, false); } if ( (temp = input->nod_arg[e_upd_sort]) ) { @@ -9240,8 +9348,8 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo } if ( (temp = input->nod_arg[e_upd_rows]) ) { - rse->nod_arg[e_rse_first] = pass1_node_psql(statement, temp->nod_arg[e_rows_length], false); - rse->nod_arg[e_rse_skip] = pass1_node_psql(statement, temp->nod_arg[e_rows_skip], false); + rse->nod_arg[e_rse_first] = PASS1_node_psql(statement, temp->nod_arg[e_rows_length], false); + rse->nod_arg[e_rse_skip] = PASS1_node_psql(statement, temp->nod_arg[e_rows_skip], false); } if (input->nod_arg[e_upd_return]) @@ -9296,7 +9404,7 @@ static dsql_nod* pass1_update(CompiledStatement* statement, dsql_nod* input, boo // Process old context values for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr) { - *ptr = pass1_node_psql(statement, *ptr, false); + *ptr = PASS1_node_psql(statement, *ptr, false); } } @@ -9403,7 +9511,7 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod* statement->req_context->push(context); statement->req_scope_level++; - const dsql_nod* matching_fields = pass1_node_psql(statement, matching, false); + const dsql_nod* matching_fields = PASS1_node_psql(statement, matching, false); statement->req_scope_level--; statement->req_context->pop(); @@ -9598,10 +9706,10 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod* /** - resolve_variable_name + PASS1_resolve_variable_name **/ -static dsql_nod* resolve_variable_name(const dsql_nod* var_nodes, const dsql_str* var_name) +dsql_nod* PASS1_resolve_variable_name(const dsql_nod* var_nodes, const dsql_str* var_name) { dsql_nod* const* ptr = var_nodes->nod_arg; dsql_nod* const* const end = ptr + var_nodes->nod_count; @@ -9614,9 +9722,7 @@ static dsql_nod* resolve_variable_name(const dsql_nod* var_nodes, const dsql_str const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable]; DEV_BLKCHK(variable, dsql_type_var); if (!strcmp(var_name->str_data, variable->var_name)) - { return var_node; - } } } @@ -9662,94 +9768,9 @@ static dsql_nod* pass1_variable( CompiledStatement* statement, dsql_nod* input) DEV_BLKCHK(var_name, dsql_type_str); - dsql_nod* var_nodes; - if (statement->req_flags & REQ_procedure) // procedures and triggers - { - dsql_nod* procedure_node = statement->req_ddl_node; - fb_assert(procedure_node); - if (!(statement->req_flags & REQ_trigger)) // no, procedures only - { - // try to resolve variable name against input and output parameters - var_nodes = procedure_node->nod_arg[e_prc_inputs]; - if (var_nodes) - { - //position = 0; - dsql_nod** ptr = var_nodes->nod_arg; - for (const dsql_nod* const* const end = ptr + var_nodes->nod_count; ptr < end; ptr++) - { - dsql_nod* var_node = *ptr; - const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable]; - DEV_BLKCHK(variable, dsql_type_var); - if (!strcmp(var_name->str_data, variable->var_name)) - { - return var_node; - } - } - } - var_nodes = procedure_node->nod_arg[e_prc_outputs]; - if (var_nodes) - { - //position = 0; - dsql_nod** ptr = var_nodes->nod_arg; - for (const dsql_nod* const* const end = ptr + var_nodes->nod_count; ptr < end; ptr++) - { - dsql_nod* var_node = *ptr; - const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable]; - DEV_BLKCHK(variable, dsql_type_var); - if (!strcmp(var_name->str_data, variable->var_name)) - { - return var_node; - } - } - } - var_nodes = procedure_node->nod_arg[e_prc_dcls]; - } - else - var_nodes = procedure_node->nod_arg[e_trg_actions]->nod_arg[e_trg_act_dcls]; - - if (var_nodes) - { - // try to resolve variable name against local variables - //position = 0; - dsql_nod** ptr = var_nodes->nod_arg; - for (const dsql_nod* const* const end = ptr + var_nodes->nod_count; ptr < end; ptr++) - { - dsql_nod* var_node = *ptr; - if (var_node->nod_type == nod_variable) - { - const dsql_var* variable = (dsql_var*) var_node->nod_arg[e_var_variable]; - DEV_BLKCHK(variable, dsql_type_var); - if (!strcmp(var_name->str_data, variable->var_name)) - { - return var_node; - } - } - } - } - } - - if (statement->req_blk_node) - { - dsql_nod* var_node; - - if (var_nodes = statement->req_blk_node->nod_arg[e_exe_blk_dcls]) - { - if (var_node = resolve_variable_name(var_nodes, var_name)) - return var_node; - } - - if (var_nodes = statement->req_blk_node->nod_arg[e_exe_blk_inputs]) - { - if (var_node = resolve_variable_name(var_nodes, var_name)) - return var_node; - } - - if (var_nodes = statement->req_blk_node->nod_arg[e_exe_blk_outputs]) - { - if (var_node = resolve_variable_name(var_nodes, var_name)) - return var_node; - } - } + dsql_nod* var_node = statement->blockNode->resolveVariable(var_name); + if (var_node) + return var_node; // field unresolved // CVC: That's all [the fix], folks! @@ -9774,7 +9795,7 @@ static dsql_nod* pass1_variable( CompiledStatement* statement, dsql_nod* input) @param context **/ -static dsql_nod* post_map( dsql_nod* node, dsql_ctx* context) +static dsql_nod* post_map(dsql_nod* node, dsql_ctx* context) { DEV_BLKCHK(node, dsql_type_nod); DEV_BLKCHK(context, dsql_type_ctx); @@ -9827,7 +9848,7 @@ static dsql_nod* post_map( dsql_nod* node, dsql_ctx* context) @param current_level **/ -static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, +static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, bool window, dsql_ctx* context, USHORT current_level) { DEV_BLKCHK(statement, dsql_type_req); @@ -9842,7 +9863,7 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, case nod_alias: field->nod_arg[e_alias_value] = - remap_field(statement, field->nod_arg[e_alias_value], context, current_level); + remap_field(statement, field->nod_arg[e_alias_value], window, context, current_level); return field; case nod_derived_field: @@ -9857,7 +9878,7 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, if (context->ctx_scope_level < lscope_level) { field->nod_arg[e_derived_field_value] = - remap_field(statement, field->nod_arg[e_derived_field_value], + remap_field(statement, field->nod_arg[e_derived_field_value], window, context, current_level); } return field; @@ -9876,14 +9897,58 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, case nod_map: { const dsql_ctx* lcontext = reinterpret_cast(field->nod_arg[e_map_context]); - if (lcontext->ctx_scope_level != context->ctx_scope_level) { + if (lcontext->ctx_scope_level != context->ctx_scope_level) + { dsql_map* lmap = reinterpret_cast(field->nod_arg[e_map_map]); - lmap->map_node = remap_field(statement, lmap->map_node, context, + lmap->map_node = remap_field(statement, lmap->map_node, window, context, lcontext->ctx_scope_level); } + + if (window && lcontext->ctx_scope_level == context->ctx_scope_level) + return post_map(field, context); + return field; } + case nod_window: + { + dsql_nod* windowNode = field; + field = field->nod_arg[e_window_expr]; + + if (pass1_found_aggregate(field->nod_arg[e_agg_function_expression], + context->ctx_scope_level, FIELD_MATCH_TYPE_EQUAL, true, true)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_agg_nested_err)); + } + + USHORT ldeepest_level = statement->req_scope_level; + USHORT lcurrent_level = current_level; + if (aggregate_found2(statement, field, false, &lcurrent_level, &ldeepest_level, false)) + { + if (!window && field->nod_count) + { + field->nod_arg[e_agg_function_expression] = + remap_field(statement, field->nod_arg[e_agg_function_expression], + window, context, current_level); + } + + if (window && statement->req_scope_level == ldeepest_level) + return post_map(field, context); + + return windowNode; + } + + if (!window && field->nod_count) + { + field->nod_arg[e_agg_function_expression] = + remap_field(statement, field->nod_arg[e_agg_function_expression], + window, context, current_level); + } + + return windowNode; + } + case nod_agg_count: case nod_agg_min: case nod_agg_max: @@ -9895,16 +9960,16 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, { USHORT ldeepest_level = statement->req_scope_level; USHORT lcurrent_level = current_level; - if (aggregate_found2(statement, field, &lcurrent_level, &ldeepest_level, false)) + if (aggregate_found2(statement, field, false, &lcurrent_level, &ldeepest_level, false)) { - if (statement->req_scope_level == ldeepest_level) { + if (!window && statement->req_scope_level == ldeepest_level) { return post_map(field, context); } if (field->nod_count) { field->nod_arg[e_agg_function_expression] = remap_field(statement, field->nod_arg[e_agg_function_expression], - context, current_level); + window, context, current_level); } return field; } @@ -9912,27 +9977,27 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, if (field->nod_count) { field->nod_arg[e_agg_function_expression] = remap_field(statement, field->nod_arg[e_agg_function_expression], - context, current_level); + window, context, current_level); } return field; } case nod_via: field->nod_arg[e_via_rse] = - remap_field(statement, field->nod_arg[e_via_rse], context, current_level); + remap_field(statement, field->nod_arg[e_via_rse], window, context, current_level); field->nod_arg[e_via_value_1] = field->nod_arg[e_via_rse]->nod_arg[e_rse_items]->nod_arg[0]; return field; case nod_rse: current_level++; field->nod_arg[e_rse_streams] = - remap_field(statement, field->nod_arg[e_rse_streams], context, current_level); + remap_field(statement, field->nod_arg[e_rse_streams], window, context, current_level); field->nod_arg[e_rse_boolean] = - remap_field(statement, field->nod_arg[e_rse_boolean], context, current_level); + remap_field(statement, field->nod_arg[e_rse_boolean], window, context, current_level); field->nod_arg[e_rse_items] = - remap_field(statement, field->nod_arg[e_rse_items], context, current_level); + remap_field(statement, field->nod_arg[e_rse_items], window, context, current_level); field->nod_arg[e_rse_sort] = - remap_field(statement, field->nod_arg[e_rse_sort], context, current_level); + remap_field(statement, field->nod_arg[e_rse_sort], window, context, current_level); current_level--; return field; @@ -9942,7 +10007,7 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, dsql_nod** ptr = field->nod_arg; for (const dsql_nod* const* const end = ptr + field->nod_count; ptr < end; ptr++) { - *ptr = remap_field(statement, *ptr, context, current_level); + *ptr = remap_field(statement, *ptr, window, context, current_level); } return field; } @@ -9950,18 +10015,20 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, case nod_coalesce: // ASF: We had deliberately changed nod_count to 1, to not process the second list. // But we should remap its fields. CORE-2176 - field->nod_arg[0] = remap_field(statement, field->nod_arg[0], context, current_level); - field->nod_arg[1] = remap_field(statement, field->nod_arg[1], context, current_level); + field->nod_arg[0] = remap_field(statement, field->nod_arg[0], window, context, + current_level); + field->nod_arg[1] = remap_field(statement, field->nod_arg[1], window, context, + current_level); return field; case nod_aggregate: field->nod_arg[e_agg_rse] = - remap_field(statement, field->nod_arg[e_agg_rse], context, current_level); + remap_field(statement, field->nod_arg[e_agg_rse], window, context, current_level); return field; case nod_order: field->nod_arg[e_order_field] = - remap_field(statement, field->nod_arg[e_order_field], context, current_level); + remap_field(statement, field->nod_arg[e_order_field], window, context, current_level); return field; case nod_or: @@ -10024,7 +10091,7 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, dsql_nod** ptr = field->nod_arg; for (const dsql_nod* const* const end = ptr + field->nod_count; ptr < end; ptr++) { - *ptr = remap_field(statement, *ptr, context, current_level); + *ptr = remap_field(statement, *ptr, window, context, current_level); } return field; } @@ -10032,10 +10099,17 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, case nod_cast: case nod_gen_id: case nod_gen_id2: - case nod_udf: case nod_sys_function: if (field->nod_count == 2) { - field->nod_arg[1] = remap_field(statement, field->nod_arg[1], context, current_level); + field->nod_arg[1] = remap_field(statement, field->nod_arg[1], window, context, + current_level); + } + return field; + + case nod_udf: + if (field->nod_count == 3) { + field->nod_arg[2] = remap_field(statement, field->nod_arg[2], window, context, + current_level); } return field; @@ -10045,19 +10119,19 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, // Check if relation is a procedure if (lrelation_context->ctx_procedure) { // Remap the input parameters - lrelation_context->ctx_proc_inputs = - remap_field(statement, lrelation_context->ctx_proc_inputs, context, current_level); + lrelation_context->ctx_proc_inputs = remap_field(statement, + lrelation_context->ctx_proc_inputs, window, context, current_level); } return field; } case nod_derived_table: - remap_field(statement, field->nod_arg[e_derived_table_rse], context, current_level); + remap_field(statement, field->nod_arg[e_derived_table_rse], window, context, current_level); return field; case nod_hidden_var: field->nod_arg[e_hidden_var_expr] = remap_field(statement, - field->nod_arg[e_hidden_var_expr], context, current_level); + field->nod_arg[e_hidden_var_expr], window, context, current_level); return field; case nod_dbkey: @@ -10082,7 +10156,8 @@ static dsql_nod* remap_field(CompiledStatement* statement, dsql_nod* field, @param context **/ -static dsql_nod* remap_fields(CompiledStatement* statement, dsql_nod* fields, dsql_ctx* context) +static dsql_nod* remap_fields(CompiledStatement* statement, dsql_nod* fields, bool window, + dsql_ctx* context) { DEV_BLKCHK(statement, dsql_type_req); DEV_BLKCHK(fields, dsql_type_nod); @@ -10092,12 +10167,12 @@ static dsql_nod* remap_fields(CompiledStatement* statement, dsql_nod* fields, ds { for (int i = 0; i < fields->nod_count; i++) { - fields->nod_arg[i] = - remap_field(statement, fields->nod_arg[i], context, statement->req_scope_level); + fields->nod_arg[i] = remap_field(statement, fields->nod_arg[i], window, context, + statement->req_scope_level); } } else { - fields = remap_field(statement, fields, context, statement->req_scope_level); + fields = remap_field(statement, fields, window, context, statement->req_scope_level); } return fields; @@ -10165,6 +10240,7 @@ static void remap_streams_to_parent_context( dsql_nod* input, dsql_ctx* parent_c break; case nod_aggregate: + // nothing to do here. break; default: @@ -10255,7 +10331,7 @@ static dsql_fld* resolve_context( CompiledStatement* statement, const dsql_str* if (relation) table_name = relation->rel_name.c_str(); else - table_name = procedure->prc_name.c_str(); + table_name = procedure->prc_name.identifier.c_str(); } // If a context qualifier is present, make sure this is the proper context @@ -10327,7 +10403,7 @@ static bool set_parameter_type(CompiledStatement* statement, dsql_nod* in_node, dsql_nod* node, bool force_varchar) { thread_db* tdbb = JRD_get_thread_data(); - Attachment* att = tdbb->getAttachment(); + Jrd::Attachment* att = tdbb->getAttachment(); DEV_BLKCHK(in_node, dsql_type_nod); DEV_BLKCHK(node, dsql_type_nod); @@ -10345,11 +10421,11 @@ static bool set_parameter_type(CompiledStatement* statement, dsql_nod* in_node, { MAKE_desc(statement, &in_node->nod_desc, node, NULL); - if (att->att_charset != CS_NONE && att->att_charset != CS_BINARY) + if (tdbb->getCharSet() != CS_NONE && tdbb->getCharSet() != CS_BINARY) { const USHORT fromCharSet = in_node->nod_desc.getCharSet(); const USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? - fromCharSet : att->att_charset; + fromCharSet : tdbb->getCharSet(); if (in_node->nod_desc.dsc_dtype <= dtype_any_text) { @@ -10956,37 +11032,6 @@ void DSQL_pretty(const dsql_nod* node, int column) case nod_del_view: verb = "delete view"; break; - case nod_def_procedure: - verb = "define procedure"; - break; - case nod_mod_procedure: - verb = "modify procedure"; - break; - case nod_replace_procedure: - verb = "replace procedure"; - break; - // CVC: New node redef_procedure. - case nod_redef_procedure: - verb = "redefine procedure"; - break; - case nod_del_procedure: - verb = "delete procedure"; - break; - case nod_def_trigger: - verb = "define trigger"; - break; - case nod_mod_trigger: - verb = "modify trigger"; - break; - case nod_replace_trigger: - verb = "replace trigger"; - break; - case nod_redef_trigger: - verb = "redefine trigger"; - break; - case nod_del_trigger: - verb = "delete trigger"; - break; case nod_divide: verb = "divide"; break; @@ -11007,9 +11052,6 @@ void DSQL_pretty(const dsql_nod* node, int column) case nod_exec_procedure: verb = "execute procedure"; break; - case nod_exec_block: - verb = "execute block"; - break; case nod_exists: verb = "exists"; break; @@ -11544,6 +11586,10 @@ void DSQL_pretty(const dsql_nod* node, int column) verb = "breakleave"; break; + case nod_continue: + verb = "continue"; + break; + case nod_for_select: verb = "for_select"; break; @@ -11606,7 +11652,7 @@ void DSQL_pretty(const dsql_nod* node, int column) trace_line("%sfield %s.%s, context %d\n", buffer, (relation != NULL ? relation->rel_name.c_str() : - (procedure != NULL ? procedure->prc_name.c_str() : "unknown_db_object")), + (procedure != NULL ? procedure->prc_name.toString().c_str() : "unknown_db_object")), field->fld_name.c_str(), context->ctx_context); return; } @@ -11650,7 +11696,7 @@ void DSQL_pretty(const dsql_nod* node, int column) } else if ( procedure != NULL ) { trace_line("%sprocedure %s, context %d\n", - buffer, procedure->prc_name.c_str(), context->ctx_context); + buffer, procedure->prc_name.toString().c_str(), context->ctx_context); } else { trace_line("%sUNKNOWN DB OBJECT, context %d\n", @@ -11694,7 +11740,7 @@ void DSQL_pretty(const dsql_nod* node, int column) switch (node->nod_arg[e_udf_name]->getType()) { case dsql_type_udf: - trace_line ("%s\"\n", ((dsql_udf*) node->nod_arg[e_udf_name])->udf_name.c_str()); + trace_line ("%s\"\n", ((dsql_udf*) node->nod_arg[e_udf_name])->udf_name.toString().c_str()); break; case dsql_type_str: string = (dsql_str*) node->nod_arg[e_udf_name]; @@ -11732,10 +11778,6 @@ void DSQL_pretty(const dsql_nod* node, int column) verb = "query_spec"; break; - case nod_comment: - verb = "comment"; - break; - case nod_mod_udf: verb = "mod_udf"; break; @@ -11849,6 +11891,10 @@ void DSQL_pretty(const dsql_nod* node, int column) verb = "hidden_var"; break; + case nod_mod_field_null_flag: + verb = "nod_mod_field_null_flag"; + break; + default: sprintf(s, "unknown type %d", node->nod_type); verb = s; diff --git a/src/dsql/pass1_proto.h b/src/dsql/pass1_proto.h index 0fe27cef5a..8177e11ee1 100644 --- a/src/dsql/pass1_proto.h +++ b/src/dsql/pass1_proto.h @@ -24,8 +24,11 @@ #ifndef DSQL_PASS1_PROTO_H #define DSQL_PASS1_PROTO_H +void PASS1_check_unique_fields_names(Jrd::StrArray& names, const Jrd::dsql_nod* fields); Jrd::dsql_ctx* PASS1_make_context(Jrd::CompiledStatement* statement, const Jrd::dsql_nod* relation_node); Jrd::dsql_nod* PASS1_node(Jrd::CompiledStatement*, Jrd::dsql_nod*); +Jrd::dsql_nod* PASS1_node_psql(Jrd::CompiledStatement*, Jrd::dsql_nod*, bool); +Jrd::dsql_nod* PASS1_resolve_variable_name(const Jrd::dsql_nod* var_nodes, const Jrd::dsql_str* var_name); Jrd::dsql_nod* PASS1_rse(Jrd::CompiledStatement*, Jrd::dsql_nod*, Jrd::dsql_nod*); Jrd::dsql_nod* PASS1_statement(Jrd::CompiledStatement*, Jrd::dsql_nod*); diff --git a/src/dudley/exe.epp b/src/dudley/exe.epp index cdb2748813..f247834778 100644 --- a/src/dudley/exe.epp +++ b/src/dudley/exe.epp @@ -2972,7 +2972,7 @@ static void get_triggers( DBB databaseL) trigger->trg_relation = (DUDLEY_REL) rel_name->sym_object; else trigger->trg_relation = (DUDLEY_REL) get_symbol(SYM_relation, T.RDB$RELATION_NAME, NULL); - trigger->trg_type = (TRG_T) T.RDB$TRIGGER_TYPE; + trigger->trg_type = (TRG_T)(SINT64) T.RDB$TRIGGER_TYPE; trigger->trg_inactive = T.RDB$TRIGGER_INACTIVE; trigger->trg_sequence = T.RDB$TRIGGER_SEQUENCE; END_FOR; diff --git a/src/dudley/extract.epp b/src/dudley/extract.epp index 6240758354..9bbd41af9c 100644 --- a/src/dudley/extract.epp +++ b/src/dudley/extract.epp @@ -1354,34 +1354,20 @@ static void extract_triggers() if (T.RDB$TRIGGER_INACTIVE) fprintf(output_file, "\tinactive\n"); - switch (T.RDB$TRIGGER_TYPE) { - case trg_store: + if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_store) fprintf(output_file, "\tpre store"); - break; - - case trg_post_store: + else if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_post_store) fprintf(output_file, "\tpost store"); - break; - - case trg_modify: + else if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_modify) fprintf(output_file, "\tpre modify"); - break; - - case trg_post_modify: + else if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_post_modify) fprintf(output_file, "\tpost modify"); - break; - - case trg_pre_erase: + else if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_pre_erase) fprintf(output_file, "\tpre erase"); - break; - - case trg_erase: + else if (T.RDB$TRIGGER_TYPE == (SSHORT) trg_erase) fprintf(output_file, "\tpost erase"); - break; - - default: + else put_comment(261); /* msg 261: ***** trigger type not understood **** */ - } fprintf(output_file, " %d:\n", T.RDB$TRIGGER_SEQUENCE); if (T.RDB$TRIGGER_SOURCE.NULL) { diff --git a/src/include/FirebirdApi.h b/src/include/FirebirdApi.h new file mode 100644 index 0000000000..1bccb5a6dd --- /dev/null +++ b/src/include/FirebirdApi.h @@ -0,0 +1,227 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project, based on previous work done + * by Eugeney Putilin , + * Vlad Khorsun and + * Roman Rokytskyy . + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Eugeney Putilin + * Vlad Khorsun + * Roman Rokytskyy + */ + +#ifndef FIREBIRD_API_H +#define FIREBIRD_API_H + +#ifdef __GNUC__ +# pragma GCC system_header // disable warning about non-existent virtual destructor +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define FB_CALL __stdcall +#else +# define FB_CALL +#endif + +#define FB_NULL 0L + + +namespace Firebird { + + +typedef unsigned int uint; +typedef short int16; +typedef int int32; + +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__) +typedef __int64 int64; +#else +typedef long long int int64; +#endif + +typedef char Utf8; // Utf8* used as nul-terminated string + +// ISC compatible handle +#if defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(_WIN64) +typedef unsigned int Handle; +#else +typedef void* Handle; +#endif + + +struct Date +{ + int year; + int month; + int day; +}; + + +struct Time +{ + int hours; + int minutes; + int seconds; + int fractions; +}; + + +struct DateTime //// FIXME: rename to TimeStamp +{ + Date date; + Time time; +}; + + +class Error +{ +public: + virtual bool FB_CALL addCode(int32 code) = 0; + virtual bool FB_CALL addString(const char* str, uint strLength) = 0; +}; + + +// Interface used in cases which an instance is created by one layer and released by another one. +class Disposable +{ +public: + // Disposes the object. + virtual void FB_CALL dispose(Error* error) = 0; +}; + + +class Attachment : public Disposable +{ +public: + // Get an ISC compatible attachment handle. + virtual Handle FB_CALL getHandle(Error* error) const = 0; + + virtual const char* FB_CALL getUserName() const = 0; + virtual const char* FB_CALL getDatabaseName() const = 0; +}; + + +class Transaction +{ +public: + // Get an ISC compatible transaction handle. + virtual Handle FB_CALL getHandle(Error* error) const = 0; +}; + + +// Represents a parameter or column. +class Value +{ +public: + // data types + enum Type + { + TYPE_SMALLINT = 1, + TYPE_INTEGER, + TYPE_BIGINT, + TYPE_DOUBLE, + TYPE_CHAR, + TYPE_VARCHAR, + TYPE_BLOB, + TYPE_DATE, + TYPE_TIME, + TYPE_TIMESTAMP + }; + +public: + // Get parameter or column name. + virtual const char* FB_CALL getName(Error* error) const = 0; + + virtual Type FB_CALL getType(Error* error) const = 0; + virtual const char* FB_CALL getCharSet(Error* error) const = 0; + + // Get BLOB sub-type. + virtual int FB_CALL getSubType(Error* error) const = 0; + + // Get numeric precision or maximum string length. + virtual int FB_CALL getPrecision(Error* error) const = 0; + + virtual int FB_CALL getScale(Error* error) const = 0; + virtual bool FB_CALL isNullable(Error* error) const = 0; + + virtual bool FB_CALL isNull() const = 0; + virtual void FB_CALL setNull(Error* error) = 0; + + virtual void FB_CALL copyFrom(Error* error, const Value* from) = 0; + + virtual int16 FB_CALL getSmallInt(Error* error, int scale = 0, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setSmallInt(Error* error, int16 value, int scale = 0) = 0; + + virtual int32 FB_CALL getInt(Error* error, int scale = 0, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setInt(Error* error, int32 value, int scale = 0) = 0; + + virtual int64 FB_CALL getBigInt(Error* error, int scale = 0, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setBigInt(Error* error, int64 value, int scale = 0) = 0; + + virtual double FB_CALL getDouble(Error* error, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setDouble(Error* error, double value) = 0; + + virtual const char* FB_CALL getString(Error* error, uint* strLength = FB_NULL, + bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setString(Error* error, const char* str, uint strLength) = 0; + + virtual int64 FB_CALL getBlobId(Error* error, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setBlobId(Error* error, int64 value) = 0; + + virtual void FB_CALL getDate(Error* error, Date* value, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setDate(Error* error, const Date* value) = 0; + + virtual void FB_CALL getTime(Error* error, Time* value, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setTime(Error* error, const Time* value) = 0; + + virtual void FB_CALL getTimeStamp(Error* error, DateTime* value, bool* isNull = FB_NULL) const = 0; + virtual void FB_CALL setTimeStamp(Error* error, const DateTime* value) = 0; +}; + + +// A queue associated with a Values. Could be used for batching processing. +class ValuesQueue : public Disposable +{ +public: + virtual void FB_CALL enqueue(Error* error) = 0; // Enqueue the current Values. + virtual bool FB_CALL dequeue(Error* error) = 0; // Dequeue in Values. +}; + + +// Represents a group of parameters or columns. +class Values +{ +public: + virtual uint FB_CALL getCount() const = 0; + virtual uint FB_CALL getIndexByName(Error* error, const char* name) const = 0; + + // Get a given value. The first value is at index 1. + virtual Value* FB_CALL getValue(Error* error, uint index) const = 0; + + virtual Value* FB_CALL getValueByName(Error* error, const char* name) const = 0; + + // Creates a queue associated with this Values. + virtual ValuesQueue* FB_CALL createQueue(Error* error) = 0; +}; + + +} // namespace Firebird + + +#endif // FIREBIRD_API_H diff --git a/src/include/FirebirdExternalApi.h b/src/include/FirebirdExternalApi.h new file mode 100644 index 0000000000..256d31a026 --- /dev/null +++ b/src/include/FirebirdExternalApi.h @@ -0,0 +1,189 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project, based on previous work done + * by Eugeney Putilin , + * Vlad Khorsun and + * Roman Rokytskyy . + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Eugeney Putilin + * Vlad Khorsun + * Roman Rokytskyy + */ + +#ifndef FIREBIRD_EXTERNAL_API_H +#define FIREBIRD_EXTERNAL_API_H + +#ifdef __GNUC__ +# pragma GCC system_header // disable warning about non-existent virtual destructor +#endif + +#include "FirebirdApi.h" + + +namespace Firebird { + +class ExternalEngine; + + +const int EXTERNAL_VERSION_1 = 1; + + +// Connection to current database in external engine. +// Context passed to ExternalEngine has SYSDBA privileges. +// Context passed to ExternalFunction, ExternalProcedure and ExternalTrigger +// has user privileges. +// There is one ExternalContext per attachment. The privileges and character +// set properties are changed during the calls. +class ExternalContext +{ +public: + // Gets the ExternalEngine associated with this context. + virtual ExternalEngine* FB_CALL getEngine(Error* error) = 0; + + // Gets the Attachment associated with this context. + virtual Attachment* FB_CALL getAttachment(Error* error) = 0; + + // Obtained transaction is valid only before control is returned to the engine + // or in ExternalResultSet::fetch calls of correspondent ExternalProcedure::open. + virtual Transaction* FB_CALL getTransaction(Error* error) = 0; + + // Get user attachment character set. + virtual const Utf8* FB_CALL getClientCharSet() = 0; + + // Misc info associated with a context. The pointers are never accessed or freed by Firebird. + + // Obtains an unique (across all contexts) code to associate plugin and/or user information. + virtual int FB_CALL obtainInfoCode() = 0; + // Gets a value associated with this code or FB_NULL if no value was set. + virtual void* FB_CALL getInfo(int code) = 0; + // Sets a value associated with this code and returns the last value. + virtual void* FB_CALL setInfo(int code, void* value) = 0; +}; + + +// To return set of rows in selectable procedures. +class ExternalResultSet : public Disposable +{ +public: + virtual bool FB_CALL fetch(Error* error) = 0; +}; + + +class ExternalFunction : public Disposable +{ +public: + // This method is called just before execute and informs the engine our requested character + // set for data exchange inside that method. + // During this call, the context uses the character set obtained from ExternalEngine::getCharSet. + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) = 0; + + virtual void FB_CALL execute(Error* error, ExternalContext* context, Values* params, + Value* result) = 0; +}; + + +class ExternalProcedure : public Disposable +{ +public: + // This method is called just before open and informs the engine our requested character + // set for data exchange inside that method and ExternalResultSet::fetch. + // During this call, the context uses the character set obtained from ExternalEngine::getCharSet. + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) = 0; + + // Returns a ExternalResultSet for selectable procedures. + // Returning NULL results in a result set of one record. + // Procedures without output parameters should return NULL. + virtual ExternalResultSet* FB_CALL open(Error* error, ExternalContext* context, + Values* params, Values* results) = 0; +}; + + +class ExternalTrigger : public Disposable +{ +public: + enum Type + { + TYPE_BEFORE = 1, + TYPE_AFTER, + TYPE_DATABASE + }; + + enum Action + { + ACTION_INSERT = 1, + ACTION_UPDATE, + ACTION_DELETE, + ACTION_CONNECT, + ACTION_DISCONNECT, + ACTION_TRANS_START, + ACTION_TRANS_COMMIT, + ACTION_TRANS_ROLLBACK, + ACTION_DDL + }; + +public: + // This method is called just before execute and informs the engine our requested character + // set for data exchange inside that method. + // During this call, the context uses the character set obtained from ExternalEngine::getCharSet. + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) = 0; + + virtual void FB_CALL execute(Error* error, ExternalContext* context, + Action action, const Values* oldValues, Values* newValues) = 0; +}; + + +// In SuperServer, shared by all attachments to one database and disposed when last (non-external) +// user attachment to the database is closed. +class ExternalEngine : public Disposable +{ +public: + virtual int FB_CALL getVersion(Error* error) = 0; + + // This method is called once (per ExternalEngine instance) before any following methods. + // The requested character set for data exchange inside methods of this interface should + // be copied to charSet parameter. + // During this call, the context uses the UTF-8 character set. + virtual void FB_CALL open(Error* error, ExternalContext* context, + Utf8* charSet, uint charSetSize) = 0; + + // Attachment is being opened. + virtual void FB_CALL openAttachment(Error* error, ExternalContext* context) = 0; + + // Attachment is being closed. + virtual void FB_CALL closeAttachment(Error* error, ExternalContext* context) = 0; + + // Called when engine wants to load object in the cache. Objects are disposed when + // going out of the cache. + virtual ExternalFunction* FB_CALL makeFunction(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body) = 0; + virtual ExternalProcedure* FB_CALL makeProcedure(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body) = 0; + virtual ExternalTrigger* FB_CALL makeTrigger(Error* error, ExternalContext* context, + const char* name, const char* entryPoint, const char* body, + const char* table, ExternalTrigger::Type type) = 0; +}; + + +} // namespace Firebird + + +#endif // FIREBIRD_EXTERNAL_API_H diff --git a/src/include/FirebirdPluginApi.h b/src/include/FirebirdPluginApi.h new file mode 100644 index 0000000000..e9f2a401ec --- /dev/null +++ b/src/include/FirebirdPluginApi.h @@ -0,0 +1,123 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +/* + * When Firebird is initializing, it opens all *.conf files from /plugins. + * For each plugin_module tag found, it constructs a Plugin object, reads the corresponding + * plugin_config tag and inserts all config information in the object. + * + * When requested, the engine gets the attribute value of plugin_module/filename, load it as a + * dynamic (shared) library and calls the exported function firebirdPlugin (FB_PLUGIN_ENTRY_POINT + * definition, InitPluginEntryPoint prototype) passing the Plugin object as parameter. + * + * The plugin library may save the plugin object and call they methods later. The object and all + * pointers returned by it are valid until the plugin is unloaded (done through OS unload of the + * dynamic library) when Firebird is shutting down. + * + * Inside the plugin entry point (firebirdPlugin), the plugin may register extra functionality that + * may be obtained by Firebird when required. Currently only External Engines may be registered + * through Plugin::setExternalEngineFactory. + * + * Example plugin configuration file: + * + * + * plugin_module UDR_engine + * + * + * + * filename $(this)/udr_engine + * plugin_config UDR_config + * + * + * + * path $(this)/udr + * + * + * Note that the external_engine tag is ignored at this stage. Only plugin_module and plugin_config + * are read. The dynamic library extension may be ommitted, and $(this) expands to the directory of + * the .conf file. + * + * Plugins may access Firebird API through the client library read from Plugin::getLibraryName + * method. This method may return different filenames depending on the server architecture, and may + * even return NULL. Currently it returns values as the table below, but this is implementation + * detail and portable plugins should just expect a filename or NULL. + * + * Architecture File + * --------------- ------------------------------------------------------ + * Embedded The embedded library + * Windows SS fbserver executable + * Windows CS/SC fb_inet_server executable + * POSIX CS/SC The embedded library + * POSIX SS NULL [application should open it through dlopen(NULL)] + */ + +#ifndef FIREBIRD_PLUGIN_API_H +#define FIREBIRD_PLUGIN_API_H + +#ifdef __GNUC__ +# pragma GCC system_header // disable warning about non-existent virtual destructor +#endif + +#include "FirebirdApi.h" + +#define FB_PLUGIN_ENTRY_POINT firebirdPlugin + + +namespace Firebird { + +class ExternalEngine; + + +const int PLUGIN_VERSION_1 = 1; + + +class ExternalEngineFactory +{ +public: + // Strings passed to error->addString during this call are assumed to be UTF-8. + virtual ExternalEngine* FB_CALL createEngine(Error* error, int version, + const char* name) = 0; +}; + + +// Passed to plugin library through entry point FB_PLUGIN_ENTRY_POINT in Firebird initialization. +class Plugin +{ +public: + virtual int FB_CALL getVersion() = 0; + virtual const char* FB_CALL getName() = 0; + virtual const char* FB_CALL getLibraryName() = 0; + virtual uint FB_CALL getConfigInfoCount() = 0; + virtual void FB_CALL getConfigInfo(Error* error, uint index, // first index is 0 + const char** key, const char** value) = 0; + + virtual void FB_CALL setExternalEngineFactory(ExternalEngineFactory* factory) = 0; +}; + + +typedef void (*PluginEntryPoint)(Error* error, Plugin* plugin); + + +} // namespace Firebird + + +#endif // FIREBIRD_PLUGIN_API_H diff --git a/src/include/FirebirdUdr.h b/src/include/FirebirdUdr.h new file mode 100644 index 0000000000..a5b9072750 --- /dev/null +++ b/src/include/FirebirdUdr.h @@ -0,0 +1,95 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef FIREBIRD_UDR_H +#define FIREBIRD_UDR_H + +#ifdef __GNUC__ +# pragma GCC system_header // disable warning about non-existent virtual destructor +#endif + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" + + +namespace Firebird +{ + namespace Udr + { +//------------------------------------------------------------------------------ + + +// Metadata information passed from the UDR engine to user's routines factories when asking to +// create routines instances. +struct MetaInfo +{ + const char* package; // package name - NULL from triggers + const char* name; // metadata object name + const char* entryPoint; // external routine's name + const char* info; // misc. info encoded on the external name + const char* body; // body text +}; + +struct TriggerMetaInfo : public MetaInfo +{ + ExternalTrigger::Type type; // trigger type + const char* table; // table name +}; + + +// Factory classes. They should be singletons instances created by user's modules and +// registered. When UDR engine is going to load a routine, it calls newItem. + +class FunctionFactory +{ +public: + virtual const char* FB_CALL getName() = 0; + virtual ExternalFunction* FB_CALL newItem(MetaInfo* metaInfo) = 0; +}; + +class ProcedureFactory +{ +public: + virtual const char* FB_CALL getName() = 0; + virtual ExternalProcedure* FB_CALL newItem(MetaInfo* metaInfo) = 0; +}; + +class TriggerFactory +{ +public: + virtual const char* FB_CALL getName() = 0; + virtual ExternalTrigger* FB_CALL newItem(TriggerMetaInfo* metaInfo) = 0; +}; + + +// Routine registration functions. +extern "C" void fbUdrRegFunction(FunctionFactory* factory); +extern "C" void fbUdrRegProcedure(ProcedureFactory* factory); +extern "C" void fbUdrRegTrigger(TriggerFactory* factory); +extern "C" void* fbUdrGetFunction(const char* symbol); + + +//------------------------------------------------------------------------------ + } // namespace Udr +} // namespace Firebird + +#endif // FIREBIRD_UDR_H diff --git a/src/include/FirebirdUdrCpp.h b/src/include/FirebirdUdrCpp.h new file mode 100644 index 0000000000..dd62d9e7cd --- /dev/null +++ b/src/include/FirebirdUdrCpp.h @@ -0,0 +1,634 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef FIREBIRD_PLUGIN_UDR_CPP +#define FIREBIRD_PLUGIN_UDR_CPP + +#ifdef __GNUC__ +# pragma GCC system_header // disable warning about non-existent virtual destructor +#endif + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "FirebirdUdr.h" +#include "ibase.h" +#include + + +namespace Firebird +{ + namespace Udr + { +//------------------------------------------------------------------------------ + + +#define FB_UDR_FUNCTION(name) Func##name +#define FB_UDR_PROCEDURE(name) Proc##name +#define FB_UDR_TRIGGER(name) Trig##name + + +#define FB_UDR_BEGIN_DECLARE_FUNCTION(name) \ + class FB_UDR_FUNCTION(name) : public ::Firebird::Udr::Function \ + { \ + public: \ + virtual void FB_CALL execute(::Firebird::Error* error, ::Firebird::ExternalContext* context, \ + ::Firebird::Values* params, ::Firebird::Value* result); \ + private: + +#define FB_UDR_END_DECLARE_FUNCTION(name) \ + }; + +#define FB_UDR_DECLARE_FUNCTION(name) \ + FB_UDR_BEGIN_DECLARE_FUNCTION(name) \ + FB_UDR_END_DECLARE_FUNCTION(name) + +#define FB_UDR_BEGIN_FUNCTION(name) \ + void FB_CALL FB_UDR_FUNCTION(name)::execute(::Firebird::Error* error, \ + ::Firebird::ExternalContext* context, ::Firebird::Values* params, ::Firebird::Value* result) \ + { \ + try \ + { + +#define FB_UDR_END_FUNCTION(name) \ + } \ + catch (const ::Firebird::Udr::ThrowError::Exception& e) \ + { \ + e.stuff(error); \ + } \ + catch (...) \ + { \ + error->addCode(isc_arg_gds); \ + error->addCode(isc_random); \ + error->addString( \ + FB_UDR_UNRECOGNIZED_EXCEPTION, \ + strlen(FB_UDR_UNRECOGNIZED_EXCEPTION)); \ + } \ + } \ + ::Firebird::Udr::FunctionFactoryImpl FuncFactory##name(#name); + + +#define FB_UDR_BEGIN_DECLARE_PROCEDURE(name) \ + class FB_UDR_PROCEDURE(name) : public ::Firebird::Udr::Procedure \ + { \ + public: \ + virtual ::Firebird::ExternalResultSet* FB_CALL open(::Firebird::Error* error, \ + ::Firebird::ExternalContext* context, ::Firebird::Values* params, \ + ::Firebird::Values* results); \ + +#define FB_UDR_END_DECLARE_PROCEDURE(name) \ + }; + +#define FB_UDR_DECLARE_PROCEDURE(name) \ + FB_UDR_BEGIN_DECLARE_PROCEDURE(name) \ + FB_UDR_END_DECLARE_PROCEDURE(name) + +#define FB_UDR_BEGIN_DECLARE_FETCH_PROCEDURE(name) \ + class ResultSet##name : public ::Firebird::Udr::ResultSet \ + { \ + public: \ + ResultSet##name(::Firebird::Error* error, ::Firebird::ExternalContext* context, \ + ::Firebird::Values* params, ::Firebird::Values* results); \ + \ + public: \ + virtual bool FB_CALL fetch(::Firebird::Error* error); \ + \ + private: + +#define FB_UDR_END_DECLARE_FETCH_PROCEDURE(name) \ + }; + +#define FB_UDR_DECLARE_FETCH_PROCEDURE(name) \ + FB_UDR_BEGIN_DECLARE_FETCH_PROCEDURE(name) \ + FB_UDR_END_DECLARE_FETCH_PROCEDURE(name) + +#define FB_UDR_DECLARE_PROCEDURE(name) \ + FB_UDR_BEGIN_DECLARE_PROCEDURE(name) \ + FB_UDR_END_DECLARE_PROCEDURE(name) + +#define FB_UDR_BEGIN_PROCEDURE(name) \ + ::Firebird::ExternalResultSet* FB_CALL Proc##name::open(::Firebird::Error* error, \ + ::Firebird::ExternalContext* context, ::Firebird::Values* params, \ + ::Firebird::Values* results) \ + { \ + return new ResultSet##name(error, context, params, results); \ + } \ + \ + ResultSet##name::ResultSet##name(::Firebird::Error* error, ::Firebird::ExternalContext* context, \ + ::Firebird::Values* params, ::Firebird::Values* results) \ + : ResultSet(context, params, results) \ + { \ + try \ + { + +#define FB_UDR_FETCH_PROCEDURE(name) \ + } \ + catch (const ::Firebird::Udr::ThrowError::Exception& e) \ + { \ + e.stuff(error); \ + } \ + catch (...) \ + { \ + error->addCode(isc_arg_gds); \ + error->addCode(isc_random); \ + error->addString( \ + FB_UDR_UNRECOGNIZED_EXCEPTION, \ + strlen(FB_UDR_UNRECOGNIZED_EXCEPTION)); \ + } \ + } \ + \ + bool FB_CALL ResultSet##name::fetch(::Firebird::Error* error) \ + { \ + try \ + { + +#define FB_UDR_END_PROCEDURE(name) \ + } \ + catch (const ::Firebird::Udr::ThrowError::Exception& e) \ + { \ + e.stuff(error); \ + } \ + catch (...) \ + { \ + error->addCode(isc_arg_gds); \ + error->addCode(isc_random); \ + error->addString( \ + FB_UDR_UNRECOGNIZED_EXCEPTION, \ + strlen(FB_UDR_UNRECOGNIZED_EXCEPTION)); \ + } \ + return false; \ + } \ + ::Firebird::Udr::ProcedureFactoryImpl ProcFactory##name(#name); + + +#define FB_UDR_BEGIN_DECLARE_TRIGGER(name) \ + class FB_UDR_TRIGGER(name) : public ::Firebird::Udr::Trigger \ + { \ + public: \ + virtual void FB_CALL execute(::Firebird::Error* error, ::Firebird::ExternalContext* context, \ + ::Firebird::ExternalTrigger::Action action, const ::Firebird::Values* oldValues, \ + ::Firebird::Values* newValues); \ + private: + +#define FB_UDR_END_DECLARE_TRIGGER(name) \ + }; + +#define FB_UDR_DECLARE_TRIGGER(name) \ + FB_UDR_BEGIN_DECLARE_TRIGGER(name) \ + FB_UDR_END_DECLARE_TRIGGER(name) + +#define FB_UDR_BEGIN_TRIGGER(name) \ + void FB_CALL FB_UDR_TRIGGER(name)::execute(::Firebird::Error* error, \ + ::Firebird::ExternalContext* context, ::Firebird::ExternalTrigger::Action action, \ + const ::Firebird::Values* oldValues, ::Firebird::Values* newValues) \ + { \ + try \ + { + +#define FB_UDR_END_TRIGGER(name) \ + } \ + catch (const ::Firebird::Udr::ThrowError::Exception& e) \ + { \ + e.stuff(error); \ + } \ + catch (...) \ + { \ + error->addCode(isc_arg_gds); \ + error->addCode(isc_random); \ + error->addString( \ + FB_UDR_UNRECOGNIZED_EXCEPTION, \ + strlen(FB_UDR_UNRECOGNIZED_EXCEPTION)); \ + } \ + } \ + ::Firebird::Udr::TriggerFactoryImpl TrigFactory##name(#name); + + +#define FB_UDR_UNRECOGNIZED_EXCEPTION "Unrecognized C++ exception" + + +class ThrowError : public Error +{ +private: + struct Info + { + Info() + : next(FB_NULL), + str(FB_NULL) + { + } + + ~Info() + { + if (str) + delete [] str; + } + + static void free(Info* info) + { + while (info) + { + Info* p = info; + info = info->next; + delete p; + } + } + + enum { TYPE_CODE, TYPE_STR } type; + + Info* next; + int32 code; + char* str; + int strLength; + }; + +public: + class Exception + { + public: + Exception(Info* aInfo) + : info(aInfo) + { + } + + Exception(const Exception& e) + : info(FB_NULL) + { + Info* end = FB_NULL; + + for (Info* p = e.info; p; p = p->next) + { + Info* newInfo = new Info; + newInfo->type = p->type; + newInfo->code = p->code; + + if (p->str) + { + newInfo->str = new char[p->strLength]; + memcpy(newInfo->str, p->str, p->strLength); + newInfo->strLength = p->strLength; + } + + if (end) + end->next = newInfo; + + end = newInfo; + + if (!info) + info = newInfo; + } + } + + ~Exception() + { + Info::free(info); + } + + public: + void stuff(Error* error) const + { + for (Info* p = info; p; p = p->next) + { + if (p->type == Info::TYPE_CODE) + error->addCode(p->code); + else if (p->type == Info::TYPE_STR) + error->addString(p->str, p->strLength); + } + } + + private: + Info* info; + }; + +public: + ThrowError() + : start(FB_NULL), + end(FB_NULL) + { + } + + virtual ~ThrowError() + { + raise(); + Info::free(start); + } + +public: + static void check(ISC_STATUS status, const ISC_STATUS* vector) + { + if (status == 0) + return; + + ThrowError error; + + while (*vector != isc_arg_end) + { + switch (*vector) + { + case isc_arg_warning: + case isc_arg_gds: + case isc_arg_number: + case isc_arg_interpreted: + case isc_arg_vms: + case isc_arg_unix: + case isc_arg_win32: + error.addCode(*vector++); + error.addCode(*vector++); + break; + + case isc_arg_string: + error.addString((const char*) vector[1], strlen((const char*) vector[1])); + vector += 2; + break; + + case isc_arg_cstring: + error.addString((const char*) vector[2], vector[1]); + vector += 3; + break; + + default: + return; + } + } + } + + static void check(const ISC_STATUS* vector) + { + check(vector[1], vector); + } + +public: + inline operator Firebird::Error* () + { + return this; + } + +public: + virtual bool FB_CALL addCode(Firebird::int32 code) + { + Info* info = new Info; + info->type = Info::TYPE_CODE; + info->code = code; + + if (end) + end->next = info; + + end = info; + + if (!start) + start = info; + + return true; + } + + virtual bool FB_CALL addString(const char* str, uint strLength) + { + Info* info = new Info; + info->type = Info::TYPE_STR; + info->str = new char[strLength]; + memcpy(info->str, str, strLength); + info->strLength = strLength; + + if (end) + end->next = info; + + end = info; + + if (!start) + start = info; + + return true; + } + +private: + void raise() + { + if (start) + throw Exception(start); + } + +protected: + Info* start; + Info* end; +}; + + +class Helper +{ +public: + static isc_db_handle getIscDbHandle(ExternalContext* context) + { + Attachment* att = context->getAttachment(ThrowError()); + return att->getHandle(ThrowError()); + } + + static isc_tr_handle getIscTrHandle(ExternalContext* context) + { + Transaction* tra = context->getTransaction(ThrowError()); + return tra->getHandle(ThrowError()); + } + + static void* getEntryPoint(ExternalContext* context, const char* entryPoint) + { + return fbUdrGetFunction(entryPoint); + } +}; + + +class ResultSet : public ExternalResultSet, public Helper +{ +public: + ResultSet(Firebird::ExternalContext* aContext, Firebird::Values* aParams, + Firebird::Values* aResults) + : context(aContext), + params(aParams), + results(aResults) + { + } + + virtual ~ResultSet() + { + } + +public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + +protected: + Firebird::ExternalContext* context; + Firebird::Values* params; + Firebird::Values* results; +}; + + +class Function : public ExternalFunction, public Helper +{ +public: + virtual ~Function() + { + } + +public: + virtual void FB_CALL dispose(Error* error) + { + delete this; + } + + void FB_CALL getCharSet(Error* error, ExternalContext* context, Utf8* name, uint nameSize) + { + } + +public: + MetaInfo* metaInfo; +}; + + +class Procedure : public ExternalProcedure, public Helper +{ +public: + virtual ~Procedure() + { + } + +public: + virtual void FB_CALL dispose(Error* error) + { + delete this; + } + + void FB_CALL getCharSet(Error* error, ExternalContext* context, Utf8* name, uint nameSize) + { + } + +public: + MetaInfo* metaInfo; +}; + + +class Trigger : public ExternalTrigger, public Helper +{ +public: + virtual ~Trigger() + { + } + +public: + virtual void FB_CALL dispose(Error* error) + { + delete this; + } + + void FB_CALL getCharSet(Error* error, ExternalContext* context, Utf8* name, uint nameSize) + { + } + +public: + TriggerMetaInfo* metaInfo; +}; + + +template class FunctionFactoryImpl : public FunctionFactory +{ +public: + FunctionFactoryImpl(const char* aName) + : name(aName) + { + fbUdrRegFunction(this); + } + +public: + virtual const char* FB_CALL getName() + { + return name; + } + + virtual ExternalFunction* FB_CALL newItem(MetaInfo* metaInfo) + { + Function* function = new T(); + function->metaInfo = metaInfo; + return function; + } + +private: + const char* name; +}; + + +template class ProcedureFactoryImpl : public ProcedureFactory +{ +public: + ProcedureFactoryImpl(const char* aName) + : name(aName) + { + fbUdrRegProcedure(this); + } + +public: + virtual const char* FB_CALL getName() + { + return name; + } + + virtual ExternalProcedure* FB_CALL newItem(MetaInfo* metaInfo) + { + Procedure* procedure = new T(); + procedure->metaInfo = metaInfo; + return procedure; + } + +private: + const char* name; +}; + + +template class TriggerFactoryImpl : public TriggerFactory +{ +public: + TriggerFactoryImpl(const char* aName) + : name(aName) + { + fbUdrRegTrigger(this); + } + +public: + virtual const char* FB_CALL getName() + { + return name; + } + + virtual ExternalTrigger* FB_CALL newItem(TriggerMetaInfo* metaInfo) + { + Trigger* trigger = new T(); + trigger->metaInfo = metaInfo; + return trigger; + } + +private: + const char* name; +}; + + +//------------------------------------------------------------------------------ + } // namespace Udr +} // namespace Firebird + +#endif // FIREBIRD_PLUGIN_UDR_CPP diff --git a/src/include/consts_pub.h b/src/include/consts_pub.h index 69799a5369..04dc11f083 100644 --- a/src/include/consts_pub.h +++ b/src/include/consts_pub.h @@ -619,6 +619,7 @@ #define isc_dyn_prm_name 137 #define isc_dyn_sql_object 196 #define isc_dyn_fld_character_set_name 174 +#define isc_dyn_pkg_name 247 /********************************/ /* Relation specific attributes */ @@ -670,6 +671,8 @@ #define isc_dyn_fld_character_set 203 #define isc_dyn_del_computed 242 +#define isc_dyn_def_engine 245 + /***********************************/ /* Local field specific attributes */ /***********************************/ @@ -717,6 +720,9 @@ #define isc_dyn_grant_user_group 205 #define isc_dyn_grant_role 218 #define isc_dyn_grant_grantor 245 +#define isc_dyn_grant_package 248 + +#define isc_dyn_fld_null 249 /**********************************/ @@ -891,7 +897,7 @@ /****************************/ /* Last $dyn value assigned */ /****************************/ -#define isc_dyn_last_dyn_value 247 +#define isc_dyn_last_dyn_value 250 /******************************************/ /* Array slice description language (SDL) */ diff --git a/src/include/firebird.h b/src/include/firebird.h index 5abac0abfb..266a2fb067 100644 --- a/src/include/firebird.h +++ b/src/include/firebird.h @@ -87,7 +87,7 @@ using namespace NAMESPACE; // Comment this definition to build without priority scheduler // OR: // Uncomment this definition to build with priority scheduler -#define THREAD_PSCHED +//#define THREAD_PSCHED #endif #if defined(WIN_NT) diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 1be99fffc4..4609d8ebd1 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -689,6 +689,8 @@ static const struct { {"out_of_temp_space", 335544985}, {"eds_expl_tran_ctrl", 335544986}, {"no_trusted_spb", 335544987}, + {"package_name", 335544988}, + {"cannot_make_not_null", 335544989}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, @@ -746,7 +748,21 @@ static const struct { {"upd_ins_with_complex_view", 336003101}, {"dsql_incompatible_trigger_type", 336003102}, {"dsql_db_trigger_type_cant_change", 336003103}, + {"dyn_filter_not_found", 336068645}, + {"dyn_func_not_found", 336068649}, + {"dyn_index_not_found", 336068656}, + {"dyn_view_not_found", 336068662}, + {"dyn_domain_not_found", 336068697}, + {"dyn_cant_modify_auto_trig", 336068717}, {"dyn_dup_table", 336068740}, + {"dyn_proc_not_found", 336068748}, + {"dyn_exception_not_found", 336068752}, + {"dyn_proc_param_not_found", 336068754}, + {"dyn_trig_not_found", 336068755}, + {"dyn_charset_not_found", 336068759}, + {"dyn_collation_not_found", 336068760}, + {"dyn_role_not_found", 336068763}, + {"dyn_name_longer", 336068767}, {"dyn_column_does_not_exist", 336068784}, {"dyn_role_does_not_exist", 336068796}, {"dyn_no_grant_admin_opt", 336068797}, @@ -765,9 +781,11 @@ static const struct { {"dyn_invalid_dtype_conversion", 336068817}, {"dyn_dtype_conv_invalid", 336068818}, {"dyn_zero_len_id", 336068820}, + {"dyn_gen_not_found", 336068822}, {"max_coll_per_charset", 336068829}, {"invalid_coll_attr", 336068830}, {"dyn_wrong_gtt_scope", 336068840}, + {"dyn_table_not_found", 336068849}, {"dyn_scale_too_big", 336068852}, {"dyn_precision_too_small", 336068853}, {"dyn_miss_priv_warning", 336068855}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 879bfe3d39..c3afe22392 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -723,6 +723,8 @@ const ISC_STATUS isc_instance_conflict = 335544984L; const ISC_STATUS isc_out_of_temp_space = 335544985L; const ISC_STATUS isc_eds_expl_tran_ctrl = 335544986L; const ISC_STATUS isc_no_trusted_spb = 335544987L; +const ISC_STATUS isc_package_name = 335544988L; +const ISC_STATUS isc_cannot_make_not_null = 335544989L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -780,7 +782,21 @@ const ISC_STATUS isc_upd_ins_doesnt_match_matching = 336003100L; const ISC_STATUS isc_upd_ins_with_complex_view = 336003101L; const ISC_STATUS isc_dsql_incompatible_trigger_type = 336003102L; const ISC_STATUS isc_dsql_db_trigger_type_cant_change = 336003103L; +const ISC_STATUS isc_dyn_filter_not_found = 336068645L; +const ISC_STATUS isc_dyn_func_not_found = 336068649L; +const ISC_STATUS isc_dyn_index_not_found = 336068656L; +const ISC_STATUS isc_dyn_view_not_found = 336068662L; +const ISC_STATUS isc_dyn_domain_not_found = 336068697L; +const ISC_STATUS isc_dyn_cant_modify_auto_trig = 336068717L; const ISC_STATUS isc_dyn_dup_table = 336068740L; +const ISC_STATUS isc_dyn_proc_not_found = 336068748L; +const ISC_STATUS isc_dyn_exception_not_found = 336068752L; +const ISC_STATUS isc_dyn_proc_param_not_found = 336068754L; +const ISC_STATUS isc_dyn_trig_not_found = 336068755L; +const ISC_STATUS isc_dyn_charset_not_found = 336068759L; +const ISC_STATUS isc_dyn_collation_not_found = 336068760L; +const ISC_STATUS isc_dyn_role_not_found = 336068763L; +const ISC_STATUS isc_dyn_name_longer = 336068767L; const ISC_STATUS isc_dyn_column_does_not_exist = 336068784L; const ISC_STATUS isc_dyn_role_does_not_exist = 336068796L; const ISC_STATUS isc_dyn_no_grant_admin_opt = 336068797L; @@ -799,9 +815,11 @@ const ISC_STATUS isc_dyn_char_fld_too_small = 336068816L; const ISC_STATUS isc_dyn_invalid_dtype_conversion = 336068817L; const ISC_STATUS isc_dyn_dtype_conv_invalid = 336068818L; const ISC_STATUS isc_dyn_zero_len_id = 336068820L; +const ISC_STATUS isc_dyn_gen_not_found = 336068822L; const ISC_STATUS isc_max_coll_per_charset = 336068829L; const ISC_STATUS isc_invalid_coll_attr = 336068830L; const ISC_STATUS isc_dyn_wrong_gtt_scope = 336068840L; +const ISC_STATUS isc_dyn_table_not_found = 336068849L; const ISC_STATUS isc_dyn_scale_too_big = 336068852L; const ISC_STATUS isc_dyn_precision_too_small = 336068853L; const ISC_STATUS isc_dyn_miss_priv_warning = 336068855L; @@ -1016,7 +1034,7 @@ const ISC_STATUS isc_fbsvcmgr_fp_open = 336986160L; const ISC_STATUS isc_fbsvcmgr_fp_read = 336986161L; const ISC_STATUS isc_fbsvcmgr_fp_empty = 336986162L; const ISC_STATUS isc_utl_trusted_switch = 337051649L; -const ISC_STATUS isc_err_max = 960; +const ISC_STATUS isc_err_max = 978; #else /* c definitions */ @@ -1709,6 +1727,8 @@ const ISC_STATUS isc_err_max = 960; #define isc_out_of_temp_space 335544985L #define isc_eds_expl_tran_ctrl 335544986L #define isc_no_trusted_spb 335544987L +#define isc_package_name 335544988L +#define isc_cannot_make_not_null 335544989L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -1766,7 +1786,21 @@ const ISC_STATUS isc_err_max = 960; #define isc_upd_ins_with_complex_view 336003101L #define isc_dsql_incompatible_trigger_type 336003102L #define isc_dsql_db_trigger_type_cant_change 336003103L +#define isc_dyn_filter_not_found 336068645L +#define isc_dyn_func_not_found 336068649L +#define isc_dyn_index_not_found 336068656L +#define isc_dyn_view_not_found 336068662L +#define isc_dyn_domain_not_found 336068697L +#define isc_dyn_cant_modify_auto_trig 336068717L #define isc_dyn_dup_table 336068740L +#define isc_dyn_proc_not_found 336068748L +#define isc_dyn_exception_not_found 336068752L +#define isc_dyn_proc_param_not_found 336068754L +#define isc_dyn_trig_not_found 336068755L +#define isc_dyn_charset_not_found 336068759L +#define isc_dyn_collation_not_found 336068760L +#define isc_dyn_role_not_found 336068763L +#define isc_dyn_name_longer 336068767L #define isc_dyn_column_does_not_exist 336068784L #define isc_dyn_role_does_not_exist 336068796L #define isc_dyn_no_grant_admin_opt 336068797L @@ -1785,9 +1819,11 @@ const ISC_STATUS isc_err_max = 960; #define isc_dyn_invalid_dtype_conversion 336068817L #define isc_dyn_dtype_conv_invalid 336068818L #define isc_dyn_zero_len_id 336068820L +#define isc_dyn_gen_not_found 336068822L #define isc_max_coll_per_charset 336068829L #define isc_invalid_coll_attr 336068830L #define isc_dyn_wrong_gtt_scope 336068840L +#define isc_dyn_table_not_found 336068849L #define isc_dyn_scale_too_big 336068852L #define isc_dyn_precision_too_small 336068853L #define isc_dyn_miss_priv_warning 336068855L @@ -2002,7 +2038,7 @@ const ISC_STATUS isc_err_max = 960; #define isc_fbsvcmgr_fp_read 336986161L #define isc_fbsvcmgr_fp_empty 336986162L #define isc_utl_trusted_switch 337051649L -#define isc_err_max 960 +#define isc_err_max 978 #endif diff --git a/src/include/gen/ids.h b/src/include/gen/ids.h index a165d0e860..1809ee03ee 100644 --- a/src/include/gen/ids.h +++ b/src/include/gen/ids.h @@ -171,6 +171,8 @@ const USHORT f_trg_flags = 9; const USHORT f_trg_valid_blr = 10; const USHORT f_trg_debug_info = 11; + const USHORT f_trg_engine_name = 12; + const USHORT f_trg_entry = 13; const USHORT f_dpd_name = 0; @@ -178,6 +180,7 @@ const USHORT f_dpd_f_name = 2; const USHORT f_dpd_type = 3; const USHORT f_dpd_o_type = 4; + const USHORT f_dpd_pkg_name = 5; const USHORT f_fun_name = 0; @@ -188,6 +191,10 @@ const USHORT f_fun_entry = 5; const USHORT f_fun_ret_arg = 6; const USHORT f_fun_sys_flag = 7; + const USHORT f_fun_engine_name = 8; + const USHORT f_fun_pkg_name = 9; + const USHORT f_fun_private_flag = 10; + const USHORT f_fun_source = 11; const USHORT f_arg_fun_name = 0; @@ -200,6 +207,7 @@ const USHORT f_arg_charset_id = 7; const USHORT f_arg_precision = 8; const USHORT f_arg_char_length = 9; + const USHORT f_arg_pkg_name = 10; const USHORT f_flt_name = 0; @@ -285,6 +293,10 @@ const USHORT f_prc_type = 11; const USHORT f_prc_valid_blr = 12; const USHORT f_prc_debug_info = 13; + const USHORT f_prc_engine_name = 14; + const USHORT f_prc_entry = 15; + const USHORT f_prc_pkg_name = 16; + const USHORT f_prc_private_flag = 17; const USHORT f_prm_name = 0; @@ -301,6 +313,7 @@ const USHORT f_prm_mech = 11; const USHORT f_prm_fname = 12; const USHORT f_prm_rname = 13; + const USHORT f_prm_pkg_name = 14; const USHORT f_cs_cs_name = 0; @@ -419,6 +432,7 @@ const USHORT f_mon_call_src_line = 6; const USHORT f_mon_call_src_column = 7; const USHORT f_mon_call_stat_id = 8; + const USHORT f_mon_call_pkg_name = 9; const USHORT f_mon_io_stat_id = 0; @@ -455,3 +469,15 @@ const USHORT f_mon_mem_max_alloc = 5; +// Continue persistent tables + + + const USHORT f_pkg_name = 0; + const USHORT f_pkg_header_source = 1; + const USHORT f_pkg_body_source = 2; + const USHORT f_pkg_class = 3; + const USHORT f_pkg_owner = 4; + const USHORT f_pkg_sys_flag = 5; + const USHORT f_pkg_desc = 6; + + diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 1a3db32e31..593ecc193e 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -252,7 +252,7 @@ static const struct { {335544548, "Attempt to define a second PRIMARY KEY for the same table"}, /* 228, primary_key_exists */ {335544549, "cannot modify or erase a system trigger"}, /* 229, systrig_update */ {335544550, "only the owner of a table may reassign ownership"}, /* 230, not_rel_owner */ - {335544551, "could not find table/procedure for GRANT"}, /* 231, grant_obj_notfound */ + {335544551, "could not find table/procedure/package for GRANT"}, /* 231, grant_obj_notfound */ {335544552, "could not find column for GRANT"}, /* 232, grant_fld_notfound */ {335544553, "user does not have GRANT privileges for operation"}, /* 233, grant_nopriv */ {335544554, "table/procedure has non-SQL security class defined"}, /* 234, nonsql_security_rel */ @@ -523,11 +523,11 @@ static const struct { {335544819, "File exceeded maximum size of 2GB. Add another database file or use a 64 bit I/O version of Firebird."}, /* 499, io_32bit_exceeded_err */ {335544820, "Unable to find savepoint with name @1 in transaction context"}, /* 500, invalid_savepoint */ {335544821, "Invalid column position used in the @1 clause"}, /* 501, dsql_column_pos_err */ - {335544822, "Cannot use an aggregate function in a WHERE clause, use HAVING instead"}, /* 502, dsql_agg_where_err */ - {335544823, "Cannot use an aggregate function in a GROUP BY clause"}, /* 503, dsql_agg_group_err */ + {335544822, "Cannot use an aggregate or window function in a WHERE clause, use HAVING (for aggregate only) instead"}, /* 502, dsql_agg_where_err */ + {335544823, "Cannot use an aggregate or window function in a GROUP BY clause"}, /* 503, dsql_agg_group_err */ {335544824, "Invalid expression in the @1 (not contained in either an aggregate function or the GROUP BY clause)"}, /* 504, dsql_agg_column_err */ {335544825, "Invalid expression in the @1 (neither an aggregate function nor a part of the GROUP BY clause)"}, /* 505, dsql_agg_having_err */ - {335544826, "Nested aggregate functions are not allowed"}, /* 506, dsql_agg_nested_err */ + {335544826, "Nested aggregate and window functions are not allowed"}, /* 506, dsql_agg_nested_err */ {335544827, "Invalid argument in EXECUTE STATEMENT - cannot convert to string"}, /* 507, exec_sql_invalid_arg */ {335544828, "Wrong request type in EXECUTE STATEMENT '@1'"}, /* 508, exec_sql_invalid_req */ {335544829, "Variable type (position @1) in EXECUTE STATEMENT '@2' INTO does not match returned column type"}, /* 509, exec_sql_invalid_var */ @@ -692,298 +692,316 @@ Data source : @4"}, /* 606, eds_statement */ {335544985, "No free space found in temporary directories"}, /* 665, out_of_temp_space */ {335544986, "Explicit transaction control is not allowed"}, /* 666, eds_expl_tran_ctrl */ {335544987, "Use of TRUSTED switches in spb_command_line is prohibited"}, /* 667, no_trusted_spb */ - {335740929, "data base file name (@1) already given"}, /* 668, gfix_db_name */ - {335740930, "invalid switch @1"}, /* 669, gfix_invalid_sw */ - {335740932, "incompatible switch combination"}, /* 670, gfix_incmp_sw */ - {335740933, "replay log pathname required"}, /* 671, gfix_replay_req */ - {335740934, "number of page buffers for cache required"}, /* 672, gfix_pgbuf_req */ - {335740935, "numeric value required"}, /* 673, gfix_val_req */ - {335740936, "positive numeric value required"}, /* 674, gfix_pval_req */ - {335740937, "number of transactions per sweep required"}, /* 675, gfix_trn_req */ - {335740940, "\"full\" or \"reserve\" required"}, /* 676, gfix_full_req */ - {335740941, "user name required"}, /* 677, gfix_usrname_req */ - {335740942, "password required"}, /* 678, gfix_pass_req */ - {335740943, "subsystem name"}, /* 679, gfix_subs_name */ - {335740944, "\"wal\" required"}, /* 680, gfix_wal_req */ - {335740945, "number of seconds required"}, /* 681, gfix_sec_req */ - {335740946, "numeric value between 0 and 32767 inclusive required"}, /* 682, gfix_nval_req */ - {335740947, "must specify type of shutdown"}, /* 683, gfix_type_shut */ - {335740948, "please retry, specifying an option"}, /* 684, gfix_retry */ - {335740951, "please retry, giving a database name"}, /* 685, gfix_retry_db */ - {335740991, "internal block exceeds maximum size"}, /* 686, gfix_exceed_max */ - {335740992, "corrupt pool"}, /* 687, gfix_corrupt_pool */ - {335740993, "virtual memory exhausted"}, /* 688, gfix_mem_exhausted */ - {335740994, "bad pool id"}, /* 689, gfix_bad_pool */ - {335740995, "Transaction state @1 not in valid range."}, /* 690, gfix_trn_not_valid */ - {335741012, "unexpected end of input"}, /* 691, gfix_unexp_eoi */ - {335741018, "failed to reconnect to a transaction in database @1"}, /* 692, gfix_recon_fail */ - {335741036, "Transaction description item unknown"}, /* 693, gfix_trn_unknown */ - {335741038, "\"read_only\" or \"read_write\" required"}, /* 694, gfix_mode_req */ - {335741042, "positive or zero numeric value required"}, /* 695, gfix_pzval_req */ - {336003074, "Cannot SELECT RDB$DB_KEY from a stored procedure."}, /* 696, dsql_dbkey_from_non_table */ - {336003075, "Precision 10 to 18 changed from DOUBLE PRECISION in SQL dialect 1 to 64-bit scaled integer in SQL dialect 3"}, /* 697, dsql_transitional_numeric */ - {336003076, "Use of @1 expression that returns different results in dialect 1 and dialect 3"}, /* 698, dsql_dialect_warning_expr */ - {336003077, "Database SQL dialect @1 does not support reference to @2 datatype"}, /* 699, sql_db_dialect_dtype_unsupport */ - {336003079, "DB dialect @1 and client dialect @2 conflict with respect to numeric precision @3."}, /* 700, isc_sql_dialect_conflict_num */ - {336003080, "WARNING: Numeric literal @1 is interpreted as a floating-point"}, /* 701, dsql_warning_number_ambiguous */ - {336003081, "value in SQL dialect 1, but as an exact numeric value in SQL dialect 3."}, /* 702, dsql_warning_number_ambiguous1 */ - {336003082, "WARNING: NUMERIC and DECIMAL fields with precision 10 or greater are stored"}, /* 703, dsql_warn_precision_ambiguous */ - {336003083, "as approximate floating-point values in SQL dialect 1, but as 64-bit"}, /* 704, dsql_warn_precision_ambiguous1 */ - {336003084, "integers in SQL dialect 3."}, /* 705, dsql_warn_precision_ambiguous2 */ - {336003085, "Ambiguous field name between @1 and @2"}, /* 706, dsql_ambiguous_field_name */ - {336003086, "External function should have return position between 1 and @1"}, /* 707, dsql_udf_return_pos_err */ - {336003087, "Label @1 @2 in the current scope"}, /* 708, dsql_invalid_label */ - {336003088, "Datatypes @1are not comparable in expression @2"}, /* 709, dsql_datatypes_not_comparable */ - {336003089, "Empty cursor name is not allowed"}, /* 710, dsql_cursor_invalid */ - {336003090, "Statement already has a cursor @1 assigned"}, /* 711, dsql_cursor_redefined */ - {336003091, "Cursor @1 is not found in the current context"}, /* 712, dsql_cursor_not_found */ - {336003092, "Cursor @1 already exists in the current context"}, /* 713, dsql_cursor_exists */ - {336003093, "Relation @1 is ambiguous in cursor @2"}, /* 714, dsql_cursor_rel_ambiguous */ - {336003094, "Relation @1 is not found in cursor @2"}, /* 715, dsql_cursor_rel_not_found */ - {336003095, "Cursor is not open"}, /* 716, dsql_cursor_not_open */ - {336003096, "Data type @1 is not supported for EXTERNAL TABLES. Relation '@2', field '@3'"}, /* 717, dsql_type_not_supp_ext_tab */ - {336003097, "Feature not supported on ODS version older than @1.@2"}, /* 718, dsql_feature_not_supported_ods */ - {336003098, "Primary key required on table @1"}, /* 719, primary_key_required */ - {336003099, "UPDATE OR INSERT field list does not match primary key of table @1"}, /* 720, upd_ins_doesnt_match_pk */ - {336003100, "UPDATE OR INSERT field list does not match MATCHING clause"}, /* 721, upd_ins_doesnt_match_matching */ - {336003101, "UPDATE OR INSERT without MATCHING could not be used with views based on more than one table"}, /* 722, upd_ins_with_complex_view */ - {336003102, "Incompatible trigger type"}, /* 723, dsql_incompatible_trigger_type */ - {336003103, "Database trigger type can't be changed"}, /* 724, dsql_db_trigger_type_cant_change */ - {336068740, "Table @1 already exists"}, /* 725, dyn_dup_table */ - {336068784, "column @1 does not exist in table/view @2"}, /* 726, dyn_column_does_not_exist */ - {336068796, "SQL role @1 does not exist"}, /* 727, dyn_role_does_not_exist */ - {336068797, "user @1 has no grant admin option on SQL role @2"}, /* 728, dyn_no_grant_admin_opt */ - {336068798, "user @1 is not a member of SQL role @2"}, /* 729, dyn_user_not_role_member */ - {336068799, "@1 is not the owner of SQL role @2"}, /* 730, dyn_delete_role_failed */ - {336068800, "@1 is a SQL role and not a user"}, /* 731, dyn_grant_role_to_user */ - {336068801, "user name @1 could not be used for SQL role"}, /* 732, dyn_inv_sql_role_name */ - {336068802, "SQL role @1 already exists"}, /* 733, dyn_dup_sql_role */ - {336068803, "keyword @1 can not be used as a SQL role name"}, /* 734, dyn_kywd_spec_for_role */ - {336068804, "SQL roles are not supported in on older versions of the database. A backup and restore of the database is required."}, /* 735, dyn_roles_not_supported */ - {336068812, "Cannot rename domain @1 to @2. A domain with that name already exists."}, /* 736, dyn_domain_name_exists */ - {336068813, "Cannot rename column @1 to @2. A column with that name already exists in table @3."}, /* 737, dyn_field_name_exists */ - {336068814, "Column @1 from table @2 is referenced in @3"}, /* 738, dyn_dependency_exists */ - {336068815, "Cannot change datatype for column @1. Changing datatype is not supported for BLOB or ARRAY columns."}, /* 739, dyn_dtype_invalid */ - {336068816, "New size specified for column @1 must be at least @2 characters."}, /* 740, dyn_char_fld_too_small */ - {336068817, "Cannot change datatype for @1. Conversion from base type @2 to @3 is not supported."}, /* 741, dyn_invalid_dtype_conversion */ - {336068818, "Cannot change datatype for column @1 from a character type to a non-character type."}, /* 742, dyn_dtype_conv_invalid */ - {336068820, "Zero length identifiers are not allowed"}, /* 743, dyn_zero_len_id */ - {336068829, "Maximum number of collations per character set exceeded"}, /* 744, max_coll_per_charset */ - {336068830, "Invalid collation attributes"}, /* 745, invalid_coll_attr */ - {336068840, "@1 cannot reference @2"}, /* 746, dyn_wrong_gtt_scope */ - {336068852, "New scale specified for column @1 must be at most @2."}, /* 747, dyn_scale_too_big */ - {336068853, "New precision specified for column @1 must be at least @2."}, /* 748, dyn_precision_too_small */ - {336068855, "Warning: @1 on @2 is not granted to @3."}, /* 749, dyn_miss_priv_warning */ - {336068856, "Feature '@1' is not supported in ODS @2.@3"}, /* 750, dyn_ods_not_supp_feature */ - {336068857, "Cannot add or remove COMPUTED from column @1"}, /* 751, dyn_cannot_addrem_computed */ - {336068858, "Password should not be empty string"}, /* 752, dyn_no_empty_pw */ - {336068859, "Index @1 already exists"}, /* 753, dyn_dup_index */ - {336330753, "found unknown switch"}, /* 754, gbak_unknown_switch */ - {336330754, "page size parameter missing"}, /* 755, gbak_page_size_missing */ - {336330755, "Page size specified (@1) greater than limit (16384 bytes)"}, /* 756, gbak_page_size_toobig */ - {336330756, "redirect location for output is not specified"}, /* 757, gbak_redir_ouput_missing */ - {336330757, "conflicting switches for backup/restore"}, /* 758, gbak_switches_conflict */ - {336330758, "device type @1 not known"}, /* 759, gbak_unknown_device */ - {336330759, "protection is not there yet"}, /* 760, gbak_no_protection */ - {336330760, "page size is allowed only on restore or create"}, /* 761, gbak_page_size_not_allowed */ - {336330761, "multiple sources or destinations specified"}, /* 762, gbak_multi_source_dest */ - {336330762, "requires both input and output filenames"}, /* 763, gbak_filename_missing */ - {336330763, "input and output have the same name. Disallowed."}, /* 764, gbak_dup_inout_names */ - {336330764, "expected page size, encountered \"@1\""}, /* 765, gbak_inv_page_size */ - {336330765, "REPLACE specified, but the first file @1 is a database"}, /* 766, gbak_db_specified */ - {336330766, "database @1 already exists. To replace it, use the -REP switch"}, /* 767, gbak_db_exists */ - {336330767, "device type not specified"}, /* 768, gbak_unk_device */ - {336330772, "gds_$blob_info failed"}, /* 769, gbak_blob_info_failed */ - {336330773, "do not understand BLOB INFO item @1"}, /* 770, gbak_unk_blob_item */ - {336330774, "gds_$get_segment failed"}, /* 771, gbak_get_seg_failed */ - {336330775, "gds_$close_blob failed"}, /* 772, gbak_close_blob_failed */ - {336330776, "gds_$open_blob failed"}, /* 773, gbak_open_blob_failed */ - {336330777, "Failed in put_blr_gen_id"}, /* 774, gbak_put_blr_gen_id_failed */ - {336330778, "data type @1 not understood"}, /* 775, gbak_unk_type */ - {336330779, "gds_$compile_request failed"}, /* 776, gbak_comp_req_failed */ - {336330780, "gds_$start_request failed"}, /* 777, gbak_start_req_failed */ - {336330781, "gds_$receive failed"}, /* 778, gbak_rec_failed */ - {336330782, "gds_$release_request failed"}, /* 779, gbak_rel_req_failed */ - {336330783, "gds_$database_info failed"}, /* 780, gbak_db_info_failed */ - {336330784, "Expected database description record"}, /* 781, gbak_no_db_desc */ - {336330785, "failed to create database @1"}, /* 782, gbak_db_create_failed */ - {336330786, "RESTORE: decompression length error"}, /* 783, gbak_decomp_len_error */ - {336330787, "cannot find table @1"}, /* 784, gbak_tbl_missing */ - {336330788, "Cannot find column for BLOB"}, /* 785, gbak_blob_col_missing */ - {336330789, "gds_$create_blob failed"}, /* 786, gbak_create_blob_failed */ - {336330790, "gds_$put_segment failed"}, /* 787, gbak_put_seg_failed */ - {336330791, "expected record length"}, /* 788, gbak_rec_len_exp */ - {336330792, "wrong length record, expected @1 encountered @2"}, /* 789, gbak_inv_rec_len */ - {336330793, "expected data attribute"}, /* 790, gbak_exp_data_type */ - {336330794, "Failed in store_blr_gen_id"}, /* 791, gbak_gen_id_failed */ - {336330795, "do not recognize record type @1"}, /* 792, gbak_unk_rec_type */ - {336330796, "Expected backup version 1..9. Found @1"}, /* 793, gbak_inv_bkup_ver */ - {336330797, "expected backup description record"}, /* 794, gbak_missing_bkup_desc */ - {336330798, "string truncated"}, /* 795, gbak_string_trunc */ - {336330799, "warning -- record could not be restored"}, /* 796, gbak_cant_rest_record */ - {336330800, "gds_$send failed"}, /* 797, gbak_send_failed */ - {336330801, "no table name for data"}, /* 798, gbak_no_tbl_name */ - {336330802, "unexpected end of file on backup file"}, /* 799, gbak_unexp_eof */ - {336330803, "database format @1 is too old to restore to"}, /* 800, gbak_db_format_too_old */ - {336330804, "array dimension for column @1 is invalid"}, /* 801, gbak_inv_array_dim */ - {336330807, "Expected XDR record length"}, /* 802, gbak_xdr_len_expected */ - {336330817, "cannot open backup file @1"}, /* 803, gbak_open_bkup_error */ - {336330818, "cannot open status and error output file @1"}, /* 804, gbak_open_error */ - {336330934, "blocking factor parameter missing"}, /* 805, gbak_missing_block_fac */ - {336330935, "expected blocking factor, encountered \"@1\""}, /* 806, gbak_inv_block_fac */ - {336330936, "a blocking factor may not be used in conjunction with device CT"}, /* 807, gbak_block_fac_specified */ - {336330940, "user name parameter missing"}, /* 808, gbak_missing_username */ - {336330941, "password parameter missing"}, /* 809, gbak_missing_password */ - {336330952, " missing parameter for the number of bytes to be skipped"}, /* 810, gbak_missing_skipped_bytes */ - {336330953, "expected number of bytes to be skipped, encountered \"@1\""}, /* 811, gbak_inv_skipped_bytes */ - {336330965, "character set"}, /* 812, gbak_err_restore_charset */ - {336330967, "collation"}, /* 813, gbak_err_restore_collation */ - {336330972, "Unexpected I/O error while reading from backup file"}, /* 814, gbak_read_error */ - {336330973, "Unexpected I/O error while writing to backup file"}, /* 815, gbak_write_error */ - {336330985, "could not drop database @1 (database might be in use)"}, /* 816, gbak_db_in_use */ - {336330990, "System memory exhausted"}, /* 817, gbak_sysmemex */ - {336331002, "SQL role"}, /* 818, gbak_restore_role_failed */ - {336331005, "SQL role parameter missing"}, /* 819, gbak_role_op_missing */ - {336331010, "page buffers parameter missing"}, /* 820, gbak_page_buffers_missing */ - {336331011, "expected page buffers, encountered \"@1\""}, /* 821, gbak_page_buffers_wrong_param */ - {336331012, "page buffers is allowed only on restore or create"}, /* 822, gbak_page_buffers_restore */ - {336331014, "size specification either missing or incorrect for file @1"}, /* 823, gbak_inv_size */ - {336331015, "file @1 out of sequence"}, /* 824, gbak_file_outof_sequence */ - {336331016, "can't join -- one of the files missing"}, /* 825, gbak_join_file_missing */ - {336331017, " standard input is not supported when using join operation"}, /* 826, gbak_stdin_not_supptd */ - {336331018, "standard output is not supported when using split operation"}, /* 827, gbak_stdout_not_supptd */ - {336331019, "backup file @1 might be corrupt"}, /* 828, gbak_bkup_corrupt */ - {336331020, "database file specification missing"}, /* 829, gbak_unk_db_file_spec */ - {336331021, "can't write a header record to file @1"}, /* 830, gbak_hdr_write_failed */ - {336331022, "free disk space exhausted"}, /* 831, gbak_disk_space_ex */ - {336331023, "file size given (@1) is less than minimum allowed (@2)"}, /* 832, gbak_size_lt_min */ - {336331025, "service name parameter missing"}, /* 833, gbak_svc_name_missing */ - {336331026, "Cannot restore over current database, must be SYSDBA or owner of the existing database."}, /* 834, gbak_not_ownr */ - {336331031, "\"read_only\" or \"read_write\" required"}, /* 835, gbak_mode_req */ - {336331033, "just data ignore all constraints etc."}, /* 836, gbak_just_data */ - {336331034, "restoring data only ignoring foreign key, unique, not null & other constraints"}, /* 837, gbak_data_only */ - {336397205, "ODS versions before ODS@1 are not supported"}, /* 838, dsql_too_old_ods */ - {336397206, "Table @1 does not exist"}, /* 839, dsql_table_not_found */ - {336397207, "View @1 does not exist"}, /* 840, dsql_view_not_found */ - {336397208, "At line @1, column @2"}, /* 841, dsql_line_col_error */ - {336397209, "At unknown line and column"}, /* 842, dsql_unknown_pos */ - {336397210, "Column @1 cannot be repeated in @2 statement"}, /* 843, dsql_no_dup_name */ - {336397211, "Too many values (more than @1) in member list to match against"}, /* 844, dsql_too_many_values */ - {336397212, "Array and BLOB data types not allowed in computed field"}, /* 845, dsql_no_array_computed */ - {336397213, "Implicit domain name @1 not allowed in user created domain"}, /* 846, dsql_implicit_domain_name */ - {336397214, "scalar operator used on field @1 which is not an array"}, /* 847, dsql_only_can_subscript_array */ - {336397215, "cannot sort on more than 255 items"}, /* 848, dsql_max_sort_items */ - {336397216, "cannot group on more than 255 items"}, /* 849, dsql_max_group_items */ - {336397217, "Cannot include the same field (@1.@2) twice in the ORDER BY clause with conflicting sorting options"}, /* 850, dsql_conflicting_sort_field */ - {336397218, "column list from derived table @1 has more columns than the number of items in its SELECT statement"}, /* 851, dsql_derived_table_more_columns */ - {336397219, "column list from derived table @1 has less columns than the number of items in its SELECT statement"}, /* 852, dsql_derived_table_less_columns */ - {336397220, "no column name specified for column number @1 in derived table @2"}, /* 853, dsql_derived_field_unnamed */ - {336397221, "column @1 was specified multiple times for derived table @2"}, /* 854, dsql_derived_field_dup_name */ - {336397222, "Internal dsql error: alias type expected by pass1_expand_select_node"}, /* 855, dsql_derived_alias_select */ - {336397223, "Internal dsql error: alias type expected by pass1_field"}, /* 856, dsql_derived_alias_field */ - {336397224, "Internal dsql error: column position out of range in pass1_union_auto_cast"}, /* 857, dsql_auto_field_bad_pos */ - {336397225, "Recursive CTE member (@1) can refer itself only in FROM clause"}, /* 858, dsql_cte_wrong_reference */ - {336397226, "CTE '@1' has cyclic dependencies"}, /* 859, dsql_cte_cycle */ - {336397227, "Recursive member of CTE can't be member of an outer join"}, /* 860, dsql_cte_outer_join */ - {336397228, "Recursive member of CTE can't reference itself more than once"}, /* 861, dsql_cte_mult_references */ - {336397229, "Recursive CTE (@1) must be an UNION"}, /* 862, dsql_cte_not_a_union */ - {336397230, "CTE '@1' defined non-recursive member after recursive"}, /* 863, dsql_cte_nonrecurs_after_recurs */ - {336397231, "Recursive member of CTE '@1' has @2 clause"}, /* 864, dsql_cte_wrong_clause */ - {336397232, "Recursive members of CTE (@1) must be linked with another members via UNION ALL"}, /* 865, dsql_cte_union_all */ - {336397233, "Non-recursive member is missing in CTE '@1'"}, /* 866, dsql_cte_miss_nonrecursive */ - {336397234, "WITH clause can't be nested"}, /* 867, dsql_cte_nested_with */ - {336397235, "column @1 appears more than once in USING clause"}, /* 868, dsql_col_more_than_once_using */ - {336397236, "feature is not supported in dialect @1"}, /* 869, dsql_unsupp_feature_dialect */ - {336397237, "CTE \"@1\" is not used in query"}, /* 870, dsql_cte_not_used */ - {336397238, "column @1 appears more than once in ALTER VIEW"}, /* 871, dsql_col_more_than_once_view */ - {336397239, "@1 is not supported inside IN AUTONOMOUS TRANSACTION block"}, /* 872, dsql_unsupported_in_auto_trans */ - {336397240, "Unknown node type @1 in dsql/GEN_expr"}, /* 873, dsql_eval_unknode */ - {336397241, "Argument for @1 in dialect 1 must be string or numeric"}, /* 874, dsql_agg_wrongarg */ - {336397242, "Argument for @1 in dialect 3 must be numeric"}, /* 875, dsql_agg2_wrongarg */ - {336397243, "Strings cannot be added to or subtracted from DATE or TIME types"}, /* 876, dsql_nodateortime_pm_string */ - {336397244, "Invalid data type for subtraction involving DATE, TIME or TIMESTAMP types"}, /* 877, dsql_invalid_datetime_subtract */ - {336397245, "Adding two DATE values or two TIME values is not allowed"}, /* 878, dsql_invalid_dateortime_add */ - {336397246, "DATE value cannot be subtracted from the provided data type"}, /* 879, dsql_invalid_type_minus_date */ - {336397247, "Strings cannot be added or subtracted in dialect 3"}, /* 880, dsql_nostring_addsub_dial3 */ - {336397248, "Invalid data type for addition or subtraction in dialect 3"}, /* 881, dsql_invalid_type_addsub_dial3 */ - {336397249, "Invalid data type for multiplication in dialect 1"}, /* 882, dsql_invalid_type_multip_dial1 */ - {336397250, "Strings cannot be multiplied in dialect 3"}, /* 883, dsql_nostring_multip_dial3 */ - {336397251, "Invalid data type for multiplication in dialect 3"}, /* 884, dsql_invalid_type_multip_dial3 */ - {336397252, "Division in dialect 1 must be between numeric data types"}, /* 885, dsql_mustuse_numeric_div_dial1 */ - {336397253, "Strings cannot be divided in dialect 3"}, /* 886, dsql_nostring_div_dial3 */ - {336397254, "Invalid data type for division in dialect 3"}, /* 887, dsql_invalid_type_div_dial3 */ - {336397255, "Strings cannot be negated (applied the minus operator) in dialect 3"}, /* 888, dsql_nostring_neg_dial3 */ - {336397256, "Invalid data type for negation (minus operator)"}, /* 889, dsql_invalid_type_neg */ - {336397257, "Cannot have more than 255 items in DISTINCT list"}, /* 890, dsql_max_distinct_items */ - {336723983, "unable to open database"}, /* 891, gsec_cant_open_db */ - {336723984, "error in switch specifications"}, /* 892, gsec_switches_error */ - {336723985, "no operation specified"}, /* 893, gsec_no_op_spec */ - {336723986, "no user name specified"}, /* 894, gsec_no_usr_name */ - {336723987, "add record error"}, /* 895, gsec_err_add */ - {336723988, "modify record error"}, /* 896, gsec_err_modify */ - {336723989, "find/modify record error"}, /* 897, gsec_err_find_mod */ - {336723990, "record not found for user: @1"}, /* 898, gsec_err_rec_not_found */ - {336723991, "delete record error"}, /* 899, gsec_err_delete */ - {336723992, "find/delete record error"}, /* 900, gsec_err_find_del */ - {336723996, "find/display record error"}, /* 901, gsec_err_find_disp */ - {336723997, "invalid parameter, no switch defined"}, /* 902, gsec_inv_param */ - {336723998, "operation already specified"}, /* 903, gsec_op_specified */ - {336723999, "password already specified"}, /* 904, gsec_pw_specified */ - {336724000, "uid already specified"}, /* 905, gsec_uid_specified */ - {336724001, "gid already specified"}, /* 906, gsec_gid_specified */ - {336724002, "project already specified"}, /* 907, gsec_proj_specified */ - {336724003, "organization already specified"}, /* 908, gsec_org_specified */ - {336724004, "first name already specified"}, /* 909, gsec_fname_specified */ - {336724005, "middle name already specified"}, /* 910, gsec_mname_specified */ - {336724006, "last name already specified"}, /* 911, gsec_lname_specified */ - {336724008, "invalid switch specified"}, /* 912, gsec_inv_switch */ - {336724009, "ambiguous switch specified"}, /* 913, gsec_amb_switch */ - {336724010, "no operation specified for parameters"}, /* 914, gsec_no_op_specified */ - {336724011, "no parameters allowed for this operation"}, /* 915, gsec_params_not_allowed */ - {336724012, "incompatible switches specified"}, /* 916, gsec_incompat_switch */ - {336724044, "Invalid user name (maximum 31 bytes allowed)"}, /* 917, gsec_inv_username */ - {336724045, "Warning - maximum 8 significant bytes of password used"}, /* 918, gsec_inv_pw_length */ - {336724046, "database already specified"}, /* 919, gsec_db_specified */ - {336724047, "database administrator name already specified"}, /* 920, gsec_db_admin_specified */ - {336724048, "database administrator password already specified"}, /* 921, gsec_db_admin_pw_specified */ - {336724049, "SQL role name already specified"}, /* 922, gsec_sql_role_specified */ - {336789504, "The license file does not exist or could not be opened for read"}, /* 923, license_no_file */ - {336789523, "operation already specified"}, /* 924, license_op_specified */ - {336789524, "no operation specified"}, /* 925, license_op_missing */ - {336789525, "invalid switch"}, /* 926, license_inv_switch */ - {336789526, "invalid switch combination"}, /* 927, license_inv_switch_combo */ - {336789527, "illegal operation/switch combination"}, /* 928, license_inv_op_combo */ - {336789528, "ambiguous switch"}, /* 929, license_amb_switch */ - {336789529, "invalid parameter, no switch specified"}, /* 930, license_inv_parameter */ - {336789530, "switch does not take any parameter"}, /* 931, license_param_specified */ - {336789531, "switch requires a parameter"}, /* 932, license_param_req */ - {336789532, "syntax error in command line"}, /* 933, license_syntx_error */ - {336789534, "The certificate was not added. A duplicate ID exists in the license file."}, /* 934, license_dup_id */ - {336789535, "The certificate was not added. Invalid certificate ID / Key combination."}, /* 935, license_inv_id_key */ - {336789536, "The certificate was not removed. The key does not exist or corresponds to a temporary evaluation license."}, /* 936, license_err_remove */ - {336789537, "An error occurred updating the license file. Operation cancelled."}, /* 937, license_err_update */ - {336789538, "The certificate could not be validated based on the information given. Please recheck the ID and key information."}, /* 938, license_err_convert */ - {336789539, "Operation failed. An unknown error occurred."}, /* 939, license_err_unk */ - {336789540, "Add license operation failed, KEY: @1 ID: @2"}, /* 940, license_svc_err_add */ - {336789541, "Remove license operation failed, KEY: @1"}, /* 941, license_svc_err_remove */ - {336789563, "The evaluation license has already been used on this server. You need to purchase a non-evaluation license."}, /* 942, license_eval_exists */ - {336920577, "found unknown switch"}, /* 943, gstat_unknown_switch */ - {336920578, "please retry, giving a database name"}, /* 944, gstat_retry */ - {336920579, "Wrong ODS version, expected @1, encountered @2"}, /* 945, gstat_wrong_ods */ - {336920580, "Unexpected end of database file."}, /* 946, gstat_unexpected_eof */ - {336920605, "Can't open database file @1"}, /* 947, gstat_open_err */ - {336920606, "Can't read a database page"}, /* 948, gstat_read_err */ - {336920607, "System memory exhausted"}, /* 949, gstat_sysmemex */ - {336986113, "Wrong value for access mode"}, /* 950, fbsvcmgr_bad_am */ - {336986114, "Wrong value for write mode"}, /* 951, fbsvcmgr_bad_wm */ - {336986115, "Wrong value for reserve space"}, /* 952, fbsvcmgr_bad_rs */ - {336986116, "Unknown tag (@1) in info_svr_db_info block after isc_svc_query()"}, /* 953, fbsvcmgr_info_err */ - {336986117, "Unknown tag (@1) in isc_svc_query() results"}, /* 954, fbsvcmgr_query_err */ - {336986118, "Unknown switch \"@1\""}, /* 955, fbsvcmgr_switch_unknown */ - {336986159, "Wrong value for shutdown mode"}, /* 956, fbsvcmgr_bad_sm */ - {336986160, "could not open file @1"}, /* 957, fbsvcmgr_fp_open */ - {336986161, "could not read file @1"}, /* 958, fbsvcmgr_fp_read */ - {336986162, "empty file @1"}, /* 959, fbsvcmgr_fp_empty */ - {337051649, "Switches trusted_svc and trusted_role are not supported from command line"}, /* 960, utl_trusted_switch */ + {335544988, "PACKAGE @1"}, /* 668, package_name */ + {335544989, "Cannot make field @1 NOT NULL because there are NULLs present"}, /* 669, cannot_make_not_null */ + {335740929, "data base file name (@1) already given"}, /* 670, gfix_db_name */ + {335740930, "invalid switch @1"}, /* 671, gfix_invalid_sw */ + {335740932, "incompatible switch combination"}, /* 672, gfix_incmp_sw */ + {335740933, "replay log pathname required"}, /* 673, gfix_replay_req */ + {335740934, "number of page buffers for cache required"}, /* 674, gfix_pgbuf_req */ + {335740935, "numeric value required"}, /* 675, gfix_val_req */ + {335740936, "positive numeric value required"}, /* 676, gfix_pval_req */ + {335740937, "number of transactions per sweep required"}, /* 677, gfix_trn_req */ + {335740940, "\"full\" or \"reserve\" required"}, /* 678, gfix_full_req */ + {335740941, "user name required"}, /* 679, gfix_usrname_req */ + {335740942, "password required"}, /* 680, gfix_pass_req */ + {335740943, "subsystem name"}, /* 681, gfix_subs_name */ + {335740944, "\"wal\" required"}, /* 682, gfix_wal_req */ + {335740945, "number of seconds required"}, /* 683, gfix_sec_req */ + {335740946, "numeric value between 0 and 32767 inclusive required"}, /* 684, gfix_nval_req */ + {335740947, "must specify type of shutdown"}, /* 685, gfix_type_shut */ + {335740948, "please retry, specifying an option"}, /* 686, gfix_retry */ + {335740951, "please retry, giving a database name"}, /* 687, gfix_retry_db */ + {335740991, "internal block exceeds maximum size"}, /* 688, gfix_exceed_max */ + {335740992, "corrupt pool"}, /* 689, gfix_corrupt_pool */ + {335740993, "virtual memory exhausted"}, /* 690, gfix_mem_exhausted */ + {335740994, "bad pool id"}, /* 691, gfix_bad_pool */ + {335740995, "Transaction state @1 not in valid range."}, /* 692, gfix_trn_not_valid */ + {335741012, "unexpected end of input"}, /* 693, gfix_unexp_eoi */ + {335741018, "failed to reconnect to a transaction in database @1"}, /* 694, gfix_recon_fail */ + {335741036, "Transaction description item unknown"}, /* 695, gfix_trn_unknown */ + {335741038, "\"read_only\" or \"read_write\" required"}, /* 696, gfix_mode_req */ + {335741042, "positive or zero numeric value required"}, /* 697, gfix_pzval_req */ + {336003074, "Cannot SELECT RDB$DB_KEY from a stored procedure."}, /* 698, dsql_dbkey_from_non_table */ + {336003075, "Precision 10 to 18 changed from DOUBLE PRECISION in SQL dialect 1 to 64-bit scaled integer in SQL dialect 3"}, /* 699, dsql_transitional_numeric */ + {336003076, "Use of @1 expression that returns different results in dialect 1 and dialect 3"}, /* 700, dsql_dialect_warning_expr */ + {336003077, "Database SQL dialect @1 does not support reference to @2 datatype"}, /* 701, sql_db_dialect_dtype_unsupport */ + {336003079, "DB dialect @1 and client dialect @2 conflict with respect to numeric precision @3."}, /* 702, isc_sql_dialect_conflict_num */ + {336003080, "WARNING: Numeric literal @1 is interpreted as a floating-point"}, /* 703, dsql_warning_number_ambiguous */ + {336003081, "value in SQL dialect 1, but as an exact numeric value in SQL dialect 3."}, /* 704, dsql_warning_number_ambiguous1 */ + {336003082, "WARNING: NUMERIC and DECIMAL fields with precision 10 or greater are stored"}, /* 705, dsql_warn_precision_ambiguous */ + {336003083, "as approximate floating-point values in SQL dialect 1, but as 64-bit"}, /* 706, dsql_warn_precision_ambiguous1 */ + {336003084, "integers in SQL dialect 3."}, /* 707, dsql_warn_precision_ambiguous2 */ + {336003085, "Ambiguous field name between @1 and @2"}, /* 708, dsql_ambiguous_field_name */ + {336003086, "External function should have return position between 1 and @1"}, /* 709, dsql_udf_return_pos_err */ + {336003087, "Label @1 @2 in the current scope"}, /* 710, dsql_invalid_label */ + {336003088, "Datatypes @1are not comparable in expression @2"}, /* 711, dsql_datatypes_not_comparable */ + {336003089, "Empty cursor name is not allowed"}, /* 712, dsql_cursor_invalid */ + {336003090, "Statement already has a cursor @1 assigned"}, /* 713, dsql_cursor_redefined */ + {336003091, "Cursor @1 is not found in the current context"}, /* 714, dsql_cursor_not_found */ + {336003092, "Cursor @1 already exists in the current context"}, /* 715, dsql_cursor_exists */ + {336003093, "Relation @1 is ambiguous in cursor @2"}, /* 716, dsql_cursor_rel_ambiguous */ + {336003094, "Relation @1 is not found in cursor @2"}, /* 717, dsql_cursor_rel_not_found */ + {336003095, "Cursor is not open"}, /* 718, dsql_cursor_not_open */ + {336003096, "Data type @1 is not supported for EXTERNAL TABLES. Relation '@2', field '@3'"}, /* 719, dsql_type_not_supp_ext_tab */ + {336003097, "Feature not supported on ODS version older than @1.@2"}, /* 720, dsql_feature_not_supported_ods */ + {336003098, "Primary key required on table @1"}, /* 721, primary_key_required */ + {336003099, "UPDATE OR INSERT field list does not match primary key of table @1"}, /* 722, upd_ins_doesnt_match_pk */ + {336003100, "UPDATE OR INSERT field list does not match MATCHING clause"}, /* 723, upd_ins_doesnt_match_matching */ + {336003101, "UPDATE OR INSERT without MATCHING could not be used with views based on more than one table"}, /* 724, upd_ins_with_complex_view */ + {336003102, "Incompatible trigger type"}, /* 725, dsql_incompatible_trigger_type */ + {336003103, "Database trigger type can't be changed"}, /* 726, dsql_db_trigger_type_cant_change */ + {336068645, "BLOB Filter @1 not found"}, /* 727, dyn_filter_not_found */ + {336068649, "Function @1 not found"}, /* 728, dyn_func_not_found */ + {336068656, "Index not found"}, /* 729, dyn_index_not_found */ + {336068662, "View @1 not found"}, /* 730, dyn_view_not_found */ + {336068697, "Domain not found"}, /* 731, dyn_domain_not_found */ + {336068717, "Triggers created automatically cannot be modified"}, /* 732, dyn_cant_modify_auto_trig */ + {336068740, "Table @1 already exists"}, /* 733, dyn_dup_table */ + {336068748, "Procedure @1 not found"}, /* 734, dyn_proc_not_found */ + {336068752, "Exception not found"}, /* 735, dyn_exception_not_found */ + {336068754, "Parameter @1 in procedure @2 not found"}, /* 736, dyn_proc_param_not_found */ + {336068755, "Trigger @1 not found"}, /* 737, dyn_trig_not_found */ + {336068759, "Character set @1 not found"}, /* 738, dyn_charset_not_found */ + {336068760, "Collation @1 not found"}, /* 739, dyn_collation_not_found */ + {336068763, "Role @1 not found"}, /* 740, dyn_role_not_found */ + {336068767, "Name longer than database column size"}, /* 741, dyn_name_longer */ + {336068784, "column @1 does not exist in table/view @2"}, /* 742, dyn_column_does_not_exist */ + {336068796, "SQL role @1 does not exist"}, /* 743, dyn_role_does_not_exist */ + {336068797, "user @1 has no grant admin option on SQL role @2"}, /* 744, dyn_no_grant_admin_opt */ + {336068798, "user @1 is not a member of SQL role @2"}, /* 745, dyn_user_not_role_member */ + {336068799, "@1 is not the owner of SQL role @2"}, /* 746, dyn_delete_role_failed */ + {336068800, "@1 is a SQL role and not a user"}, /* 747, dyn_grant_role_to_user */ + {336068801, "user name @1 could not be used for SQL role"}, /* 748, dyn_inv_sql_role_name */ + {336068802, "SQL role @1 already exists"}, /* 749, dyn_dup_sql_role */ + {336068803, "keyword @1 can not be used as a SQL role name"}, /* 750, dyn_kywd_spec_for_role */ + {336068804, "SQL roles are not supported in on older versions of the database. A backup and restore of the database is required."}, /* 751, dyn_roles_not_supported */ + {336068812, "Cannot rename domain @1 to @2. A domain with that name already exists."}, /* 752, dyn_domain_name_exists */ + {336068813, "Cannot rename column @1 to @2. A column with that name already exists in table @3."}, /* 753, dyn_field_name_exists */ + {336068814, "Column @1 from table @2 is referenced in @3"}, /* 754, dyn_dependency_exists */ + {336068815, "Cannot change datatype for column @1. Changing datatype is not supported for BLOB or ARRAY columns."}, /* 755, dyn_dtype_invalid */ + {336068816, "New size specified for column @1 must be at least @2 characters."}, /* 756, dyn_char_fld_too_small */ + {336068817, "Cannot change datatype for @1. Conversion from base type @2 to @3 is not supported."}, /* 757, dyn_invalid_dtype_conversion */ + {336068818, "Cannot change datatype for column @1 from a character type to a non-character type."}, /* 758, dyn_dtype_conv_invalid */ + {336068820, "Zero length identifiers are not allowed"}, /* 759, dyn_zero_len_id */ + {336068822, "Generator @1 not found"}, /* 760, dyn_gen_not_found */ + {336068829, "Maximum number of collations per character set exceeded"}, /* 761, max_coll_per_charset */ + {336068830, "Invalid collation attributes"}, /* 762, invalid_coll_attr */ + {336068840, "@1 cannot reference @2"}, /* 763, dyn_wrong_gtt_scope */ + {336068849, "Table @1 not found"}, /* 764, dyn_table_not_found */ + {336068852, "New scale specified for column @1 must be at most @2."}, /* 765, dyn_scale_too_big */ + {336068853, "New precision specified for column @1 must be at least @2."}, /* 766, dyn_precision_too_small */ + {336068855, "Warning: @1 on @2 is not granted to @3."}, /* 767, dyn_miss_priv_warning */ + {336068856, "Feature '@1' is not supported in ODS @2.@3"}, /* 768, dyn_ods_not_supp_feature */ + {336068857, "Cannot add or remove COMPUTED from column @1"}, /* 769, dyn_cannot_addrem_computed */ + {336068858, "Password should not be empty string"}, /* 770, dyn_no_empty_pw */ + {336068859, "Index @1 already exists"}, /* 771, dyn_dup_index */ + {336330753, "found unknown switch"}, /* 772, gbak_unknown_switch */ + {336330754, "page size parameter missing"}, /* 773, gbak_page_size_missing */ + {336330755, "Page size specified (@1) greater than limit (16384 bytes)"}, /* 774, gbak_page_size_toobig */ + {336330756, "redirect location for output is not specified"}, /* 775, gbak_redir_ouput_missing */ + {336330757, "conflicting switches for backup/restore"}, /* 776, gbak_switches_conflict */ + {336330758, "device type @1 not known"}, /* 777, gbak_unknown_device */ + {336330759, "protection is not there yet"}, /* 778, gbak_no_protection */ + {336330760, "page size is allowed only on restore or create"}, /* 779, gbak_page_size_not_allowed */ + {336330761, "multiple sources or destinations specified"}, /* 780, gbak_multi_source_dest */ + {336330762, "requires both input and output filenames"}, /* 781, gbak_filename_missing */ + {336330763, "input and output have the same name. Disallowed."}, /* 782, gbak_dup_inout_names */ + {336330764, "expected page size, encountered \"@1\""}, /* 783, gbak_inv_page_size */ + {336330765, "REPLACE specified, but the first file @1 is a database"}, /* 784, gbak_db_specified */ + {336330766, "database @1 already exists. To replace it, use the -REP switch"}, /* 785, gbak_db_exists */ + {336330767, "device type not specified"}, /* 786, gbak_unk_device */ + {336330772, "gds_$blob_info failed"}, /* 787, gbak_blob_info_failed */ + {336330773, "do not understand BLOB INFO item @1"}, /* 788, gbak_unk_blob_item */ + {336330774, "gds_$get_segment failed"}, /* 789, gbak_get_seg_failed */ + {336330775, "gds_$close_blob failed"}, /* 790, gbak_close_blob_failed */ + {336330776, "gds_$open_blob failed"}, /* 791, gbak_open_blob_failed */ + {336330777, "Failed in put_blr_gen_id"}, /* 792, gbak_put_blr_gen_id_failed */ + {336330778, "data type @1 not understood"}, /* 793, gbak_unk_type */ + {336330779, "gds_$compile_request failed"}, /* 794, gbak_comp_req_failed */ + {336330780, "gds_$start_request failed"}, /* 795, gbak_start_req_failed */ + {336330781, "gds_$receive failed"}, /* 796, gbak_rec_failed */ + {336330782, "gds_$release_request failed"}, /* 797, gbak_rel_req_failed */ + {336330783, "gds_$database_info failed"}, /* 798, gbak_db_info_failed */ + {336330784, "Expected database description record"}, /* 799, gbak_no_db_desc */ + {336330785, "failed to create database @1"}, /* 800, gbak_db_create_failed */ + {336330786, "RESTORE: decompression length error"}, /* 801, gbak_decomp_len_error */ + {336330787, "cannot find table @1"}, /* 802, gbak_tbl_missing */ + {336330788, "Cannot find column for BLOB"}, /* 803, gbak_blob_col_missing */ + {336330789, "gds_$create_blob failed"}, /* 804, gbak_create_blob_failed */ + {336330790, "gds_$put_segment failed"}, /* 805, gbak_put_seg_failed */ + {336330791, "expected record length"}, /* 806, gbak_rec_len_exp */ + {336330792, "wrong length record, expected @1 encountered @2"}, /* 807, gbak_inv_rec_len */ + {336330793, "expected data attribute"}, /* 808, gbak_exp_data_type */ + {336330794, "Failed in store_blr_gen_id"}, /* 809, gbak_gen_id_failed */ + {336330795, "do not recognize record type @1"}, /* 810, gbak_unk_rec_type */ + {336330796, "Expected backup version 1..9. Found @1"}, /* 811, gbak_inv_bkup_ver */ + {336330797, "expected backup description record"}, /* 812, gbak_missing_bkup_desc */ + {336330798, "string truncated"}, /* 813, gbak_string_trunc */ + {336330799, "warning -- record could not be restored"}, /* 814, gbak_cant_rest_record */ + {336330800, "gds_$send failed"}, /* 815, gbak_send_failed */ + {336330801, "no table name for data"}, /* 816, gbak_no_tbl_name */ + {336330802, "unexpected end of file on backup file"}, /* 817, gbak_unexp_eof */ + {336330803, "database format @1 is too old to restore to"}, /* 818, gbak_db_format_too_old */ + {336330804, "array dimension for column @1 is invalid"}, /* 819, gbak_inv_array_dim */ + {336330807, "Expected XDR record length"}, /* 820, gbak_xdr_len_expected */ + {336330817, "cannot open backup file @1"}, /* 821, gbak_open_bkup_error */ + {336330818, "cannot open status and error output file @1"}, /* 822, gbak_open_error */ + {336330934, "blocking factor parameter missing"}, /* 823, gbak_missing_block_fac */ + {336330935, "expected blocking factor, encountered \"@1\""}, /* 824, gbak_inv_block_fac */ + {336330936, "a blocking factor may not be used in conjunction with device CT"}, /* 825, gbak_block_fac_specified */ + {336330940, "user name parameter missing"}, /* 826, gbak_missing_username */ + {336330941, "password parameter missing"}, /* 827, gbak_missing_password */ + {336330952, " missing parameter for the number of bytes to be skipped"}, /* 828, gbak_missing_skipped_bytes */ + {336330953, "expected number of bytes to be skipped, encountered \"@1\""}, /* 829, gbak_inv_skipped_bytes */ + {336330965, "character set"}, /* 830, gbak_err_restore_charset */ + {336330967, "collation"}, /* 831, gbak_err_restore_collation */ + {336330972, "Unexpected I/O error while reading from backup file"}, /* 832, gbak_read_error */ + {336330973, "Unexpected I/O error while writing to backup file"}, /* 833, gbak_write_error */ + {336330985, "could not drop database @1 (database might be in use)"}, /* 834, gbak_db_in_use */ + {336330990, "System memory exhausted"}, /* 835, gbak_sysmemex */ + {336331002, "SQL role"}, /* 836, gbak_restore_role_failed */ + {336331005, "SQL role parameter missing"}, /* 837, gbak_role_op_missing */ + {336331010, "page buffers parameter missing"}, /* 838, gbak_page_buffers_missing */ + {336331011, "expected page buffers, encountered \"@1\""}, /* 839, gbak_page_buffers_wrong_param */ + {336331012, "page buffers is allowed only on restore or create"}, /* 840, gbak_page_buffers_restore */ + {336331014, "size specification either missing or incorrect for file @1"}, /* 841, gbak_inv_size */ + {336331015, "file @1 out of sequence"}, /* 842, gbak_file_outof_sequence */ + {336331016, "can't join -- one of the files missing"}, /* 843, gbak_join_file_missing */ + {336331017, " standard input is not supported when using join operation"}, /* 844, gbak_stdin_not_supptd */ + {336331018, "standard output is not supported when using split operation"}, /* 845, gbak_stdout_not_supptd */ + {336331019, "backup file @1 might be corrupt"}, /* 846, gbak_bkup_corrupt */ + {336331020, "database file specification missing"}, /* 847, gbak_unk_db_file_spec */ + {336331021, "can't write a header record to file @1"}, /* 848, gbak_hdr_write_failed */ + {336331022, "free disk space exhausted"}, /* 849, gbak_disk_space_ex */ + {336331023, "file size given (@1) is less than minimum allowed (@2)"}, /* 850, gbak_size_lt_min */ + {336331025, "service name parameter missing"}, /* 851, gbak_svc_name_missing */ + {336331026, "Cannot restore over current database, must be SYSDBA or owner of the existing database."}, /* 852, gbak_not_ownr */ + {336331031, "\"read_only\" or \"read_write\" required"}, /* 853, gbak_mode_req */ + {336331033, "just data ignore all constraints etc."}, /* 854, gbak_just_data */ + {336331034, "restoring data only ignoring foreign key, unique, not null & other constraints"}, /* 855, gbak_data_only */ + {336397205, "ODS versions before ODS@1 are not supported"}, /* 856, dsql_too_old_ods */ + {336397206, "Table @1 does not exist"}, /* 857, dsql_table_not_found */ + {336397207, "View @1 does not exist"}, /* 858, dsql_view_not_found */ + {336397208, "At line @1, column @2"}, /* 859, dsql_line_col_error */ + {336397209, "At unknown line and column"}, /* 860, dsql_unknown_pos */ + {336397210, "Column @1 cannot be repeated in @2 statement"}, /* 861, dsql_no_dup_name */ + {336397211, "Too many values (more than @1) in member list to match against"}, /* 862, dsql_too_many_values */ + {336397212, "Array and BLOB data types not allowed in computed field"}, /* 863, dsql_no_array_computed */ + {336397213, "Implicit domain name @1 not allowed in user created domain"}, /* 864, dsql_implicit_domain_name */ + {336397214, "scalar operator used on field @1 which is not an array"}, /* 865, dsql_only_can_subscript_array */ + {336397215, "cannot sort on more than 255 items"}, /* 866, dsql_max_sort_items */ + {336397216, "cannot group on more than 255 items"}, /* 867, dsql_max_group_items */ + {336397217, "Cannot include the same field (@1.@2) twice in the ORDER BY clause with conflicting sorting options"}, /* 868, dsql_conflicting_sort_field */ + {336397218, "column list from derived table @1 has more columns than the number of items in its SELECT statement"}, /* 869, dsql_derived_table_more_columns */ + {336397219, "column list from derived table @1 has less columns than the number of items in its SELECT statement"}, /* 870, dsql_derived_table_less_columns */ + {336397220, "no column name specified for column number @1 in derived table @2"}, /* 871, dsql_derived_field_unnamed */ + {336397221, "column @1 was specified multiple times for derived table @2"}, /* 872, dsql_derived_field_dup_name */ + {336397222, "Internal dsql error: alias type expected by pass1_expand_select_node"}, /* 873, dsql_derived_alias_select */ + {336397223, "Internal dsql error: alias type expected by pass1_field"}, /* 874, dsql_derived_alias_field */ + {336397224, "Internal dsql error: column position out of range in pass1_union_auto_cast"}, /* 875, dsql_auto_field_bad_pos */ + {336397225, "Recursive CTE member (@1) can refer itself only in FROM clause"}, /* 876, dsql_cte_wrong_reference */ + {336397226, "CTE '@1' has cyclic dependencies"}, /* 877, dsql_cte_cycle */ + {336397227, "Recursive member of CTE can't be member of an outer join"}, /* 878, dsql_cte_outer_join */ + {336397228, "Recursive member of CTE can't reference itself more than once"}, /* 879, dsql_cte_mult_references */ + {336397229, "Recursive CTE (@1) must be an UNION"}, /* 880, dsql_cte_not_a_union */ + {336397230, "CTE '@1' defined non-recursive member after recursive"}, /* 881, dsql_cte_nonrecurs_after_recurs */ + {336397231, "Recursive member of CTE '@1' has @2 clause"}, /* 882, dsql_cte_wrong_clause */ + {336397232, "Recursive members of CTE (@1) must be linked with another members via UNION ALL"}, /* 883, dsql_cte_union_all */ + {336397233, "Non-recursive member is missing in CTE '@1'"}, /* 884, dsql_cte_miss_nonrecursive */ + {336397234, "WITH clause can't be nested"}, /* 885, dsql_cte_nested_with */ + {336397235, "column @1 appears more than once in USING clause"}, /* 886, dsql_col_more_than_once_using */ + {336397236, "feature is not supported in dialect @1"}, /* 887, dsql_unsupp_feature_dialect */ + {336397237, "CTE \"@1\" is not used in query"}, /* 888, dsql_cte_not_used */ + {336397238, "column @1 appears more than once in ALTER VIEW"}, /* 889, dsql_col_more_than_once_view */ + {336397239, "@1 is not supported inside IN AUTONOMOUS TRANSACTION block"}, /* 890, dsql_unsupported_in_auto_trans */ + {336397240, "Unknown node type @1 in dsql/GEN_expr"}, /* 891, dsql_eval_unknode */ + {336397241, "Argument for @1 in dialect 1 must be string or numeric"}, /* 892, dsql_agg_wrongarg */ + {336397242, "Argument for @1 in dialect 3 must be numeric"}, /* 893, dsql_agg2_wrongarg */ + {336397243, "Strings cannot be added to or subtracted from DATE or TIME types"}, /* 894, dsql_nodateortime_pm_string */ + {336397244, "Invalid data type for subtraction involving DATE, TIME or TIMESTAMP types"}, /* 895, dsql_invalid_datetime_subtract */ + {336397245, "Adding two DATE values or two TIME values is not allowed"}, /* 896, dsql_invalid_dateortime_add */ + {336397246, "DATE value cannot be subtracted from the provided data type"}, /* 897, dsql_invalid_type_minus_date */ + {336397247, "Strings cannot be added or subtracted in dialect 3"}, /* 898, dsql_nostring_addsub_dial3 */ + {336397248, "Invalid data type for addition or subtraction in dialect 3"}, /* 899, dsql_invalid_type_addsub_dial3 */ + {336397249, "Invalid data type for multiplication in dialect 1"}, /* 900, dsql_invalid_type_multip_dial1 */ + {336397250, "Strings cannot be multiplied in dialect 3"}, /* 901, dsql_nostring_multip_dial3 */ + {336397251, "Invalid data type for multiplication in dialect 3"}, /* 902, dsql_invalid_type_multip_dial3 */ + {336397252, "Division in dialect 1 must be between numeric data types"}, /* 903, dsql_mustuse_numeric_div_dial1 */ + {336397253, "Strings cannot be divided in dialect 3"}, /* 904, dsql_nostring_div_dial3 */ + {336397254, "Invalid data type for division in dialect 3"}, /* 905, dsql_invalid_type_div_dial3 */ + {336397255, "Strings cannot be negated (applied the minus operator) in dialect 3"}, /* 906, dsql_nostring_neg_dial3 */ + {336397256, "Invalid data type for negation (minus operator)"}, /* 907, dsql_invalid_type_neg */ + {336397257, "Cannot have more than 255 items in DISTINCT list"}, /* 908, dsql_max_distinct_items */ + {336723983, "unable to open database"}, /* 909, gsec_cant_open_db */ + {336723984, "error in switch specifications"}, /* 910, gsec_switches_error */ + {336723985, "no operation specified"}, /* 911, gsec_no_op_spec */ + {336723986, "no user name specified"}, /* 912, gsec_no_usr_name */ + {336723987, "add record error"}, /* 913, gsec_err_add */ + {336723988, "modify record error"}, /* 914, gsec_err_modify */ + {336723989, "find/modify record error"}, /* 915, gsec_err_find_mod */ + {336723990, "record not found for user: @1"}, /* 916, gsec_err_rec_not_found */ + {336723991, "delete record error"}, /* 917, gsec_err_delete */ + {336723992, "find/delete record error"}, /* 918, gsec_err_find_del */ + {336723996, "find/display record error"}, /* 919, gsec_err_find_disp */ + {336723997, "invalid parameter, no switch defined"}, /* 920, gsec_inv_param */ + {336723998, "operation already specified"}, /* 921, gsec_op_specified */ + {336723999, "password already specified"}, /* 922, gsec_pw_specified */ + {336724000, "uid already specified"}, /* 923, gsec_uid_specified */ + {336724001, "gid already specified"}, /* 924, gsec_gid_specified */ + {336724002, "project already specified"}, /* 925, gsec_proj_specified */ + {336724003, "organization already specified"}, /* 926, gsec_org_specified */ + {336724004, "first name already specified"}, /* 927, gsec_fname_specified */ + {336724005, "middle name already specified"}, /* 928, gsec_mname_specified */ + {336724006, "last name already specified"}, /* 929, gsec_lname_specified */ + {336724008, "invalid switch specified"}, /* 930, gsec_inv_switch */ + {336724009, "ambiguous switch specified"}, /* 931, gsec_amb_switch */ + {336724010, "no operation specified for parameters"}, /* 932, gsec_no_op_specified */ + {336724011, "no parameters allowed for this operation"}, /* 933, gsec_params_not_allowed */ + {336724012, "incompatible switches specified"}, /* 934, gsec_incompat_switch */ + {336724044, "Invalid user name (maximum 31 bytes allowed)"}, /* 935, gsec_inv_username */ + {336724045, "Warning - maximum 8 significant bytes of password used"}, /* 936, gsec_inv_pw_length */ + {336724046, "database already specified"}, /* 937, gsec_db_specified */ + {336724047, "database administrator name already specified"}, /* 938, gsec_db_admin_specified */ + {336724048, "database administrator password already specified"}, /* 939, gsec_db_admin_pw_specified */ + {336724049, "SQL role name already specified"}, /* 940, gsec_sql_role_specified */ + {336789504, "The license file does not exist or could not be opened for read"}, /* 941, license_no_file */ + {336789523, "operation already specified"}, /* 942, license_op_specified */ + {336789524, "no operation specified"}, /* 943, license_op_missing */ + {336789525, "invalid switch"}, /* 944, license_inv_switch */ + {336789526, "invalid switch combination"}, /* 945, license_inv_switch_combo */ + {336789527, "illegal operation/switch combination"}, /* 946, license_inv_op_combo */ + {336789528, "ambiguous switch"}, /* 947, license_amb_switch */ + {336789529, "invalid parameter, no switch specified"}, /* 948, license_inv_parameter */ + {336789530, "switch does not take any parameter"}, /* 949, license_param_specified */ + {336789531, "switch requires a parameter"}, /* 950, license_param_req */ + {336789532, "syntax error in command line"}, /* 951, license_syntx_error */ + {336789534, "The certificate was not added. A duplicate ID exists in the license file."}, /* 952, license_dup_id */ + {336789535, "The certificate was not added. Invalid certificate ID / Key combination."}, /* 953, license_inv_id_key */ + {336789536, "The certificate was not removed. The key does not exist or corresponds to a temporary evaluation license."}, /* 954, license_err_remove */ + {336789537, "An error occurred updating the license file. Operation cancelled."}, /* 955, license_err_update */ + {336789538, "The certificate could not be validated based on the information given. Please recheck the ID and key information."}, /* 956, license_err_convert */ + {336789539, "Operation failed. An unknown error occurred."}, /* 957, license_err_unk */ + {336789540, "Add license operation failed, KEY: @1 ID: @2"}, /* 958, license_svc_err_add */ + {336789541, "Remove license operation failed, KEY: @1"}, /* 959, license_svc_err_remove */ + {336789563, "The evaluation license has already been used on this server. You need to purchase a non-evaluation license."}, /* 960, license_eval_exists */ + {336920577, "found unknown switch"}, /* 961, gstat_unknown_switch */ + {336920578, "please retry, giving a database name"}, /* 962, gstat_retry */ + {336920579, "Wrong ODS version, expected @1, encountered @2"}, /* 963, gstat_wrong_ods */ + {336920580, "Unexpected end of database file."}, /* 964, gstat_unexpected_eof */ + {336920605, "Can't open database file @1"}, /* 965, gstat_open_err */ + {336920606, "Can't read a database page"}, /* 966, gstat_read_err */ + {336920607, "System memory exhausted"}, /* 967, gstat_sysmemex */ + {336986113, "Wrong value for access mode"}, /* 968, fbsvcmgr_bad_am */ + {336986114, "Wrong value for write mode"}, /* 969, fbsvcmgr_bad_wm */ + {336986115, "Wrong value for reserve space"}, /* 970, fbsvcmgr_bad_rs */ + {336986116, "Unknown tag (@1) in info_svr_db_info block after isc_svc_query()"}, /* 971, fbsvcmgr_info_err */ + {336986117, "Unknown tag (@1) in isc_svc_query() results"}, /* 972, fbsvcmgr_query_err */ + {336986118, "Unknown switch \"@1\""}, /* 973, fbsvcmgr_switch_unknown */ + {336986159, "Wrong value for shutdown mode"}, /* 974, fbsvcmgr_bad_sm */ + {336986160, "could not open file @1"}, /* 975, fbsvcmgr_fp_open */ + {336986161, "could not read file @1"}, /* 976, fbsvcmgr_fp_read */ + {336986162, "empty file @1"}, /* 977, fbsvcmgr_fp_empty */ + {337051649, "Switches trusted_svc and trusted_role are not supported from command line"}, /* 978, utl_trusted_switch */ {0, NULL} }; diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index 3a5730eacc..22cd3ac02b 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -688,6 +688,8 @@ static const struct { {335544985, -901}, /* 665 out_of_temp_space */ {335544986, -901}, /* 666 eds_expl_tran_ctrl */ {335544987, -902}, /* 667 no_trusted_spb */ + {335544988, -901}, /* 668 package_name */ + {335544989, -901}, /* 669 cannot_make_not_null */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ @@ -745,7 +747,21 @@ static const struct { {336003101, -817}, /* 29 upd_ins_with_complex_view */ {336003102, -817}, /* 30 dsql_incompatible_trigger_type */ {336003103, -817}, /* 31 dsql_db_trigger_type_cant_change */ + {336068645, -901}, /* 37 dyn_filter_not_found */ + {336068649, -901}, /* 41 dyn_func_not_found */ + {336068656, -901}, /* 48 dyn_index_not_found */ + {336068662, -901}, /* 54 dyn_view_not_found */ + {336068697, -901}, /* 89 dyn_domain_not_found */ + {336068717, -901}, /* 109 dyn_cant_modify_auto_trig */ {336068740, -901}, /* 132 dyn_dup_table */ + {336068748, -901}, /* 140 dyn_proc_not_found */ + {336068752, -901}, /* 144 dyn_exception_not_found */ + {336068754, -901}, /* 146 dyn_proc_param_not_found */ + {336068755, -901}, /* 147 dyn_trig_not_found */ + {336068759, -901}, /* 151 dyn_charset_not_found */ + {336068760, -901}, /* 152 dyn_collation_not_found */ + {336068763, -901}, /* 155 dyn_role_not_found */ + {336068767, -901}, /* 159 dyn_name_longer */ {336068784, -901}, /* 176 dyn_column_does_not_exist */ {336068796, -901}, /* 188 dyn_role_does_not_exist */ {336068797, -901}, /* 189 dyn_no_grant_admin_opt */ @@ -764,9 +780,11 @@ static const struct { {336068817, -829}, /* 209 dyn_invalid_dtype_conversion */ {336068818, -829}, /* 210 dyn_dtype_conv_invalid */ {336068820, -901}, /* 212 dyn_zero_len_id */ + {336068822, -901}, /* 214 dyn_gen_not_found */ {336068829, -829}, /* 221 max_coll_per_charset */ {336068830, -829}, /* 222 invalid_coll_attr */ {336068840, -901}, /* 232 dyn_wrong_gtt_scope */ + {336068849, -901}, /* 241 dyn_table_not_found */ {336068852, -829}, /* 244 dyn_scale_too_big */ {336068853, -829}, /* 245 dyn_precision_too_small */ {336068855, 106}, /* 247 dyn_miss_priv_warning */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index cdfb069326..5c6fb0bdd5 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -688,6 +688,8 @@ static const struct { {335544985, "HY000"}, // 665 out_of_temp_space {335544986, "42000"}, // 666 eds_expl_tran_ctrl {335544987, "28000"}, // 667 no_trusted_spb + {335544988, "42000"}, // 668 package_name + {335544989, "22006"}, // 669 cannot_make_not_null {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw @@ -745,7 +747,21 @@ static const struct { {336003101, "54001"}, // 29 upd_ins_with_complex_view {336003102, "42000"}, // 30 dsql_incompatible_trigger_type {336003103, "42000"}, // 31 dsql_db_trigger_type_cant_change + {336068645, "42000"}, // 37 dyn_filter_not_found + {336068649, "42000"}, // 41 dyn_func_not_found + {336068656, "42000"}, // 48 dyn_index_not_found + {336068662, "42000"}, // 54 dyn_view_not_found + {336068697, "42000"}, // 89 dyn_domain_not_found + {336068717, "42000"}, // 109 dyn_cant_modify_auto_trig {336068740, "42S01"}, // 132 dyn_dup_table + {336068748, "42000"}, // 140 dyn_proc_not_found + {336068752, "42000"}, // 144 dyn_exception_not_found + {336068754, "42000"}, // 146 dyn_proc_param_not_found + {336068755, "42000"}, // 147 dyn_trig_not_found + {336068759, "42000"}, // 151 dyn_charset_not_found + {336068760, "42000"}, // 152 dyn_collation_not_found + {336068763, "42000"}, // 155 dyn_role_not_found + {336068767, "42000"}, // 159 dyn_name_longer {336068784, "42S22"}, // 176 dyn_column_does_not_exist {336068796, "28000"}, // 188 dyn_role_does_not_exist {336068797, "28000"}, // 189 dyn_no_grant_admin_opt @@ -764,9 +780,11 @@ static const struct { {336068817, "42000"}, // 209 dyn_invalid_dtype_conversion {336068818, "42000"}, // 210 dyn_dtype_conv_invalid {336068820, "42000"}, // 212 dyn_zero_len_id + {336068822, "42000"}, // 214 dyn_gen_not_found {336068829, "2C000"}, // 221 max_coll_per_charset {336068830, "HY000"}, // 222 invalid_coll_attr {336068840, "HY000"}, // 232 dyn_wrong_gtt_scope + {336068849, "42000"}, // 241 dyn_table_not_found {336068852, "42000"}, // 244 dyn_scale_too_big {336068853, "42000"}, // 245 dyn_precision_too_small {336068855, "42000"}, // 247 dyn_miss_priv_warning diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 54f831f567..72aab3e847 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -5496,6 +5496,9 @@ static processing_state get_statement(TEXT* const statement, in_double_quoted_string } state = normal; + char lastChar = '\0'; + char altQuoteChar = '\0'; + while (!done) { SSHORT c = getNextInputChar(); @@ -5624,10 +5627,36 @@ static processing_state get_statement(TEXT* const statement, switch (state) { case normal: + if (lastChar == 'q' || lastChar == 'Q') + { + *p++ = lastChar = c; + count++; + altQuoteChar = c = getNextInputChar(); + + switch (altQuoteChar) + { + case '{': + altQuoteChar = '}'; + break; + case '(': + altQuoteChar = ')'; + break; + case '[': + altQuoteChar = ']'; + break; + case '<': + altQuoteChar = '>'; + break; + } + } + else + altQuoteChar = '\0'; + state = in_single_quoted_string; break; case in_single_quoted_string: - state = normal; + if (!altQuoteChar || lastChar == altQuoteChar) + state = normal; break; } break; @@ -5673,7 +5702,7 @@ static processing_state get_statement(TEXT* const statement, } } - *p++ = c; + *p++ = lastChar = c; count++; if (count > bufsize && !done) diff --git a/src/jrd/Database.h b/src/jrd/Database.h index 025459614e..4a022d4eb7 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -324,6 +324,20 @@ public: return TypedHandle::checkHandle(); } + void checkOdsForDsql(USHORT encodedVersion) + { + using namespace Firebird; + + if (ENCODE_ODS(dbb_ods_version, dbb_minor_original) < encodedVersion) + { + // Feature not supported on ODS version older than %d.%d + status_exception::raise( + Arg::Gds(isc_dsql_feature_not_supported_ods) << + Arg::Num(DECODE_ODS_MAJOR(encodedVersion)) << + Arg::Num(DECODE_ODS_MINOR(encodedVersion))); + } + } + mutable Firebird::RefPtr dbb_sync; // Database sync primitive Firebird::RefPtr dbb_lock_mgr; @@ -349,10 +363,12 @@ public: vcl* dbb_gen_id_pages; // known pages for gen_id BlobFilter* dbb_blob_filters; // known blob filters trig_vec* dbb_triggers[DB_TRIGGER_MAX]; + trig_vec* dbb_ddl_triggers; DatabaseSnapshot::SharedData* dbb_monitoring_data; // monitoring data DatabaseModules dbb_modules; // external function/filter modules + ExtEngineManager dbb_extManager; // external engine manager Firebird::Mutex dbb_meta_mutex; // Mutex to protect metadata changes while dbb_sync is unlocked Firebird::Mutex dbb_cmp_clone_mutex; @@ -436,7 +452,9 @@ public: BackupManager* dbb_backup_manager; // physical backup manager Firebird::TimeStamp dbb_creation_date; // creation date Firebird::GenericMap > > dbb_functions; // User defined functions + Firebird::QualifiedName, UserFunction*> > > dbb_functions; // User defined functions + Firebird::GenericMap > > dbb_charset_ids; // Character set ids // returns true if primary file is located on raw device bool onRawDevice() const; @@ -458,6 +476,7 @@ private: : dbb_sync(FB_NEW(*getDefaultMemoryPool()) Sync), dbb_page_manager(*p), dbb_modules(*p), + dbb_extManager(*p), dbb_filename(*p), dbb_database_name(*p), dbb_encrypt_key(*p), @@ -469,7 +488,8 @@ private: dbb_lock_owner_id(getLockOwnerId()), dbb_charsets(*p), dbb_creation_date(Firebird::TimeStamp::getCurrentTimeStamp()), - dbb_functions(*p) + dbb_functions(*p), + dbb_charset_ids(*p) { dbb_pools.add(p); dbb_internal.grow(irq_MAX); diff --git a/src/jrd/DatabaseSnapshot.cpp b/src/jrd/DatabaseSnapshot.cpp index c09673813b..74e896d67f 100644 --- a/src/jrd/DatabaseSnapshot.cpp +++ b/src/jrd/DatabaseSnapshot.cpp @@ -689,9 +689,7 @@ void DatabaseSnapshot::putField(thread_db* tdbb, Record* record, const DumpField MOV_move(tdbb, &from_desc, &to_desc); if (set_charset) - { charset = (int) value; - } } else if (field.type == VALUE_TIMESTAMP) { @@ -784,7 +782,7 @@ void DatabaseSnapshot::dumpData(thread_db* tdbb) for (Attachment* attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) { - if (!putAttachment(attachment, writer, fb_utils::genUniqueId())) + if (!putAttachment(tdbb, attachment, writer, fb_utils::genUniqueId())) continue; putContextVars(attachment->att_context_vars, writer, attachment->att_attachment_id, true); @@ -939,7 +937,8 @@ void DatabaseSnapshot::putDatabase(const Database* database, Writer& writer, int } -bool DatabaseSnapshot::putAttachment(const Attachment* attachment, Writer& writer, int stat_id) +bool DatabaseSnapshot::putAttachment(thread_db* tdbb, const Jrd::Attachment* attachment, + Writer& writer, int stat_id) { fb_assert(attachment); @@ -989,7 +988,7 @@ bool DatabaseSnapshot::putAttachment(const Attachment* attachment, Writer& write // remote process name record.storeString(f_mon_att_remote_process, attachment->att_remote_process); // charset - record.storeInteger(f_mon_att_charset_id, attachment->att_charset); + record.storeInteger(f_mon_att_charset_id, tdbb->getCharSet()); // timestamp record.storeTimestamp(f_mon_att_timestamp, attachment->att_timestamp); // garbage collection flag @@ -1139,7 +1138,10 @@ void DatabaseSnapshot::putCall(const jrd_req* request, Writer& writer, int stat_ // object name/type if (request->req_procedure) { - record.storeString(f_mon_call_name, request->req_procedure->prc_name); + if (request->req_procedure->prc_name.qualifier.hasData()) + record.storeString(f_mon_call_pkg_name, request->req_procedure->prc_name.qualifier); + + record.storeString(f_mon_call_name, request->req_procedure->prc_name.identifier); record.storeInteger(f_mon_call_type, obj_procedure); } else if (!request->req_trg_name.isEmpty()) diff --git a/src/jrd/DatabaseSnapshot.h b/src/jrd/DatabaseSnapshot.h index 7797f7d0ff..8d6a955562 100644 --- a/src/jrd/DatabaseSnapshot.h +++ b/src/jrd/DatabaseSnapshot.h @@ -335,7 +335,7 @@ private: static SINT64 getGlobalId(int); static void putDatabase(const Database*, Writer&, int); - static bool putAttachment(const Attachment*, Writer&, int); + static bool putAttachment(thread_db*, const Attachment*, Writer&, int); static void putTransaction(const jrd_tra*, Writer&, int); static void putRequest(const jrd_req*, Writer&, int); static void putCall(const jrd_req*, Writer&, int); diff --git a/src/jrd/ErrorImpl.cpp b/src/jrd/ErrorImpl.cpp new file mode 100644 index 0000000000..e5dbb2cb2c --- /dev/null +++ b/src/jrd/ErrorImpl.cpp @@ -0,0 +1,167 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project, based on previous work done + * by Eugeney Putilin , + * Vlad Khorsun and + * Roman Rokytskyy . + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Eugeney Putilin + * Vlad Khorsun + * Roman Rokytskyy + */ + +#include "firebird.h" +#include "../jrd/ErrorImpl.h" +#include "../jrd/err_proto.h" +#include "../common/classes/alloc.h" + +using namespace Firebird; +using Firebird::uint; + +namespace Jrd { + + +ErrorImpl::~ErrorImpl() +{ + for (ISC_STATUS* p = status; p <= next; p++) + { + if (*p == isc_arg_cstring) + { + ++p; + char* s = (char*) (*++p); + delete[] s; + } + } +} + + +void ErrorImpl::statusVectorToError(const ISC_STATUS* vector, Error* error) +{ + while (*vector != isc_arg_end) + { + switch (*vector) + { + case isc_arg_warning: + case isc_arg_gds: + case isc_arg_number: + case isc_arg_interpreted: + case isc_arg_vms: + case isc_arg_unix: + case isc_arg_win32: + error->addCode(*vector++); + error->addCode(*vector++); + break; + + case isc_arg_string: + error->addString((const char*) vector[1], strlen((const char*) vector[1])); + vector += 2; + break; + + case isc_arg_cstring: + error->addString((const char*) vector[2], vector[1]); + vector += 3; + break; + + default: + fb_assert(false); + return; + } + } +} + + +void ErrorImpl::exceptionToError(const Firebird::Exception& ex, Error* error) +{ + ISC_STATUS_ARRAY statusVector; + Firebird::stuff_exception(statusVector, ex); + statusVectorToError(statusVector, error); +} + + +bool FB_CALL ErrorImpl::addCode(int32 code) +{ + if (next - status >= ISC_STATUS_LENGTH - 1) + return false; + + *next++ = code; + *next = isc_arg_end; + + return true; +} + + +bool FB_CALL ErrorImpl::addString(const char* str, uint strLength) +{ + //// TODO: transliteration + + if (next - status < ISC_STATUS_LENGTH - 3) + { + *next++ = isc_arg_cstring; + + MemoryPool* pool = getDefaultMemoryPool(); + char* s = FB_NEW(*pool) char[strLength + 1]; + + memcpy(s, str, strLength); + s[strLength] = 0; + + *next++ = (ISC_STATUS) strLength; + *next++ = (ISC_STATUS)(IPTR) s; + *next = isc_arg_end; + } + + return true; +} + + +//--------------------- + + +RaiseError::~RaiseError() +{ + if (next == status) + return; + + Arg::StatusVector newStatusVector; + + if (*status != isc_arg_gds) + newStatusVector << Arg::Gds(isc_random); + + newStatusVector.append(Arg::StatusVector(status)); + + Firebird::status_exception::raise(newStatusVector); +} + + +//--------------------- + + +LogError::~LogError() +{ + if (next == status) + return; + + char msg[1024]; + const ISC_STATUS* p = status; + + if (fb_interpret(msg, sizeof(msg), &p) != 0) + gds__log("%s", msg); +} + + +} // namespace Jrd diff --git a/src/jrd/ErrorImpl.h b/src/jrd/ErrorImpl.h new file mode 100644 index 0000000000..f48d695686 --- /dev/null +++ b/src/jrd/ErrorImpl.h @@ -0,0 +1,124 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project, based on previous work done + * by Eugeney Putilin , + * Vlad Khorsun and + * Roman Rokytskyy . + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Eugeney Putilin + * Vlad Khorsun + * Roman Rokytskyy + */ + +#ifndef JRD_ERROR_IMPL_H +#define JRD_ERROR_IMPL_H + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "iberror.h" + +namespace Jrd { + + +class ErrorImpl : public Firebird::Error +{ +public: + inline ErrorImpl() + : next(status) + { + *next = isc_arg_end; + } + + virtual ~ErrorImpl(); + +public: + static void statusVectorToError(const ISC_STATUS* vector, Error* error); + static void exceptionToError(const Firebird::Exception& ex, Error* error); + +public: + bool errorOccurred() + { + return next != status; + } + +public: + inline operator Error* () + { + return this; + } + +public: + virtual bool FB_CALL addCode(Firebird::int32 code); + virtual bool FB_CALL addString(const char* str, Firebird::uint strLength); + +protected: + ISC_STATUS_ARRAY status; + ISC_STATUS* next; +}; + + +class RaiseError : public ErrorImpl +{ +public: + virtual ~RaiseError(); +}; + + +class LogError : public ErrorImpl +{ +public: + virtual ~LogError(); +}; + + +class DelegateError : public Firebird::Error +{ +public: + DelegateError(Firebird::Error* aError) + : error(aError), + raised(false) + { + } + + virtual bool FB_CALL addCode(Firebird::int32 code) + { + raised = true; + return error->addCode(code); + } + + virtual bool FB_CALL addString(const char* str, Firebird::uint strLength) + { + raised = true; + return error->addString(str, strLength); + } + + bool isRaised() + { + return raised; + } + +private: + Firebird::Error* error; + bool raised; +}; + + +} // namespace Jrd + +#endif // JRD_ERROR_IMPL_H diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp new file mode 100644 index 0000000000..11b66c4db8 --- /dev/null +++ b/src/jrd/ExtEngineManager.cpp @@ -0,0 +1,1033 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "consts_pub.h" +#include "iberror.h" +#include "inf_pub.h" +#include "../jrd/ExtEngineManager.h" +#include "../jrd/ErrorImpl.h" +#include "../jrd/ValueImpl.h" +#include "../jrd/ValuesImpl.h" +#include "../dsql/sqlda_pub.h" +#include "../jrd/dsc.h" +#include "../jrd/jrd.h" +#include "../jrd/exe.h" +#include "../jrd/req.h" +#include "../jrd/status.h" +#include "../jrd/tra.h" +#include "../jrd/PluginManager.h" +#include "../jrd/ibase.h" +#include "../jrd/os/path_utils.h" +#include "../jrd/cvt_proto.h" +#include "../jrd/evl_proto.h" +#include "../jrd/intl_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/mov_proto.h" +#include "../jrd/thread_proto.h" +#include "../common/classes/auto.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/init.h" +#include "../common/classes/objects_array.h" +#include "../common/config/config.h" + +#include "../config/ConfigFile.h" +#include "../config/ConfObj.h" +#include "../config/ConfObject.h" +#include "../config/Element.h" +#include "../config/ScanDir.h" +#include "../config/AdminException.h" + +using namespace Firebird; + + +namespace Jrd { + + +class ExtEngineManager::AttachmentImpl : public Firebird::Attachment +{ +public: + AttachmentImpl(ExternalContextImpl* aContext, Handle aHandle, Jrd::Attachment* aAttachment); + virtual ~AttachmentImpl(); + +public: + virtual void FB_CALL dispose(Error* error); + + virtual Handle FB_CALL getHandle(Error* error) const; + virtual const char* FB_CALL getUserName() const; + virtual const char* FB_CALL getDatabaseName() const; + +private: + ExternalContextImpl* context; + FB_API_HANDLE handle; + Jrd::Attachment* attachment; +}; + + +class ExtEngineManager::TransactionImpl : public Firebird::Transaction +{ +public: + TransactionImpl(Handle aHandle); + virtual ~TransactionImpl(); + +public: + virtual Handle FB_CALL getHandle(Error* error) const; + +private: + FB_API_HANDLE handle; +}; + + +template class ExtEngineManager::ContextManager +{ +public: + ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, T* obj, + CallerName aCallerName = CallerName()) + : attInfo(aAttInfo), + attachment(tdbb->getAttachment()), + transaction(tdbb->getTransaction()), + charSet(attachment->att_charset), + attInUse(attachment->att_in_use), + traInUse(transaction ? transaction->tra_in_use : false) + { + attachment->att_in_use = true; + + if (transaction) + { + callerName = transaction->tra_caller_name; + transaction->tra_caller_name = aCallerName; + ++transaction->tra_callback_count; + transaction->tra_in_use = true; + } + + attInfo->context->setTransaction(tdbb); + + setCharSet(tdbb, attInfo, obj); + } + + ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, USHORT aCharSet, + CallerName aCallerName = CallerName()) + : attInfo(aAttInfo), + attachment(tdbb->getAttachment()), + transaction(tdbb->getTransaction()), + charSet(attachment->att_charset), + attInUse(attachment->att_in_use), + traInUse(transaction ? transaction->tra_in_use : false) + { + attachment->att_charset = aCharSet; + attachment->att_in_use = true; + + if (transaction) + { + callerName = transaction->tra_caller_name; + transaction->tra_caller_name = aCallerName; + ++transaction->tra_callback_count; + transaction->tra_in_use = true; + } + + attInfo->context->setTransaction(tdbb); + } + + ~ContextManager() + { + if (transaction) + { + --transaction->tra_callback_count; + transaction->tra_in_use = traInUse; + transaction->tra_caller_name = callerName; + } + + attachment->att_in_use = attInUse; + attachment->att_charset = charSet; + } + +private: + void setCharSet(thread_db* tdbb, EngineAttachmentInfo* attInfo, T* obj) + { + attachment->att_charset = attInfo->adminCharSet; + + if (!obj) + return; + + Utf8 charSetName[MAX_SQL_IDENTIFIER_SIZE]; + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + + obj->getCharSet(RaiseError(), attInfo->context, charSetName, MAX_SQL_IDENTIFIER_SIZE - 1); + charSetName[MAX_SQL_IDENTIFIER_SIZE - 1] = '\0'; + } + + USHORT charSetId; + + if (!MET_get_char_coll_subtype(tdbb, &charSetId, + reinterpret_cast(charSetName), strlen(charSetName))) + { + status_exception::raise(Arg::Gds(isc_charset_not_found) << Arg::Str(charSetName)); + } + + attachment->att_charset = charSetId; + } + +private: + EngineAttachmentInfo* attInfo; + Jrd::Attachment* attachment; + jrd_tra* transaction; + USHORT charSet; + bool attInUse; + bool traInUse; + CallerName callerName; +}; + + +//--------------------- + + +ExtEngineManager::AttachmentImpl::AttachmentImpl(ExternalContextImpl* aContext, Handle aHandle, + Jrd::Attachment* aAttachment) + : context(aContext), + handle(aHandle), + attachment(aAttachment) +{ +} + +ExtEngineManager::AttachmentImpl::~AttachmentImpl() +{ + context->attachment.release(); + handle = 0; + dispose(LogError()); +} + +void FB_CALL ExtEngineManager::AttachmentImpl::dispose(Error* error) +{ + ISC_STATUS_ARRAY statusVector; + + if (handle) + { + if (isc_detach_database(statusVector, &handle) != 0) + { + ErrorImpl::statusVectorToError(statusVector, error); + return; + } + } + + context->attachment = NULL; +} + +Handle FB_CALL ExtEngineManager::AttachmentImpl::getHandle(Error* error) const +{ + return handle; +} + + +const char* FB_CALL ExtEngineManager::AttachmentImpl::getUserName() const +{ + return attachment->att_user->usr_user_name.c_str(); +} + + +const char* FB_CALL ExtEngineManager::AttachmentImpl::getDatabaseName() const +{ + return attachment->att_database->dbb_database_name.c_str(); +} + + +//--------------------- + + +ExtEngineManager::TransactionImpl::TransactionImpl(Handle aHandle) + : handle(aHandle) +{ +} + +ExtEngineManager::TransactionImpl::~TransactionImpl() +{ +} + +Handle FB_CALL ExtEngineManager::TransactionImpl::getHandle(Error* error) const +{ + return handle; +} + + +//--------------------- + + +ExtEngineManager::ExternalContextImpl::ExternalContextImpl(thread_db* tdbb, + ExternalEngine* aEngine) + : engine(aEngine), + internalAttachment(tdbb->getAttachment()), + miscInfo(*internalAttachment->att_pool), + traHandle(0) +{ + //// TODO: admin rights + + attHandle = internalAttachment->att_public_handle; + clientCharSet = INTL_charset_lookup(tdbb, internalAttachment->att_client_charset)->getName(); + + setTransaction(tdbb); +} + +ExtEngineManager::ExternalContextImpl::~ExternalContextImpl() +{ + releaseTransaction(); +} + +void ExtEngineManager::ExternalContextImpl::releaseTransaction() +{ + if (traHandle) + { + traHandle = 0; + transaction = NULL; + } +} + +void ExtEngineManager::ExternalContextImpl::setTransaction(thread_db* tdbb) +{ + releaseTransaction(); + + jrd_tra* tra = tdbb->getTransaction(); + traHandle = tra ? tra->tra_public_handle : 0; + + transaction = FB_NEW(*internalAttachment->att_pool) TransactionImpl(traHandle); +} + +ExternalEngine* ExtEngineManager::ExternalContextImpl::getEngine(Firebird::Error* error) +{ + return engine; +} + +Firebird::Attachment* FB_CALL ExtEngineManager::ExternalContextImpl::getAttachment(Error* error) +{ + if (!this->attachment) + { + thread_db* tdbb = JRD_get_thread_data(); + attachment = FB_NEW(*internalAttachment->att_pool) AttachmentImpl(this, attHandle, + tdbb->getAttachment()); + } + + return attachment; +} + +Firebird::Transaction* FB_CALL ExtEngineManager::ExternalContextImpl::getTransaction(Error* error) +{ + return transaction; +} + + +const Firebird::Utf8* FB_CALL ExtEngineManager::ExternalContextImpl::getClientCharSet() +{ + return clientCharSet.c_str(); +} + + +int FB_CALL ExtEngineManager::ExternalContextImpl::obtainInfoCode() +{ + static AtomicCounter counter; + return ++counter; +} + + +void* FB_CALL ExtEngineManager::ExternalContextImpl::getInfo(int code) +{ + void* value = NULL; + miscInfo.get(code, value); + return value; +} + + +void* FB_CALL ExtEngineManager::ExternalContextImpl::setInfo(int code, void* value) +{ + void* oldValue = getInfo(code); + miscInfo.put(code, value); + return oldValue; +} + + +//--------------------- + + +static InitInstance > > > enginesModules; + + +//--------------------- + + +ExtEngineManager::Function::Function(thread_db* tdbb, ExtEngineManager* aExtManager, + ExternalEngine* aEngine, Firebird::ExternalFunction* aFunction, + const UserFunction* aUdf) + : extManager(aExtManager), + engine(aEngine), + function(aFunction), + udf(aUdf), + database(tdbb->getDatabase()) +{ +} + + +ExtEngineManager::Function::~Function() +{ + Database::Checkout dcoHolder(database); + function->dispose(LogError()); +} + + +void ExtEngineManager::Function::execute(thread_db* tdbb, jrd_nod* args, impure_value* impure) +{ + EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine); + ContextManager ctxManager(tdbb, attInfo, + function); // CallerName(obj_udf, function->fun_name) + Attachment* attachment = tdbb->getAttachment(); + + impure->vlu_desc.dsc_flags = DSC_null; + ValueImpl result(&impure->vlu_desc, "", true); + + Firebird::HalfStaticArray impureArgs; + + impure_value* impureArgsPtr = impureArgs.getBuffer(args->nod_count); + try + { + ValuesImpl params(args->nod_count); + + for (int i = 0; i < args->nod_count; ++i) + { + impureArgsPtr->vlu_desc = udf->fun_rpt[i + 1].fun_desc; + + if (impureArgsPtr->vlu_desc.isText()) + { + impureArgsPtr->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), + impureArgsPtr->vlu_desc.getStringLength()) VaryingString(); + impureArgsPtr->vlu_desc.dsc_address = (UCHAR*) impureArgsPtr->vlu_string; + } + else + { + impureArgsPtr->vlu_string = NULL; + impureArgsPtr->vlu_desc.dsc_address = (UCHAR*) &impureArgsPtr->vlu_misc; + } + + dsc* arg = EVL_expr(tdbb, args->nod_arg[i]); + + if (tdbb->getRequest()->req_flags & req_null) + impureArgsPtr->vlu_desc.dsc_flags = DSC_null; + else + { + MOV_move(tdbb, arg, &impureArgsPtr->vlu_desc); + INTL_adjust_text_descriptor(tdbb, &impureArgsPtr->vlu_desc); + } + + params.getValue(i + 1)->make(&impureArgsPtr->vlu_desc, "", true); + + ++impureArgsPtr; + } + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + function->execute(RaiseError(), attInfo->context, ¶ms, &result); + } + } + catch (...) + { + for (int i = 0; i < args->nod_count; ++i) + delete impureArgs[i].vlu_string; + + throw; + } + + for (int i = 0; i < args->nod_count; ++i) + delete impureArgs[i].vlu_string; + + if (result.isNull()) + tdbb->getRequest()->req_flags |= req_null; + else + tdbb->getRequest()->req_flags &= ~req_null; +} + + +//--------------------- + + +ExtEngineManager::Procedure::Procedure(thread_db* tdbb, ExtEngineManager* aExtManager, + ExternalEngine* aEngine, Firebird::ExternalProcedure* aProcedure, + const jrd_prc* aPrc) + : extManager(aExtManager), + engine(aEngine), + procedure(aProcedure), + prc(aPrc), + database(tdbb->getDatabase()) +{ +} + + +ExtEngineManager::Procedure::~Procedure() +{ + Database::Checkout dcoHolder(database); + procedure->dispose(LogError()); +} + + +ExtEngineManager::ResultSet* ExtEngineManager::Procedure::open(thread_db* tdbb, + ValuesImpl* inputParams, ValuesImpl* outputParams) +{ + return FB_NEW(*tdbb->getDefaultPool()) ResultSet(tdbb, inputParams, outputParams, this); +} + + +//--------------------- + + +ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, ValuesImpl* inputParams, + ValuesImpl* outputParams, ExtEngineManager::Procedure* aProcedure) + : procedure(aProcedure), + database(tdbb->getDatabase()), + firstFetch(true) +{ + attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine); + ContextManager ctxManager(tdbb, attInfo, procedure->procedure, + (procedure->prc->prc_name.qualifier.isEmpty() ? + CallerName(obj_procedure, procedure->prc->prc_name.identifier) : + CallerName(obj_package_header, procedure->prc->prc_name.qualifier))); + Attachment* attachment = tdbb->getAttachment(); + + charSet = attachment->att_charset; + + Database::Checkout dcoHolder(tdbb->getDatabase()); + + resultSet = procedure->procedure->open(RaiseError(), attInfo->context, inputParams, + outputParams); +} + + +ExtEngineManager::ResultSet::~ResultSet() +{ + if (resultSet) + { + Database::Checkout dcoHolder(database); + resultSet->dispose(LogError()); + } +} + + +bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb) +{ + bool wasFirstFetch = firstFetch; + firstFetch = false; + + if (!resultSet) + return wasFirstFetch; + + ContextManager ctxManager(tdbb, attInfo, charSet, + (procedure->prc->prc_name.qualifier.isEmpty() ? + CallerName(obj_procedure, procedure->prc->prc_name.identifier) : + CallerName(obj_package_header, procedure->prc->prc_name.qualifier))); + + Database::Checkout dcoHolder(tdbb->getDatabase()); + return resultSet->fetch(RaiseError()); +} + + +//--------------------- + + +ExtEngineManager::Trigger::Trigger(thread_db* tdbb, ExtEngineManager* aExtManager, + ExternalEngine* aEngine, Firebird::ExternalTrigger* aTrigger, + const Jrd::Trigger* aTrg) + : extManager(aExtManager), + engine(aEngine), + trigger(aTrigger), + trg(aTrg), + database(tdbb->getDatabase()) +{ +} + + +ExtEngineManager::Trigger::~Trigger() +{ +} + + +void ExtEngineManager::Trigger::execute(thread_db* tdbb, Firebird::ExternalTrigger::Action action, + record_param* oldRpb, record_param* newRpb) +{ + EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine); + ContextManager ctxManager(tdbb, attInfo, trigger, + CallerName(obj_trigger, trg->name)); + + Array descs; + try + { + AutoPtr oldValues, newValues; + int valueOldCount = 0; + int valueNewCount = 0; + + if (oldRpb) + valueOldCount = setValues(tdbb, attInfo->context, oldValues, descs, oldRpb); + + if (newRpb) + valueNewCount = setValues(tdbb, attInfo->context, newValues, descs, newRpb); + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + + trigger->execute(RaiseError(), attInfo->context, action, oldValues, newValues); + + for (int i = 1; i <= valueNewCount; ++i) + { + ValueImpl* val = newValues->getValue(i); + + if (val->isNull()) + SET_NULL(newRpb->rpb_record, val->getFieldNumber()); + else + CLEAR_NULL(newRpb->rpb_record, val->getFieldNumber()); + } + } + } + catch (...) + { + for (size_t i = 0; i < descs.getCount(); ++i) + delete descs[i]; + throw; + } + + for (size_t i = 0; i < descs.getCount(); ++i) + delete descs[i]; +} + + +int ExtEngineManager::Trigger::setValues(thread_db* tdbb, ExternalContextImpl* context, + AutoPtr& values, Array& descs, record_param* rpb) +{ + Attachment* attachment = tdbb->getAttachment(); + + if (!rpb || !rpb->rpb_record) + return 0; + + Record* record = rpb->rpb_record; + const Format* format = record->rec_format; + + values = FB_NEW(*tdbb->getDefaultPool()) ValuesImpl(format->fmt_count); + + int start = descs.getCount(); + descs.resize(start + format->fmt_count); + + int j = 0; + + for (int i = 0; i < format->fmt_count; ++i) + { + descs[start + i] = FB_NEW(*tdbb->getDefaultPool()) dsc; + + if (format->fmt_desc[i].dsc_dtype != dtype_unknown) + { + EVL_field(rpb->rpb_relation, record, i, descs[start + i]); + + jrd_fld* field = (*rpb->rpb_relation->rel_fields)[i]; + fb_assert(field); + + values->getValue(j + 1)->make(descs[start + i], field->fld_name, true, i); + ++j; + } + } + + return j; +} + + +//--------------------- + + +ExtEngineManager::~ExtEngineManager() +{ + fb_assert(enginesAttachments.count() == 0); + + EnginesMap::Accessor accessor(&engines); + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + { + ExternalEngine* engine = accessor.current()->second; + engine->dispose(LogError()); + } +} + + +//--------------------- + + +void ExtEngineManager::initialize() +{ + Firebird::PathName pluginsPath = PluginManager::getPluginsDirectory(); + ScanDir dir(pluginsPath.c_str(), "*.conf"); + + try + { + SortedObjectsArray conflicts(*getDefaultMemoryPool()); + + while (dir.next()) + { + Vulcan::ConfigFile configFile(dir.getFilePath(), Vulcan::ConfigFile::LEX_none); + + for (Element* el = configFile.getObjects()->children; el; el = el->sibling) + { + if (el->name == "external_engine") + { + MetaName name = el->getAttributeName(0); + + if (enginesModules().exist(name) || conflicts.exist(name)) + { + string s; + s.printf("External engine %s defined more than once.", name.c_str()); + gds__log(s.c_str()); + + conflicts.add(name); + continue; + } + + Element* plugin = el->findChild("plugin_module"); + + if (plugin) + enginesModules().put(name, plugin->getAttributeName(0)); + else + conflicts.add(name); + } + } + } + } + catch (AdminException& ex) + { + string s; + s.printf("Error in plugin config file '%s': %s'", dir.getFilePath(), ex.getText()); + gds__log(s.c_str()); + } +} + + +void ExtEngineManager::closeAttachment(thread_db* tdbb, Attachment* attachment) +{ + Array enginesCopy; + + { // scope + ReadLockGuard readGuard(enginesLock); + + EnginesMap::Accessor accessor(&engines); + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + enginesCopy.add(accessor.current()->second); + } + + Database::Checkout dcoHolder(tdbb->getDatabase()); + + for (Array::iterator i = enginesCopy.begin(); i != enginesCopy.end(); ++i) + { + ExternalEngine* engine = *i; + EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine, true); + + if (attInfo) + { + ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet); + engine->closeAttachment(LogError(), attInfo->context); + delete attInfo; + } + } +} + + +ExtEngineManager::Function* ExtEngineManager::makeFunction(thread_db* tdbb, const UserFunction* udf, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body) +{ + string entryPointTrimmed = entryPoint; + entryPointTrimmed.trim(); + + EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine); + ContextManager ctxManager(tdbb, attInfo, + attInfo->adminCharSet); // CallerName(obj_udf, udf->fun_name) + + ExternalFunction* externalFunction; + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + + externalFunction = attInfo->engine->makeFunction(RaiseError(), + attInfo->context, udf->fun_name.qualifier.nullStr(), udf->fun_name.identifier.c_str(), + entryPointTrimmed.nullStr(), body.nullStr()); + + if (!externalFunction) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("External function not returned by the external engine plugin")); + } + } + + try + { + return FB_NEW(getPool()) Function(tdbb, this, attInfo->engine, externalFunction, udf); + } + catch (...) + { + Database::Checkout dcoHolder(tdbb->getDatabase()); + externalFunction->dispose(LogError()); + throw; + } +} + + +ExtEngineManager::Procedure* ExtEngineManager::makeProcedure(thread_db* tdbb, const jrd_prc* prc, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body) +{ + string entryPointTrimmed = entryPoint; + entryPointTrimmed.trim(); + + EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine); + ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet, + (prc->prc_name.qualifier.isEmpty() ? + CallerName(obj_procedure, prc->prc_name.identifier) : + CallerName(obj_package_header, prc->prc_name.qualifier))); + + ExternalProcedure* externalProcedure; + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + + externalProcedure = attInfo->engine->makeProcedure(RaiseError(), + attInfo->context, prc->prc_name.qualifier.nullStr(), prc->prc_name.identifier.c_str(), + entryPointTrimmed.nullStr(), body.nullStr()); + + if (!externalProcedure) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("External procedure not returned by the external engine plugin")); + } + } + + try + { + return FB_NEW(getPool()) Procedure(tdbb, this, attInfo->engine, externalProcedure, prc); + } + catch (...) + { + Database::Checkout dcoHolder(tdbb->getDatabase()); + externalProcedure->dispose(LogError()); + throw; + } +} + + +ExtEngineManager::Trigger* ExtEngineManager::makeTrigger(thread_db* tdbb, const Jrd::Trigger* trg, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body, Firebird::ExternalTrigger::Type type) +{ + string entryPointTrimmed = entryPoint; + entryPointTrimmed.trim(); + + MetaName relationNameTrimmed; + if (trg->relation) + relationNameTrimmed = trg->relation->rel_name; + + EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine); + ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet, + CallerName(obj_trigger, trg->name)); + + ExternalTrigger* externalTrigger; + + { // scope + Database::Checkout dcoHolder(tdbb->getDatabase()); + + externalTrigger = attInfo->engine->makeTrigger(RaiseError(), attInfo->context, + trg->name.c_str(), entryPointTrimmed.nullStr(), body.nullStr(), + relationNameTrimmed.c_str(), type); + + if (!externalTrigger) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("External trigger not returned by the external engine plugin")); + } + } + + try + { + return FB_NEW(getPool()) Trigger(tdbb, this, attInfo->engine, externalTrigger, trg); + } + catch (...) + { + Database::Checkout dcoHolder(tdbb->getDatabase()); + externalTrigger->dispose(LogError()); + throw; + } +} + + +ExternalEngine* ExtEngineManager::getEngine(thread_db* tdbb, const Firebird::MetaName& name) +{ + ReadLockGuard readGuard(enginesLock); + ExternalEngine* engine = NULL; + + if (!engines.get(name, engine)) + { + readGuard.release(); + WriteLockGuard writeGuard(enginesLock); + + if (!engines.get(name, engine)) + { + string pluginName; + if (enginesModules().get(name, pluginName)) + { + PluginImpl* plugin = PluginManager::getPlugin(pluginName); + + EngineAttachment key(NULL, NULL); + AutoPtr attInfo; + ExternalContextImpl* context = NULL; + + try + { + Database::Checkout dcoHolder(tdbb->getDatabase()); + + engine = plugin->getExternalEngineFactory()->createEngine(RaiseError(), + EXTERNAL_VERSION_1, name.c_str()); + + if (engine) + { + if (engine->getVersion(RaiseError()) != EXTERNAL_VERSION_1) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str(string("Incompatible plugin version for external engine \"") + + name.c_str() + "\"")); + } + + Database::SyncGuard dsGuard(tdbb->getDatabase()); + + key = EngineAttachment(engine, tdbb->getAttachment()); + attInfo = FB_NEW(getPool()) EngineAttachmentInfo(); + attInfo->engine = engine; + attInfo->context = FB_NEW(getPool()) ExternalContextImpl(tdbb, engine); + + { // scope + ContextManager ctxManager(tdbb, attInfo, CS_UTF8); + + Utf8 charSetName[MAX_SQL_IDENTIFIER_SIZE] = "NONE"; + engine->open(RaiseError(), attInfo->context, charSetName, + MAX_SQL_IDENTIFIER_SIZE - 1); + charSetName[MAX_SQL_IDENTIFIER_SIZE - 1] = '\0'; + + if (!MET_get_char_coll_subtype(tdbb, &attInfo->adminCharSet, + reinterpret_cast(charSetName), + strlen(charSetName))) + { + status_exception::raise(Arg::Gds( + isc_charset_not_found) << + Arg::Str(charSetName)); + } + } + + ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet); + engine->openAttachment(LogError(), attInfo->context); + } + } + catch (...) + { + if (context) + context->releaseTransaction(); + if (engine) + engine->dispose(LogError()); + + throw; + } + + engines.put(name, engine); + enginesAttachments.put(key, attInfo); + attInfo.release(); + + if (context) + context->releaseTransaction(); + } + } + } + + if (!engine) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str(string("External engine \"") + name.c_str() + "\" not found")); + } + + return engine; +} + + +ExtEngineManager::EngineAttachmentInfo* ExtEngineManager::getEngineAttachment( + thread_db* tdbb, const Firebird::MetaName& name) +{ + ExternalEngine* engine = getEngine(tdbb, name); + return getEngineAttachment(tdbb, engine); +} + + +ExtEngineManager::EngineAttachmentInfo* ExtEngineManager::getEngineAttachment( + thread_db* tdbb, ExternalEngine* engine, bool closing) +{ + EngineAttachment key(engine, tdbb->getAttachment()); + EngineAttachmentInfo* attInfo = NULL; + + ReadLockGuard readGuard(&enginesLock); + + if (!enginesAttachments.get(key, attInfo) && !closing) + { + readGuard.release(); + WriteLockGuard writeGuard(enginesLock); + + if (!enginesAttachments.get(key, attInfo)) + { + attInfo = FB_NEW(getPool()) EngineAttachmentInfo(); + attInfo->engine = engine; + attInfo->context = FB_NEW(getPool()) ExternalContextImpl(tdbb, engine); + + EnginesAttachmentsMap::Accessor accessor(&enginesAttachments); + accessor.getFirst(); + attInfo->adminCharSet = accessor.current()->second->adminCharSet; + + enginesAttachments.put(key, attInfo); + + ContextManager ctxManager(tdbb, attInfo, attInfo->adminCharSet); + Database::Checkout dcoHolder(tdbb->getDatabase()); + engine->openAttachment(LogError(), attInfo->context); + } + + return attInfo; + } + + if (closing && attInfo) + { + readGuard.release(); + WriteLockGuard writeGuard(enginesLock); + enginesAttachments.remove(key); + } + + return attInfo; +} + + +} // namespace Jrd diff --git a/src/jrd/ExtEngineManager.h b/src/jrd/ExtEngineManager.h new file mode 100644 index 0000000000..e274af17d1 --- /dev/null +++ b/src/jrd/ExtEngineManager.h @@ -0,0 +1,262 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_EXT_ENGINE_MANAGER_H +#define JRD_EXT_ENGINE_MANAGER_H + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "../jrd/common.h" +#include "../common/classes/array.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/MetaName.h" +#include "../common/classes/auto.h" +#include "../common/classes/rwlock.h" + +struct dsc; + +namespace Jrd { + +class thread_db; +class jrd_nod; +class jrd_prc; +class Attachment; +class Database; +class Format; +class Trigger; +class UserFunction; +class ValueImpl; +class ValuesImpl; +struct impure_value; +struct record_param; + + +class ExtEngineManager : public Firebird::PermanentStorage +{ +private: + class AttachmentImpl; + template class ContextManager; + class TransactionImpl; + + class ExternalContextImpl : public Firebird::ExternalContext + { + friend class AttachmentImpl; + + public: + ExternalContextImpl(thread_db* tdbb, Firebird::ExternalEngine* aEngine); + virtual ~ExternalContextImpl(); + + public: + void releaseTransaction(); + void setTransaction(thread_db* tdbb); + + public: + virtual Firebird::ExternalEngine* FB_CALL getEngine(Firebird::Error* error); + virtual Firebird::Attachment* FB_CALL getAttachment(Firebird::Error* error); + virtual Firebird::Transaction* FB_CALL getTransaction(Firebird::Error* error); + virtual const Firebird::Utf8* FB_CALL getClientCharSet(); + virtual int FB_CALL obtainInfoCode(); + virtual void* FB_CALL getInfo(int code); + virtual void* FB_CALL setInfo(int code, void* value); + + private: + Firebird::ExternalEngine* engine; + Attachment* internalAttachment; + Firebird::GenericMap > miscInfo; + FB_API_HANDLE traHandle; + FB_API_HANDLE attHandle; + Firebird::MetaName clientCharSet; + Firebird::AutoPtr attachment; + Firebird::AutoPtr transaction; + }; + + struct EngineAttachment + { + EngineAttachment(Firebird::ExternalEngine* aEngine, Jrd::Attachment* aAttachment) + : engine(aEngine), + attachment(aAttachment) + { + } + + static bool greaterThan(const EngineAttachment& i1, const EngineAttachment& i2) + { + return (i1.engine > i2.engine) || + (i1.engine == i2.engine && i1.attachment > i2.attachment); + } + + Firebird::ExternalEngine* engine; + Jrd::Attachment* attachment; + }; + + struct EngineAttachmentInfo + { + EngineAttachmentInfo() + : engine(NULL), + context(NULL) + { + } + + Firebird::ExternalEngine* engine; + Firebird::AutoPtr context; + USHORT adminCharSet; + }; + +public: + class Function + { + public: + Function(thread_db* tdbb, ExtEngineManager* aExtManager, + Firebird::ExternalEngine* aEngine, + Firebird::ExternalFunction* aFunction, + const UserFunction* aUdf); + ~Function(); + + public: + void execute(thread_db* tdbb, jrd_nod* args, impure_value* impure); + + private: + ExtEngineManager* extManager; + Firebird::ExternalEngine* engine; + Firebird::ExternalFunction* function; + const UserFunction* udf; + Database* database; + }; + + class ResultSet; + + class Procedure + { + public: + Procedure(thread_db* tdbb, ExtEngineManager* aExtManager, + Firebird::ExternalEngine* aEngine, + Firebird::ExternalProcedure* aProcedure, + const jrd_prc* aPrc); + ~Procedure(); + + public: + ResultSet* open(thread_db* tdbb, ValuesImpl* inputParams, ValuesImpl* outputParams); + + private: + ExtEngineManager* extManager; + Firebird::ExternalEngine* engine; + Firebird::ExternalProcedure* procedure; + const jrd_prc* prc; + Database* database; + + friend class ResultSet; + }; + + class ResultSet + { + public: + ResultSet(thread_db* tdbb, ValuesImpl* inputParams, ValuesImpl* outputParams, + Procedure* aProcedure); + ~ResultSet(); + + public: + bool fetch(thread_db* tdbb); + + private: + Procedure* procedure; + Database* database; + bool firstFetch; + EngineAttachmentInfo* attInfo; + Firebird::ExternalResultSet* resultSet; + USHORT charSet; + }; + + class Trigger + { + public: + Trigger(thread_db* tdbb, ExtEngineManager* aExtManager, + Firebird::ExternalEngine* aEngine, + Firebird::ExternalTrigger* aTrigger, + const Jrd::Trigger* aTrg); + ~Trigger(); + + public: + void execute(thread_db* tdbb, Firebird::ExternalTrigger::Action action, + record_param* oldRpb, record_param* newRpb); + + private: + int setValues(thread_db* tdbb, ExternalContextImpl* context, + Firebird::AutoPtr& values, + Firebird::Array& descs, record_param* rpb); + + private: + ExtEngineManager* extManager; + Firebird::ExternalEngine* engine; + Firebird::ExternalTrigger* trigger; + const Jrd::Trigger* trg; + Database* database; + }; + +public: + ExtEngineManager(MemoryPool& p) + : PermanentStorage(p), + engines(p), + enginesAttachments(p) + { + } + + ~ExtEngineManager(); + +public: + static void initialize(); + +public: + void closeAttachment(thread_db* tdbb, Attachment* attachment); + + Function* makeFunction(thread_db* tdbb, const UserFunction* udf, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body); + Procedure* makeProcedure(thread_db* tdbb, const jrd_prc* prc, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body); + Trigger* makeTrigger(thread_db* tdbb, const Jrd::Trigger* trg, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const Firebird::string& body, Firebird::ExternalTrigger::Type type); + +private: + Firebird::ExternalEngine* getEngine(thread_db* tdbb, + const Firebird::MetaName& name); + EngineAttachmentInfo* getEngineAttachment(thread_db* tdbb, + const Firebird::MetaName& name); + EngineAttachmentInfo* getEngineAttachment(thread_db* tdbb, + Firebird::ExternalEngine* engine, bool closing = false); + +private: + typedef Firebird::GenericMap > > EnginesMap; + typedef Firebird::GenericMap >, EngineAttachment> EnginesAttachmentsMap; + + Firebird::RWLock enginesLock; + EnginesMap engines; + EnginesAttachmentsMap enginesAttachments; +}; + + +} // namespace Jrd + +#endif // JRD_EXT_ENGINE_MANAGER_H diff --git a/src/jrd/PluginManager.cpp b/src/jrd/PluginManager.cpp new file mode 100644 index 0000000000..c0d8b243b0 --- /dev/null +++ b/src/jrd/PluginManager.cpp @@ -0,0 +1,292 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "consts_pub.h" +#include "iberror.h" +#include "inf_pub.h" +#include "../jrd/PluginManager.h" +#include "../jrd/ErrorImpl.h" +#include "../jrd/os/path_utils.h" +#include "../jrd/err_proto.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/init.h" +#include "../common/config/config.h" +#include "../config/ConfigFile.h" +#include "../config/ConfObj.h" +#include "../config/ConfObject.h" +#include "../config/Element.h" +#include "../config/ScanDir.h" +#include "../config/AdminException.h" + +using namespace Firebird; +using Firebird::uint; + +namespace Jrd { + + +namespace +{ + class PluginsMap : public GenericMap > > + { + public: + PluginsMap(MemoryPool& p) + : GenericMap > >(p) + { + } + + ~PluginsMap() + { + // unload plugins + Accessor accessor(this); + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + { + PluginImpl* plugin = accessor.current()->second; + delete plugin; + } + } + }; +} + + +static GlobalPtr plugins; +static GlobalPtr libraryName; + + +void PluginManager::initialize() +{ +#ifdef WIN_NT +#ifdef EMBEDDED + libraryName->assign("fbembed.dll"); +#elif defined(SUPERSERVER) + libraryName->assign("fbserver.exe"); +#else + libraryName->assign("fb_inet_server.exe"); +#endif +#elif !defined(SUPERSERVER) + PathUtils::concatPath(libraryName, Config::getRootDirectory(), "lib/libfbembed.so"); +#endif + + PathName pluginsPath = getPluginsDirectory(); + ScanDir dir(pluginsPath.c_str(), "*.conf"); + + try + { + SortedObjectsArray conflicts(*getDefaultMemoryPool()); + + while (dir.next()) + { + Vulcan::ConfigFile configFile(dir.getFilePath(), Vulcan::ConfigFile::LEX_none); + + for (Element* el = configFile.getObjects()->children; el; el = el->sibling) + { + if (el->name == "plugin_module") + { + AutoPtr plugin(new PluginImpl); + plugin->name = el->getAttributeName(0); + + if (plugins->exist(plugin->name) || conflicts.exist(plugin->name)) + { + string s; + s.printf("Plugin %s defined more than once.", plugin->name.c_str()); + gds__log(s.c_str()); + + conflicts.add(plugin->name); + continue; + } + + ConfObj objModule(configFile.findObject("plugin_module", plugin->name.c_str())); + plugin->filename = objModule->getValue("filename", ""); + + Element* config = el->findChild("plugin_config"); + if (config) + { + string configName(config->getAttributeName(0)); + ConfObj objConfig(configFile.findObject("plugin_config", + configName.c_str())); + + for (Element* elConfig = objConfig->object->children; + elConfig; elConfig = elConfig->sibling) + { + string key = elConfig->name; + string value; + const char* attribute; + + for (int i = 0; (attribute = elConfig->getAttributeName(i)); ++i) + { + if (i != 0) + value += PathUtils::dir_list_sep; + value += objConfig->expand(attribute); + } + + plugin->configInfo.add(ConfigEntry(*getDefaultMemoryPool(), key, value)); + } + + PluginImpl* p = plugin.release(); + plugins->put(p->name, p); + } + } + } + } + } + catch (AdminException& ex) + { + string s; + s.printf("Error in plugin config file '%s': %s'", dir.getFilePath(), ex.getText()); + gds__log(s.c_str()); + } +} + + +PathName PluginManager::getPluginsDirectory() +{ + PathName pluginsPath; + PathUtils::concatPath(pluginsPath, Config::getRootDirectory(), "plugins"); + return pluginsPath; +} + + +PluginImpl* PluginManager::getPlugin(const string& name) +{ + PluginImpl* plugin; + + if (!plugins->get(name, plugin)) + { + //// TODO: localize + string s; + s.printf("Plugin %s not found.", name.c_str()); + gds__log(s.c_str()); + + status_exception::raise(Arg::Gds(isc_random) << Arg::Str(s)); + } + + if (plugin->module) + return plugin; + + static GlobalPtr pluginInitMutex; + MutexLockGuard pluginInitGuard(pluginInitMutex); + + if (plugin->module) + return plugin; + + ModuleLoader::Module* module = ModuleLoader::loadModule(plugin->filename); + + if (!module) + { + ModuleLoader::doctorModuleExtention(plugin->filename); + module = ModuleLoader::loadModule(plugin->filename); + } + + if (!module) + { + //// TODO: localize + string s; + s.printf("Can't load plugin module '%s'.", plugin->filename.c_str()); + gds__log(s.c_str()); + + status_exception::raise(Arg::Gds(isc_random) << Arg::Str(s)); + } + + PluginEntryPoint entryPoint; + if (module->findSymbol(STRINGIZE(FB_PLUGIN_ENTRY_POINT), entryPoint)) + entryPoint(RaiseError(), plugin); + else + { + //// TODO: localize + string s; + s.printf("Entrypoint of Plugin '%s' does not exist.", plugin->filename.c_str()); + gds__log(s.c_str()); + + status_exception::raise(Arg::Gds(isc_random) << Arg::Str(s)); + } + + plugin->module = module; + + return plugin; +} + + +//--------------------- + + +int FB_CALL PluginImpl::getVersion() +{ + return PLUGIN_VERSION_1; +} + +const char* FB_CALL PluginImpl::getName() +{ + return name.c_str(); +} + +const char* FB_CALL PluginImpl::getLibraryName() +{ + return libraryName->nullStr(); +} + +uint FB_CALL PluginImpl::getConfigInfoCount() +{ + return configInfo.getCount(); +} + +void FB_CALL PluginImpl::getConfigInfo(Error* error, uint index, const char** key, const char** value) +{ + if (index < 0 || (size_t) index >= configInfo.getCount()) + { + //// TODO: localize + ISC_STATUS status[] = { + isc_arg_gds, + isc_random, isc_arg_string, + (ISC_STATUS) "Invalid value for parameter index at Plugin::getConfigInfo: out of bounds", + isc_arg_end}; + ErrorImpl::statusVectorToError(status, error); + } + else + { + *key = configInfo[index].first.c_str(); + *value = configInfo[index].second.c_str(); + } +} + + +void FB_CALL PluginImpl::setExternalEngineFactory(ExternalEngineFactory* factory) +{ + externalEngineFactory = factory; +} + + +ExternalEngineFactory* PluginImpl::getExternalEngineFactory() +{ + if (!externalEngineFactory) + { + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Plugin @1 does not create @2 instances") << + Arg::Str(name) << + Arg::Str("ExternalEngine")); + } + + return externalEngineFactory; +} + + +} // namespace Jrd diff --git a/src/jrd/PluginManager.h b/src/jrd/PluginManager.h new file mode 100644 index 0000000000..4be4d16db7 --- /dev/null +++ b/src/jrd/PluginManager.h @@ -0,0 +1,92 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_PLUGIN_MANAGER_H +#define JRD_PLUGIN_MANAGER_H + +#include "FirebirdPluginApi.h" +#include "../jrd/common.h" +#include "../jrd/os/mod_loader.h" +#include "../common/classes/array.h" +#include "../common/classes/auto.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/MetaName.h" +#include "../common/classes/objects_array.h" +#include "../common/classes/rwlock.h" + +namespace Jrd { + + +typedef Firebird::Pair > ConfigEntry; + +class PluginImpl : public Firebird::Plugin, public Firebird::GlobalStorage +{ +friend class PluginManager; + +public: + PluginImpl() + : name(getPool()), + filename(getPool()), + configInfo(getPool()), + module(NULL), + externalEngineFactory(NULL) + { + } + + virtual int FB_CALL getVersion(); + virtual const char* FB_CALL getName(); + virtual const char* FB_CALL getLibraryName(); + virtual Firebird::uint FB_CALL getConfigInfoCount(); + virtual void FB_CALL getConfigInfo(Firebird::Error* error, Firebird::uint index, + const char** key, const char** value); + + virtual void FB_CALL setExternalEngineFactory(Firebird::ExternalEngineFactory* factory); + +public: + Firebird::ExternalEngineFactory* getExternalEngineFactory(); + +private: + Firebird::string name; + Firebird::PathName filename; + Firebird::SortedObjectsArray< + ConfigEntry, + Firebird::EmptyStorage, + Firebird::string, Firebird::FirstPointerKey + > configInfo; + Firebird::AutoPtr module; + Firebird::ExternalEngineFactory* externalEngineFactory; +}; + + +class PluginManager +{ +public: + static void initialize(); + static Firebird::PathName getPluginsDirectory(); + static PluginImpl* getPlugin(const Firebird::string& name); +}; + + +} // namespace Jrd + +#endif // JRD_PLUGIN_MANAGER_H diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index 330faff467..63af7622d5 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -25,9 +25,28 @@ #include "../jrd/align.h" #include "../jrd/jrd.h" #include "../jrd/dsc.h" +#include "../jrd/req.h" #include "../dsql/dsql.h" +#include "../common/classes/auto.h" #include "../dsql/sqlda_pub.h" #include "../dsql/dsql_proto.h" +#include "../jrd/mov_proto.h" + +using namespace Firebird; + + +namespace +{ + class ParamCmp + { + public: + static int greaterThan(const Jrd::dsql_par* p1, const Jrd::dsql_par* p2) + { + return p1->par_index > p2->par_index; + } + }; +} + namespace Jrd { @@ -35,9 +54,12 @@ namespace Jrd { PreparedStatement::PreparedStatement(thread_db* tdbb, Firebird::MemoryPool& pool, Attachment* attachment, jrd_tra* transaction, const Firebird::string& text) : Firebird::PermanentStorage(pool), - values(pool), - blr(pool), - message(pool), + inValues(pool), + outValues(pool), + inBlr(pool), + outBlr(pool), + inMessage(pool), + outMessage(pool), resultSet(NULL) { request = DSQL_allocate_statement(tdbb, attachment); @@ -49,9 +71,11 @@ PreparedStatement::PreparedStatement(thread_db* tdbb, Firebird::MemoryPool& pool DSQL_prepare(tdbb, transaction, &request, text.length(), text.c_str(), dialect, 0, NULL, 0, NULL); - if (request->req_receive) { - parseDsqlMessage(request->req_receive, values, blr, message); - } + if (request->req_send) + parseDsqlMessage(request->req_send, inValues, inBlr, inMessage); + + if (request->req_receive) + parseDsqlMessage(request->req_receive, outValues, outBlr, outMessage); } catch (const Firebird::Exception&) { @@ -72,10 +96,27 @@ PreparedStatement::~PreparedStatement() } +void PreparedStatement::setDesc(thread_db* tdbb, int param, const dsc& value) +{ + // Setup tdbb info necessary for blobs. + AutoSetRestore2 autoRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, getRequest()->req_request); + AutoSetRestore autoRequestTrans(&getRequest()->req_request->req_transaction, + tdbb->getTransaction()); + + MOV_move(tdbb, const_cast(&value), &inValues[(param - 1) * 2]); + + const dsc* desc = &inValues[(param - 1) * 2 + 1]; + fb_assert(desc->dsc_dtype == dtype_short); + *reinterpret_cast(desc->dsc_address) = 0; +} + + void PreparedStatement::execute(thread_db* tdbb, jrd_tra* transaction) { fb_assert(resultSet == NULL); - DSQL_execute(tdbb, &transaction, request, 0, NULL, 0, 0, NULL, 0, NULL, /*0,*/ 0, NULL); + DSQL_execute(tdbb, &transaction, request, inBlr.getCount(), inBlr.begin(), 0, + inMessage.getCount(), inMessage.begin(), 0, NULL, /*0,*/ 0, NULL); } @@ -88,33 +129,37 @@ ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction int PreparedStatement::getResultCount() const { - return values.getCount() / 2; + return outValues.getCount() / 2; +} + + +int PreparedStatement::getUpdateCount() const +{ + return request->req_request->req_records_updated; } void PreparedStatement::parseDsqlMessage(dsql_msg* dsqlMsg, Firebird::Array& values, Firebird::UCharBuffer& blr, Firebird::UCharBuffer& msg) { - // Parameters in dsqlMsg->msg_parameters almost always linked in descending + // hvlad: Parameters in dsqlMsg->msg_parameters almost always linked in descending // order by par_index. The only known exception is EXECUTE BLOCK statement. // To generate correct BLR we must walk params in ascending par_index order. // So store all params in array in an ascending par_index order despite of // order in linked list. + // ASF: Input parameters doesn't come necessary in ascending or descending order, + // so I changed the code to use a SortedArray. + + SortedArray< + const dsql_par*, + EmptyStorage, const dsql_par*, + DefaultKeyValue, + ParamCmp> params; - Firebird::HalfStaticArray params; - USHORT first_index = 0; for (const dsql_par* par = dsqlMsg->msg_parameters; par; par = par->par_next) { if (par->par_index) - { - if (!first_index) - first_index = par->par_index; - - if (first_index > par->par_index) - params.insert(0, par); - else - params.add(par); - } + params.add(par); } size_t msgLength = 0; @@ -183,6 +228,7 @@ void PreparedStatement::parseDsqlMessage(dsql_msg* dsqlMsg, Firebird::Array blr.add(blr_end); } + void PreparedStatement::generateBlr(const dsc* desc, Firebird::UCharBuffer& blr) { USHORT length = 0; diff --git a/src/jrd/PreparedStatement.h b/src/jrd/PreparedStatement.h index cdb678b16a..fab84040d7 100644 --- a/src/jrd/PreparedStatement.h +++ b/src/jrd/PreparedStatement.h @@ -23,11 +23,13 @@ #ifndef JRD_PREPARED_STATEMENT_H #define JRD_PREPARED_STATEMENT_H +#include "firebird.h" +#include "../jrd/common.h" +#include "../jrd/dsc.h" #include "../common/classes/alloc.h" #include "../common/classes/array.h" #include "../common/classes/fb_string.h" - -struct dsc; +#include "../common/classes/MetaName.h" namespace Jrd { @@ -49,10 +51,38 @@ public: ~PreparedStatement(); public: + void setDesc(thread_db* tdbb, int param, const dsc& value); + + void setNull(int param) + { + const dsc* desc = &inValues[(param - 1) * 2 + 1]; + fb_assert(desc->dsc_dtype == dtype_short); + *reinterpret_cast(desc->dsc_address) = -1; + } + + void setString(thread_db* tdbb, int param, const Firebird::AbstractString& value) + { + dsc desc; + desc.makeText((USHORT) value.length(), inValues[(param - 1) * 2].getTextType(), + (UCHAR*) value.c_str()); + + setDesc(tdbb, param, desc); + } + + void setString(thread_db* tdbb, int param, const Firebird::MetaName& value) + { + dsc desc; + desc.makeText((USHORT) value.length(), inValues[(param - 1) * 2].getTextType(), + (UCHAR*) value.c_str()); + + setDesc(tdbb, param, desc); + } + void execute(thread_db* tdbb, jrd_tra* transaction); ResultSet* executeQuery(thread_db* tdbb, jrd_tra* transaction); int getResultCount() const; + int getUpdateCount() const; dsql_req* getRequest() { @@ -60,15 +90,13 @@ public: } static void parseDsqlMessage(dsql_msg* dsqlMsg, Firebird::Array& values, Firebird::UCharBuffer& blr, Firebird::UCharBuffer& msg); - -private: static void generateBlr(const dsc* desc, Firebird::UCharBuffer& blr); private: dsql_req* request; - Firebird::Array values; - Firebird::UCharBuffer blr; - Firebird::UCharBuffer message; + Firebird::Array inValues, outValues; + Firebird::UCharBuffer inBlr, outBlr; + Firebird::UCharBuffer inMessage, outMessage; ResultSet* resultSet; }; diff --git a/src/jrd/ResultSet.cpp b/src/jrd/ResultSet.cpp index c9cf2904d6..5fbc718811 100644 --- a/src/jrd/ResultSet.cpp +++ b/src/jrd/ResultSet.cpp @@ -37,8 +37,7 @@ ResultSet::ResultSet(thread_db* tdbb, PreparedStatement* aStmt, jrd_tra* aTransa transaction(aTransaction), firstFetchDone(false) { - DSQL_execute(tdbb, &transaction, stmt->request, 0, NULL, 0, 0, NULL, 0, NULL, /*0,*/ 0, NULL); - + stmt->execute(tdbb, transaction); stmt->resultSet = this; } @@ -62,10 +61,10 @@ bool ResultSet::fetch(thread_db* tdbb) if (stmt->request->req_type == REQ_EXEC_PROCEDURE && firstFetchDone) return false; - memset(stmt->message.begin(), 0, stmt->message.getCount()); + memset(stmt->outMessage.begin(), 0, stmt->outMessage.getCount()); - ISC_STATUS status = DSQL_fetch(tdbb, stmt->request, stmt->blr.getCount(), stmt->blr.begin(), - /*0,*/ stmt->message.getCount(), stmt->message.begin()); + ISC_STATUS status = DSQL_fetch(tdbb, stmt->request, stmt->outBlr.getCount(), stmt->outBlr.begin(), + /*0,*/ stmt->outMessage.getCount(), stmt->outMessage.begin()); if (status == 100) return false; @@ -78,7 +77,7 @@ bool ResultSet::fetch(thread_db* tdbb) bool ResultSet::isNull(int param) const { - const dsc* desc = &stmt->values[(param - 1) * 2 + 1]; + const dsc* desc = &stmt->outValues[(param - 1) * 2 + 1]; fb_assert(desc->dsc_dtype == dtype_short); return *reinterpret_cast(desc->dsc_address) != 0; @@ -87,7 +86,7 @@ bool ResultSet::isNull(int param) const dsc& ResultSet::getDesc(int param) { - return stmt->values[(param - 1) * 2]; + return stmt->outValues[(param - 1) * 2]; } diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index bd3ca15a4d..9317dfd4cf 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -42,7 +42,12 @@ #include "../jrd/evl_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/mov_proto.h" +#include "../jrd/pag_proto.h" +#include "../jrd/tra_proto.h" #include "../jrd/os/guid.h" +#include "../jrd/license.h" +#include "../jrd/trace/TraceManager.h" +#include "../jrd/trace/TraceObjects.h" #include "../common/classes/FpeControl.h" #include @@ -108,6 +113,7 @@ void setParamsAsciiVal(DataTypeUtilBase* dataTypeUtil, const SysFunction* functi void setParamsCharToUuid(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsDateDiff(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); +void setParamsGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsPosition(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsRoundTrunc(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); @@ -128,6 +134,7 @@ void makeBin(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* r void makeBinShift(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeCeilFloor(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeMod(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -156,6 +163,8 @@ dsc* evlDateDiff(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod dsc* evlExp(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); dsc* evlFloor(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); dsc* evlGenUuid(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); +dsc* evlGetContext(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); +dsc* evlSetContext(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); dsc* evlHash(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); dsc* evlLeft(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); dsc* evlLnLog10(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); @@ -178,6 +187,40 @@ dsc* evlTrunc(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* a dsc* evlUuidToChar(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure); +// Context variable function names. Do not forget to change functions.h too if changing +const char + RDB_GET_CONTEXT[] = "RDB$GET_CONTEXT", + RDB_SET_CONTEXT[] = "RDB$SET_CONTEXT"; + +// Context namespace names +const char + SYSTEM_NAMESPACE[] = "SYSTEM", + DDL_TRIGGER_NAMESPACE[] = "DDL_TRIGGER", + USER_SESSION_NAMESPACE[] = "USER_SESSION", + USER_TRANSACTION_NAMESPACE[] = "USER_TRANSACTION"; + +// System context variables names +const char + ENGINE_VERSION[] = "ENGINE_VERSION", + NETWORK_PROTOCOL_NAME[] = "NETWORK_PROTOCOL", + CLIENT_ADDRESS_NAME[] = "CLIENT_ADDRESS", + DATABASE_NAME[] = "DB_NAME", + ISOLATION_LEVEL_NAME[] = "ISOLATION_LEVEL", + TRANSACTION_ID_NAME[] = "TRANSACTION_ID", + SESSION_ID_NAME[] = "SESSION_ID", + CURRENT_USER_NAME[] = "CURRENT_USER", + CURRENT_ROLE_NAME[] = "CURRENT_ROLE", + DDL_EVENT_NAME[] = "DDL_EVENT", + OBJECT_NAME[] = "OBJECT_NAME", + SQL_TEXT_NAME[] = "SQL_TEXT"; + +// Isolation values modes +const char + READ_COMMITTED_VALUE[] = "READ COMMITTED", + CONSISTENCY_VALUE[] = "CONSISTENCY", + SNAPSHOT_VALUE[] = "SNAPSHOT"; + + void add10msec(ISC_TIMESTAMP* v, int msec, SINT64 multiplier) { const SINT64 full = msec * multiplier; @@ -312,6 +355,20 @@ void setParamsDateDiff(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc } +void setParamsGetSetContext(DataTypeUtilBase*, const SysFunction* function, + int argsCount, dsc** args) +{ + if (argsCount >= 1 && args[0]->isUnknown()) + args[0]->makeText(80, ttype_none); + + if (argsCount >= 2 && args[1]->isUnknown()) + args[1]->makeText(80, ttype_none); + + if (argsCount >= 3 && args[2]->isUnknown()) + args[2]->makeText(255, ttype_none); +} + + void setParamsOverlay(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) { if (argsCount >= 3) @@ -648,6 +705,21 @@ void makeDateAdd(DataTypeUtilBase*, const SysFunction*, dsc* result, int argsCou } +void makeGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, + int argsCount, const dsc** args) +{ + fb_assert(argsCount == function->minArgCount); + + if (argsCount == 3) // set_context + result->makeLong(0); + else + { + result->makeVarying(255, ttype_none); + result->setNullable(true); + } +} + + void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args) { @@ -1967,6 +2039,259 @@ dsc* evlGenUuid(Jrd::thread_db* tdbb, const SysFunction*, Jrd::jrd_nod* args, } +dsc* evlGetContext(Jrd::thread_db* tdbb, const SysFunction*, Jrd::jrd_nod* args, + Jrd::impure_value* impure) +{ + fb_assert(args->nod_count == 2); + + Jrd::Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* transaction = tdbb->getTransaction(); + jrd_req* request = tdbb->getRequest(); + + request->req_flags &= ~req_null; + const dsc* nameSpace = EVL_expr(tdbb, args->nod_arg[0]); + if (request->req_flags & req_null) // Complain if namespace is null + ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_GET_CONTEXT)); + + const dsc* name = EVL_expr(tdbb, args->nod_arg[1]); + if (request->req_flags & req_null) // Complain if variable name is null + ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_GET_CONTEXT)); + + const string nameSpaceStr(MOV_make_string2(tdbb, nameSpace, ttype_none)); + const string nameStr(MOV_make_string2(tdbb, name, ttype_none)); + + string resultStr; + USHORT resultType = ttype_none; + request->req_flags |= req_null; + + if (nameSpaceStr == SYSTEM_NAMESPACE) // Handle system variables + { + if (nameStr == ENGINE_VERSION) + resultStr.printf("%s.%s.%s", FB_MAJOR_VER, FB_MINOR_VER, FB_REV_NO); + else if (nameStr == NETWORK_PROTOCOL_NAME) + { + if (attachment->att_network_protocol.isEmpty()) + return NULL; + + resultStr = attachment->att_network_protocol; + } + else if (nameStr == CLIENT_ADDRESS_NAME) + { + if (attachment->att_remote_address.isEmpty()) + return NULL; + + resultStr = attachment->att_remote_address; + } + else if (nameStr == DATABASE_NAME) + resultStr = dbb->dbb_database_name.ToString(); + else if (nameStr == CURRENT_USER_NAME) + { + if (!attachment->att_user || attachment->att_user->usr_user_name.isEmpty()) + return NULL; + + resultStr = attachment->att_user->usr_user_name; + } + else if (nameStr == CURRENT_ROLE_NAME) + { + if (!attachment->att_user || attachment->att_user->usr_sql_role_name.isEmpty()) + return NULL; + + resultStr = attachment->att_user->usr_sql_role_name; + } + else if (nameStr == SESSION_ID_NAME) + resultStr.printf("%d", PAG_attachment_id(tdbb)); + else if (nameStr == TRANSACTION_ID_NAME) + resultStr.printf("%d", transaction->tra_number); + else if (nameStr == ISOLATION_LEVEL_NAME) + { + if (transaction->tra_flags & TRA_read_committed) + resultStr = READ_COMMITTED_VALUE; + else if (transaction->tra_flags & TRA_degree3) + resultStr = CONSISTENCY_VALUE; + else + resultStr = SNAPSHOT_VALUE; + } + else + { + // "Context variable %s is not found in namespace %s" + ERR_post(Arg::Gds(isc_ctx_var_not_found) << Arg::Str(nameStr) << + Arg::Str(nameSpaceStr)); + } + } + else if (nameSpaceStr == DDL_TRIGGER_NAMESPACE) // Handle ddl trigger variables + { + if (!attachment->ddlTriggersContext.hasData()) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Invalid usage of context namespace DDL_TRIGGER")); + } + + const DdlTriggerContext& context = Stack::const_iterator( + attachment->ddlTriggersContext).object(); + + if (nameStr == DDL_EVENT_NAME) + resultStr = context.ddlEvent; + else if (nameStr == OBJECT_NAME) + { + resultStr = context.objectName.c_str(); + resultType = ttype_metadata; + } + else if (nameStr == SQL_TEXT_NAME) + { + if (context.sqlText.isEmpty()) + return NULL; + + blb* blob = BLB_create(tdbb, transaction, &impure->vlu_misc.vlu_bid); + BLB_put_data(tdbb, blob, reinterpret_cast(context.sqlText.c_str()), + context.sqlText.length()); + BLB_close(tdbb, blob); + + dsc result; + result.makeBlob(isc_blob_text, ttype_dynamic, (ISC_QUAD*) &impure->vlu_misc.vlu_bid); + EVL_make_value(tdbb, &result, impure); + + request->req_flags &= ~req_null; + return &impure->vlu_desc; + } + else + { + // "Context variable %s is not found in namespace %s" + ERR_post(Arg::Gds(isc_ctx_var_not_found) << Arg::Str(nameStr) << + Arg::Str(nameStr)); + } + } + else if (nameSpaceStr == USER_SESSION_NAMESPACE) // Handle user-defined session variables + { + if (!attachment->att_context_vars.get(nameStr, resultStr)) + return NULL; + } + else if (nameSpaceStr == USER_TRANSACTION_NAMESPACE) // Handle user-defined trans. variables + { + if (!transaction->tra_context_vars.get(nameStr, resultStr)) + return NULL; + } + else + { + // "Invalid namespace name %s passed to %s" + ERR_post(Arg::Gds(isc_ctx_namespace_invalid) << + Arg::Str(nameSpaceStr) << Arg::Str(RDB_GET_CONTEXT)); + } + + dsc result; + result.makeText(resultStr.length(), resultType, + (UCHAR*) const_cast(resultStr.c_str())); // safe const_cast + EVL_make_value(tdbb, &result, impure); + + request->req_flags &= ~req_null; + return &impure->vlu_desc; +} + + +dsc* evlSetContext(Jrd::thread_db* tdbb, const SysFunction*, Jrd::jrd_nod* args, + Jrd::impure_value* impure) +{ + fb_assert(args->nod_count == 3); + + Jrd::Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* transaction = tdbb->getTransaction(); + jrd_req* request = tdbb->getRequest(); + + request->req_flags &= ~req_null; + const dsc* nameSpace = EVL_expr(tdbb, args->nod_arg[0]); + if (request->req_flags & req_null) // Complain if namespace is null + ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_GET_CONTEXT)); + + const dsc* name = EVL_expr(tdbb, args->nod_arg[1]); + if (request->req_flags & req_null) // Complain if variable name is null + ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_GET_CONTEXT)); + + const dsc* value = EVL_expr(tdbb, args->nod_arg[2]); + + const string nameSpaceStr(MOV_make_string2(tdbb, nameSpace, ttype_none)); + const string nameStr(MOV_make_string2(tdbb, name, ttype_none)); + + impure->vlu_desc.makeLong(0, &impure->vlu_misc.vlu_long); + + Firebird::StringMap* contextVars = NULL; + + if (nameSpaceStr == USER_SESSION_NAMESPACE) + { + if (!attachment) + { + fb_assert(false); + return 0; + } + + contextVars = &attachment->att_context_vars; + } + else if (nameSpaceStr == USER_TRANSACTION_NAMESPACE) + { + if (!transaction) + { + fb_assert(false); + return 0; + } + + contextVars = &transaction->tra_context_vars; + } + else + { + // "Invalid namespace name %s passed to %s" + ERR_post(Arg::Gds(isc_ctx_namespace_invalid) << + Arg::Str(nameStr) << Arg::Str(RDB_SET_CONTEXT)); + } + + string valueStr; + + if (!value) + impure->vlu_misc.vlu_long = (SLONG) contextVars->remove(nameStr); + else + { + valueStr = MOV_make_string2(tdbb, value, ttype_none); + + if (contextVars->count() == MAX_CONTEXT_VARS) + { + string* rc = contextVars->get(nameStr); + if (rc) + { + *rc = valueStr; + impure->vlu_misc.vlu_long = 1; + } + else + ERR_post(Arg::Gds(isc_ctx_too_big)); // "Too many context variables" + } + else + { + if (contextVars->count() >= MAX_CONTEXT_VARS) + { + // "Too many context variables" + ERR_post(Arg::Gds(isc_ctx_too_big)); + } + + impure->vlu_misc.vlu_long = (SLONG) contextVars->put(nameStr, valueStr); + } + } + + if (attachment->att_trace_manager->needs().event_set_context) + { + TraceConnectionImpl conn(attachment); + TraceTransactionImpl tran(transaction); + + TraceContextVarImpl ctxvar(nameSpaceStr.c_str(), nameStr.c_str(), + (value ? valueStr.c_str() : NULL)); + + attachment->att_trace_manager->event_set_context(&conn, &tran, &ctxvar); + } + + request->req_flags &= ~req_null; + return &impure->vlu_desc; +} + + dsc* evlHash(Jrd::thread_db* tdbb, const SysFunction*, Jrd::jrd_nod* args, Jrd::impure_value* impure) { @@ -3326,6 +3651,8 @@ const SysFunction SysFunction::functions[] = {"POSITION", 2, 3, setParamsPosition, makeLongResult, evlPosition, NULL}, {"POWER", 2, 2, setParamsDouble, makeDoubleResult, evlPower, NULL}, {"RAND", 0, 0, NULL, makeDoubleResult, evlRand, NULL}, + {RDB_GET_CONTEXT, 2, 2, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, + {RDB_SET_CONTEXT, 3, 3, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, {"REPLACE", 3, 3, setParamsFromList, makeReplace, evlReplace, NULL}, {"REVERSE", 1, 1, NULL, makeReverse, evlReverse, NULL}, {"RIGHT", 2, 2, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, diff --git a/src/jrd/ValueImpl.cpp b/src/jrd/ValueImpl.cpp new file mode 100644 index 0000000000..362ba199cd --- /dev/null +++ b/src/jrd/ValueImpl.cpp @@ -0,0 +1,689 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/ValueImpl.h" +#include "../jrd/ErrorImpl.h" +#include "../jrd/intl_classes.h" +#include "../jrd/DataTypeUtil.h" +#include "../jrd/intl_proto.h" +#include "../jrd/mov_proto.h" + +using namespace Firebird; +using Firebird::uint; + +namespace Jrd { + + +USHORT ValueMover::getClientCharSet() const +{ + thread_db* tdbb = getThreadData(); + return tdbb->getAttachment()->att_charset; +} + + +void ValueMover::getClientCharSetName(Firebird::MetaName& name) const +{ + thread_db* tdbb = getThreadData(); + Database::SyncGuard dsGuard(tdbb->getDatabase()); + + CharSet* cs = INTL_charset_lookup(tdbb, tdbb->getAttachment()->att_charset); + name = cs->getName(); +} + + +int ValueMover::getMaxBytesPerChar(USHORT charSet) const +{ + thread_db* tdbb = getThreadData(); + Database::SyncGuard dsGuard(tdbb->getDatabase()); + + return DataTypeUtil(tdbb).maxBytesPerChar(charSet); +} + + +const char* ValueMover::getString(const ValueImpl* value, const dsc* desc, MoveBuffer& buffer, + uint* strLength, bool* isNull) const +{ + if (value->isNull()) + { + if (isNull) + *isNull = true; + + return NULL; + } + + if (isNull) + *isNull = false; + + thread_db* tdbb = getThreadData(); + Database::SyncGuard dsGuard(tdbb->getDatabase()); + USHORT charSet = tdbb->getAttachment()->att_charset; + + UCHAR* temp = NULL; + int len = MOV_make_string2(tdbb, desc, charSet, &temp, buffer, false); + + if (temp == buffer.begin()) + buffer.resize(len + 1); + else + memcpy(buffer.getBuffer(len + 1), temp, len); + + buffer[len] = '\0'; + + if (strLength) + *strLength = len; + + return (const char*) buffer.begin(); +} + + +void ValueMover::getValue(const ValueImpl* value, const dsc* desc, dsc* target, bool* isNull) const +{ + if (value->isNull()) + { + if (isNull) + *isNull = true; + else + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Value is NULL and isNull parameter was not informed")); + } + } + else + { + if (isNull) + *isNull = false; + + thread_db* tdbb = getThreadData(); + Database::SyncGuard dsGuard(tdbb->getDatabase()); + + MOV_move(tdbb, const_cast(desc), target); + } +} + + +void ValueMover::setValue(dsc* desc, USHORT* nullFlag, dsc* from) const +{ + thread_db* tdbb = getThreadData(); + Database::SyncGuard dsGuard(tdbb->getDatabase()); + + MOV_move(tdbb, from, desc); + *nullFlag &= ~DSC_null; +} + + +//------------------------------------- + + +ValueImpl::ValueImpl(const dsc* aDesc, const Firebird::MetaName& aName, bool aNullable) + : name(*getDefaultMemoryPool()), + fieldNumber(0), + buffer(*getDefaultMemoryPool()), + charSetName(*getDefaultMemoryPool()) +{ + make(aDesc, aName, aNullable); +} + + +ValueImpl::ValueImpl(const Format* format, unsigned index, UCHAR* msg, + const Firebird::MetaName& aName, bool aNullable) + : name(*getDefaultMemoryPool()), + fieldNumber(0), + buffer(*getDefaultMemoryPool()), + charSetName(*getDefaultMemoryPool()) +{ + make(format, index, msg, aName, aNullable); +} + + +ValueImpl::ValueImpl() + : nullFlag(NULL), + name(*getDefaultMemoryPool()), + buffer(*getDefaultMemoryPool()) +{ + desc.clear(); +} + + +void ValueImpl::make(const dsc* aDesc, const Firebird::MetaName& aName, bool aNullable, + USHORT aFieldNumber) +{ + desc = *aDesc; + nullFlag = &desc.dsc_flags; + + name = aName; + nullable = aNullable; + fieldNumber = aFieldNumber; +} + + +void ValueImpl::make(const Format* format, unsigned index, UCHAR* msg, + const Firebird::MetaName& aName, bool aNullable, USHORT aFieldNumber) +{ + desc = format->fmt_desc[index * 2]; + desc.dsc_address = msg + (IPTR) desc.dsc_address; + + const dsc* nullDesc = &format->fmt_desc[index * 2 + 1]; + fb_assert(nullDesc->dsc_dtype == dtype_short); + nullFlag = (USHORT*) (msg + (IPTR) nullDesc->dsc_address); + + name = aName; + nullable = aNullable; + fieldNumber = aFieldNumber; +} + + +const char* FB_CALL ValueImpl::getName(Error* error) const +{ + return name.c_str(); +} + + +Value::Type FB_CALL ValueImpl::getType(Error* error) const +{ + switch (desc.dsc_dtype) + { + case dtype_byte: + case dtype_short: + return TYPE_SMALLINT; + + case dtype_long: + return TYPE_INTEGER; + + case dtype_int64: + return TYPE_BIGINT; + + case dtype_real: + case dtype_double: + case dtype_d_float: + return TYPE_DOUBLE; + + case dtype_text: + return TYPE_CHAR; + + case dtype_cstring: + case dtype_varying: + return TYPE_VARCHAR; + + case dtype_blob: + case dtype_quad: + return TYPE_BLOB; + + case dtype_sql_date: + return TYPE_DATE; + + case dtype_sql_time: + return TYPE_TIME; + + case dtype_timestamp: + return TYPE_TIMESTAMP; + + default: + ISC_STATUS status[] = {isc_arg_gds, isc_wish_list, 0}; + ErrorImpl::statusVectorToError(status, error); + return (Type) 0; + } +} + + +const char* FB_CALL ValueImpl::getCharSet(Error* error) const +{ + if ((desc.isText() || desc.isBlob()) && charSetName.isEmpty()) + mover.getClientCharSetName(charSetName); + + return charSetName.c_str(); +} + + +int FB_CALL ValueImpl::getSubType(Error* error) const +{ + if (desc.isBlob()) + return desc.getBlobSubType(); + else + return 0; +} + + +int FB_CALL ValueImpl::getPrecision(Error* error) const +{ + try + { + if (desc.isText()) + return desc.getStringLength() / mover.getMaxBytesPerChar(desc.getCharSet()); + + return 0; + + + } + catch (const Exception& e) + { + ErrorImpl::exceptionToError(e, error); + } + + return 0; +} + + +int FB_CALL ValueImpl::getScale(Error* error) const +{ + return desc.dsc_scale; +} + + +bool FB_CALL ValueImpl::isNullable(Error* error) const +{ + return nullable; +} + + +void FB_CALL ValueImpl::setNull(Error* error) +{ + if (isNullable(error)) + setNull(); + else + { + string text; + text.printf("parameter %s", name.c_str()); + + ISC_STATUS status[] = { + isc_arg_gds, isc_not_valid_for, + isc_arg_string, (ISC_STATUS) text.c_str(), + isc_arg_string, (ISC_STATUS) NULL_STRING_MARK, + 0}; + ErrorImpl::statusVectorToError(status, error); + } +} + + +void FB_CALL ValueImpl::copyFrom(Error* error, const Value* from) +{ + if (from->isNull()) + setNull(error); + else + { + switch (from->getType(error)) + { + case TYPE_SMALLINT: + case TYPE_INTEGER: + case TYPE_BIGINT: + { + DelegateError err(error); + int scale = from->getScale(error); + int64 value = from->getBigInt(&err, scale); + if (!err.isRaised()) + setBigInt(error, value, scale); + break; + } + + case TYPE_DOUBLE: + { + DelegateError err(error); + double value = from->getDouble(&err); + if (!err.isRaised()) + setDouble(error, value); + break; + } + + case TYPE_BLOB: + { + DelegateError err(error); + int64 id = from->getBlobId(&err); + if (!err.isRaised()) + setBlobId(error, id); + break; + } + + case TYPE_DATE: + { + DelegateError err(error); + Date value; + from->getDate(&err, &value); + if (!err.isRaised()) + setDate(error, &value); + break; + } + + case TYPE_TIME: + { + DelegateError err(error); + Time value; + from->getTime(&err, &value); + if (!err.isRaised()) + setTime(error, &value); + break; + } + + case TYPE_TIMESTAMP: + { + DelegateError err(error); + DateTime value; + from->getTimeStamp(&err, &value); + if (!err.isRaised()) + setTimeStamp(error, &value); + break; + } + + case TYPE_CHAR: + case TYPE_VARCHAR: + default: + { + uint len; + const char* p = from->getString(error, &len); + if (p) + setString(error, p, len); + break; + } + } + } +} + + +int16 FB_CALL ValueImpl::getSmallInt(Error* error, int scale, bool* isNull) const +{ + SSHORT value = 0; + dsc target; + target.makeShort(scale, &value); + getValue(error, &target, isNull); + return value; +} + + +void FB_CALL ValueImpl::setSmallInt(Error* error, int16 value, int scale) +{ + SSHORT val = value; + dsc from; + from.makeShort(scale, &val); + setValue(error, &from); +} + + +int32 FB_CALL ValueImpl::getInt(Error* error, int scale, bool* isNull) const +{ + SLONG value = 0; + dsc target; + target.makeLong(scale, &value); + getValue(error, &target, isNull); + return value; +} + + +void FB_CALL ValueImpl::setInt(Error* error, int32 value, int scale) +{ + SLONG val = value; + dsc from; + from.makeLong(scale, &val); + setValue(error, &from); +} + + +int64 FB_CALL ValueImpl::getBigInt(Error* error, int scale, bool* isNull) const +{ + SINT64 value = 0; + dsc target; + target.makeInt64(scale, &value); + getValue(error, &target, isNull); + return value; +} + + +void FB_CALL ValueImpl::setBigInt(Error* error, int64 value, int scale) +{ + dsc from; + from.makeInt64(scale, &value); + setValue(error, &from); +} + + +double FB_CALL ValueImpl::getDouble(Error* error, bool* isNull) const +{ + double value = 0; + dsc target; + target.makeDouble(&value); + getValue(error, &target, isNull); + return value; +} + + +void FB_CALL ValueImpl::setDouble(Error* error, double value) +{ + dsc from; + from.makeDouble(&value); + setValue(error, &from); +} + + +const char* FB_CALL ValueImpl::getString(Error* error, uint* strLength, bool* isNull) const +{ + return mover.getString(this, &desc, buffer, strLength, isNull); +} + + +void FB_CALL ValueImpl::setString(Error* error, const char* str, uint strLength) +{ + dsc from; + from.makeText(strLength, mover.getClientCharSet(), (UCHAR*) str); + setValue(error, &from); +} + + +Firebird::int64 FB_CALL ValueImpl::getBlobId(Error* error, bool* isNull) const +{ + if (!desc.isBlob()) + { + //// TODO: localize + ISC_STATUS status[] = { + isc_arg_gds, isc_random, + isc_arg_string, (ISC_STATUS) "Incompatible type", + 0}; + ErrorImpl::statusVectorToError(status, error); + return 0; + } + + int64 blobId; + + if (this->isNull()) + blobId = 0; + else + { + ISC_QUAD quad = *(ISC_QUAD*) desc.dsc_address; + blobId = quad.gds_quad_low | ((int64) quad.gds_quad_high << 32); + } + + if (isNull) + *isNull = blobId == 0; + + return blobId; +} + + +void FB_CALL ValueImpl::getDate(Error* error, Date* value, bool* isNull) const +{ + DelegateError err(error); + + GDS_DATE gdsDate; + dsc target; + target.makeDate(&gdsDate); + getValue(&err, &target, isNull); + + if (!err.isRaised()) + { + tm t; + TimeStamp::decode_date(gdsDate, &t); + + value->year = t.tm_year + 1900; + value->month = t.tm_mon + 1; + value->day = t.tm_mday; + } +} + + +void FB_CALL ValueImpl::setDate(Error* error, const Date* value) +{ + tm t; + t.tm_year = value->year - 1900; + t.tm_mon = value->month - 1; + t.tm_mday = value->day; + + GDS_DATE gdsDate = TimeStamp::encode_date(&t); + + tm t2; + TimeStamp::decode_date(gdsDate, &t2); + + if (t2.tm_year != t.tm_year || t2.tm_mon != t.tm_mon || t2.tm_mday != t.tm_mday) + { + //// TODO: localize + ISC_STATUS status[] = { + isc_arg_gds, isc_random, + isc_arg_string, (ISC_STATUS) "Invalid date", + 0}; + ErrorImpl::statusVectorToError(status, error); + return; + } + + dsc from; + from.makeDate(&gdsDate); + setValue(error, &from); +} + + +void FB_CALL ValueImpl::getTime(Error* error, Time* value, bool* isNull) const +{ + DelegateError err(error); + + GDS_TIME gdsTime; + dsc target; + target.makeTime(&gdsTime); + getValue(&err, &target, isNull); + + if (!err.isRaised()) + { + TimeStamp::decode_time(gdsTime, &value->hours, &value->minutes, + &value->seconds, &value->fractions); + } +} + + +void FB_CALL ValueImpl::setTime(Error* error, const Time* value) +{ + GDS_TIME gdsTime; + gdsTime = TimeStamp::encode_time(value->hours, value->minutes, value->seconds, value->fractions); + + Time t2; + TimeStamp::decode_time(gdsTime, &t2.hours, &t2.minutes, &t2.seconds, &t2.fractions); + if (t2.hours != value->hours || t2.minutes != value->minutes || + t2.seconds != value->seconds || t2.fractions != value->fractions) + { + //// TODO: localize + ISC_STATUS status[] = { + isc_arg_gds, isc_random, + isc_arg_string, (ISC_STATUS) "Invalid time", + 0}; + ErrorImpl::statusVectorToError(status, error); + return; + } + + dsc from; + from.makeTime(&gdsTime); + setValue(error, &from); +} + + +void FB_CALL ValueImpl::getTimeStamp(Error* error, DateTime* value, bool* isNull) const +{ + DelegateError err(error); + + GDS_TIMESTAMP gdsTimeStamp; + dsc target; + target.makeTimestamp(&gdsTimeStamp); + getValue(&err, &target, isNull); + + if (!err.isRaised()) + { + tm t; + TimeStamp::decode_date(gdsTimeStamp.timestamp_date, &t); + TimeStamp::decode_time(gdsTimeStamp.timestamp_time, &value->time.hours, + &value->time.minutes, &value->time.seconds, &value->time.fractions); + + value->date.year = t.tm_year + 1900; + value->date.month = t.tm_mon + 1; + value->date.day = t.tm_mday; + } +} + + +void FB_CALL ValueImpl::setTimeStamp(Error* error, const DateTime* value) +{ + tm t; + t.tm_year = value->date.year - 1900; + t.tm_mon = value->date.month - 1; + t.tm_mday = value->date.day; + + GDS_TIMESTAMP gdsTimeStamp; + gdsTimeStamp.timestamp_date = TimeStamp::encode_date(&t); + gdsTimeStamp.timestamp_time = TimeStamp::encode_time(value->time.hours, + value->time.minutes, value->time.seconds, value->time.fractions); + + DateTime timeStamp2; + TimeStamp::decode_time(gdsTimeStamp.timestamp_time, &timeStamp2.time.hours, + &timeStamp2.time.minutes, &timeStamp2.time.seconds, &timeStamp2.time.fractions); + + tm t2; + TimeStamp(gdsTimeStamp).decode(&t2); + + if (timeStamp2.time.hours != value->time.hours || + timeStamp2.time.minutes != value->time.minutes || + timeStamp2.time.seconds != value->time.seconds || + timeStamp2.time.fractions != value->time.fractions || + t2.tm_year != t.tm_year || + t2.tm_mon != t.tm_mon || + t2.tm_mday != t.tm_mday) + { + //// TODO: localize + ISC_STATUS status[] = { + isc_arg_gds, isc_random, + isc_arg_string, (ISC_STATUS) "Invalid timestamp", + 0}; + ErrorImpl::statusVectorToError(status, error); + return; + } + + dsc from; + from.makeTimestamp(&gdsTimeStamp); + setValue(error, &from); +} + + +void FB_CALL ValueImpl::setBlobId(Error* error, Firebird::int64 value) +{ + bid bid; + bid.bid_quad.bid_quad_low = value; + bid.bid_quad.bid_quad_high = value >> 32; + + dsc from; + from.makeBlob(desc.getBlobSubType(), mover.getClientCharSet(), reinterpret_cast(&bid)); + + setValue(error, &from); +} + + +} // namespace Jrd diff --git a/src/jrd/ValueImpl.h b/src/jrd/ValueImpl.h new file mode 100644 index 0000000000..8172eab383 --- /dev/null +++ b/src/jrd/ValueImpl.h @@ -0,0 +1,190 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_VALUE_IMPL_H +#define JRD_VALUE_IMPL_H + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "../jrd/common.h" +#include "../jrd/dsc.h" +#include "../jrd/jrd.h" +#include "../jrd/ErrorImpl.h" + +namespace Jrd { + +class ValueImpl; + + +class ValueMover +{ +public: + USHORT getClientCharSet() const; + void getClientCharSetName(Firebird::MetaName& name) const; + int getMaxBytesPerChar(USHORT charSet) const; + + const char* getString(const ValueImpl* value, const dsc* desc, MoveBuffer& buffer, + Firebird::uint* strLength, bool* isNull) const; + + void getValue(const ValueImpl* value, const dsc* desc, dsc* target, bool* isNull) const; + void setValue(dsc* desc, USHORT* nullFlag, dsc* from) const; + + static inline thread_db* getThreadData() + { + return JRD_get_thread_data(); + } +}; + + +class ValueImpl : public Firebird::Value +{ +public: + ValueImpl(const dsc* aDesc, const Firebird::MetaName& aName, bool aNullable); + ValueImpl(const Format* format, unsigned index, UCHAR* msg, const Firebird::MetaName& aName, + bool aNullable); + ValueImpl(); + + virtual ~ValueImpl() + { + } + +private: + // copying is prohibited + ValueImpl(const ValueImpl&); + ValueImpl& operator =(const ValueImpl&); + +public: + // used in triggers code + USHORT getFieldNumber() const + { + return fieldNumber; + } + + dsc& getDesc() + { + return desc; + } + + void setNull() + { + *nullFlag |= DSC_null; + } + + void make(const dsc* aDesc, const Firebird::MetaName& aName, bool aNullable, + USHORT aFieldNumber = 0); + void make(const Format* format, unsigned index, UCHAR* msg, const Firebird::MetaName& aName, + bool aNullable, USHORT aFieldNumber = 0); + +public: + virtual const char* FB_CALL getName(Firebird::Error* error) const; + virtual Type FB_CALL getType(Firebird::Error* error) const; + virtual const char* FB_CALL getCharSet(Firebird::Error* error) const; + virtual int FB_CALL getSubType(Firebird::Error* error) const; + virtual int FB_CALL getPrecision(Firebird::Error* error) const; + virtual int FB_CALL getScale(Firebird::Error* error) const; + virtual bool FB_CALL isNullable(Firebird::Error* error) const; + + virtual bool FB_CALL isNull() const + { + return *nullFlag & DSC_null; + } + + virtual void FB_CALL setNull(Firebird::Error* error); + + virtual void FB_CALL copyFrom(Firebird::Error* error, const Firebird::Value* from); + + virtual Firebird::int16 FB_CALL getSmallInt(Firebird::Error* error, int scale = 0, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setSmallInt(Firebird::Error* error, Firebird::int16 value, int scale = 0); + + virtual Firebird::int32 FB_CALL getInt(Firebird::Error* error, int scale = 0, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setInt(Firebird::Error* error, Firebird::int32 value, int scale = 0); + + virtual Firebird::int64 FB_CALL getBigInt(Firebird::Error* error, int scale = 0, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setBigInt(Firebird::Error* error, Firebird::int64 value, int scale = 0); + + virtual double FB_CALL getDouble(Firebird::Error* error, bool* isNull = FB_NULL) const; + virtual void FB_CALL setDouble(Firebird::Error* error, double value); + + virtual const char* FB_CALL getString(Firebird::Error* error, + Firebird::uint* strLength = FB_NULL, bool* isNull = FB_NULL) const; + virtual void FB_CALL setString(Firebird::Error* error, const char* str, + Firebird::uint strLength); + + virtual Firebird::int64 FB_CALL getBlobId(Firebird::Error* error, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setBlobId(Firebird::Error* error, Firebird::int64 value); + + virtual void FB_CALL getDate(Firebird::Error* error, Firebird::Date* value, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setDate(Firebird::Error* error, const Firebird::Date* value); + + virtual void FB_CALL getTime(Firebird::Error* error, Firebird::Time* value, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setTime(Firebird::Error* error, const Firebird::Time* value); + + virtual void FB_CALL getTimeStamp(Firebird::Error* error, Firebird::DateTime* value, + bool* isNull = FB_NULL) const; + virtual void FB_CALL setTimeStamp(Firebird::Error* error, const Firebird::DateTime* value); + +private: + void getValue(Firebird::Error* error, dsc* target, bool* isNull) const + { + try + { + mover.getValue(this, &desc, target, isNull); + } + catch (const Firebird::Exception& ex) + { + ErrorImpl::exceptionToError(ex, error); + } + } + + void setValue(Firebird::Error* error, dsc* from) + { + try + { + mover.setValue(&desc, nullFlag, from); + } + catch (const Firebird::Exception& ex) + { + ErrorImpl::exceptionToError(ex, error); + } + } + +private: + dsc desc; + USHORT* nullFlag; + Firebird::MetaName name; + bool nullable; + USHORT fieldNumber; + mutable MoveBuffer buffer; + mutable Firebird::MetaName charSetName; + ValueMover mover; +}; + + +} // namespace Jrd + +#endif // JRD_VALUE_IMPL_H diff --git a/src/jrd/ValuesImpl.cpp b/src/jrd/ValuesImpl.cpp new file mode 100644 index 0000000000..876411c51a --- /dev/null +++ b/src/jrd/ValuesImpl.cpp @@ -0,0 +1,238 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/ValuesImpl.h" +#include "../jrd/ErrorImpl.h" +#include "../jrd/mov_proto.h" +#include "../jrd/align.h" + +using namespace Firebird; +using Firebird::uint; + +namespace Jrd { + + +ValuesImpl::IndividualQueue::IndividualQueue(Error* error, ValuesImpl* aValues) + : pool(*getDefaultMemoryPool()), //// TODO: pass the pool in the callers + values(aValues), + valueCount(values->getCount()), + recordSize(0), + records(pool), + enqueuePos(0), + recordNumber(0) +{ + for (uint i = 1; i <= valueCount; ++i) + { + ValueImpl* value = values->getValue(i); + const dsc& desc = value->getDesc(); + + USHORT align = type_alignments[desc.dsc_dtype]; + if (align) + recordSize = FB_ALIGN(recordSize, align); + + recordSize += desc.dsc_length; + } + + nullsStart = recordSize; + recordSize += valueCount / 8 + 1; +} + + +ValuesImpl::IndividualQueue::~IndividualQueue() +{ + for (Array::iterator end = records.end(), i = records.begin(); i != end; ++i) + delete [] *i; +} + + +void FB_CALL ValuesImpl::IndividualQueue::enqueue(Error* error) +{ + thread_db* tdbb = JRD_get_thread_data(); + size_t recordCount = records.getCount(); + UCHAR* buffer = (enqueuePos < recordCount ? + records[enqueuePos] : FB_NEW(pool) UCHAR[recordSize]); + UCHAR* nullsBuffer = buffer + nullsStart; + uint pos = 0; + + for (uint i = 1; i <= valueCount; ++i) + { + ValueImpl* value = values->getValue(i); + const dsc& desc = value->getDesc(); + + USHORT align = type_alignments[desc.dsc_dtype]; + if (align) + pos = FB_ALIGN(pos, align); + + if (value->isNull()) + nullsBuffer[(i - 1) >> 3] |= 1 << ((i - 1) & 7); + else + { + nullsBuffer[(i - 1) >> 3] &= ~(1 << ((i - 1) & 7)); + memcpy(buffer + pos, desc.dsc_address, desc.dsc_length); + } + + pos += desc.dsc_length; + } + + if (enqueuePos++ >= recordCount) + records.push(buffer); +} + + +bool FB_CALL ValuesImpl::IndividualQueue::dequeue(Error* error) +{ + if (recordNumber == enqueuePos) + return false; + + const UCHAR* buffer = records[recordNumber++]; + const UCHAR* nullsBuffer = buffer + nullsStart; + uint pos = 0; + + for (uint i = 1; i <= valueCount; ++i) + { + ValueImpl* value = values->getValue(i); + dsc& desc = value->getDesc(); + + USHORT align = type_alignments[desc.dsc_dtype]; + if (align) + pos = FB_ALIGN(pos, align); + + if (nullsBuffer[(i - 1) >> 3] & (1 << ((i - 1) & 7))) + desc.dsc_flags |= DSC_null; + else + { + memcpy(desc.dsc_address, buffer + pos, desc.dsc_length); + desc.dsc_flags &= ~DSC_null; + } + + pos += desc.dsc_length; + } + + if (recordNumber == enqueuePos) + { + recordNumber = 0; + enqueuePos = 0; + } + + return true; +} + + +//--------------------- + + +ValuesImpl::MsgQueue::MsgQueue(Error* error, UCHAR* aMsg, unsigned aMsgLength) + : pool(*getDefaultMemoryPool()), //// TODO: pass the pool in the callers + msg(aMsg), + msgLength(aMsgLength), + records(pool), + enqueuePos(0), + recordNumber(0) +{ +} + + +ValuesImpl::MsgQueue::~MsgQueue() +{ + for (Array::iterator end = records.end(), i = records.begin(); i != end; ++i) + delete [] *i; +} + + +void FB_CALL ValuesImpl::MsgQueue::enqueue(Error* error) +{ + thread_db* tdbb = JRD_get_thread_data(); + size_t recordCount = records.getCount(); + UCHAR* buffer = (enqueuePos < recordCount ? + records[enqueuePos] : FB_NEW(pool) UCHAR[msgLength]); + + memcpy(buffer, msg, msgLength); + + if (enqueuePos++ >= recordCount) + records.push(buffer); +} + + +bool FB_CALL ValuesImpl::MsgQueue::dequeue(Error* error) +{ + if (recordNumber == enqueuePos) + return false; + + const UCHAR* buffer = records[recordNumber++]; + memcpy(msg, buffer, msgLength); + + if (recordNumber == enqueuePos) + { + recordNumber = 0; + enqueuePos = 0; + } + + return true; +} + + +//--------------------- + + +Firebird::uint FB_CALL ValuesImpl::getIndexByName(Error* error, const char* name) const +{ + string nameStr(name); + + for (unsigned i = 0; i < count; ++i) + { + const char* valName = values[i].getName(error); + + if (valName) + { + if (nameStr == valName) + return i + 1; + } + else + break; + } + + return 0; +} + + +Value* FB_CALL ValuesImpl::getValueByName(Error* error, const char* name) const +{ + int index = getIndexByName(error, name); + + if (index > 0) + return getValue(error, index); + else + return NULL; +} + + +ValuesQueue* FB_CALL ValuesImpl::createQueue(Error* error) +{ + if (msg) + return new MsgQueue(error, msg, msgLength); + else + return new IndividualQueue(error, this); +} + + +} // namespace Jrd diff --git a/src/jrd/ValuesImpl.h b/src/jrd/ValuesImpl.h new file mode 100644 index 0000000000..fbd79ff92d --- /dev/null +++ b/src/jrd/ValuesImpl.h @@ -0,0 +1,172 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_VALUES_IMPL_H +#define JRD_VALUES_IMPL_H + +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "../jrd/ValueImpl.h" +#include "../common/classes/auto.h" + +namespace Jrd { + + +class ValuesImpl : public Firebird::Values +{ +private: + // ValuesQueue used for Values composed by independent fields. This is slower. + class IndividualQueue : public Firebird::ValuesQueue + { + public: + IndividualQueue(Firebird::Error* error, ValuesImpl* aValues); + virtual ~IndividualQueue(); + + public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + + virtual void FB_CALL enqueue(Firebird::Error* error); + virtual bool FB_CALL dequeue(Firebird::Error* error); + + private: + MemoryPool& pool; + ValuesImpl* values; + Firebird::uint valueCount; + Firebird::uint recordSize; + Firebird::Array records; + Firebird::uint enqueuePos; + Firebird::uint recordNumber; + Firebird::uint nullsStart; + }; + + // ValuesQueue used for Values composed by fields of a message. This is faster. + class MsgQueue : public Firebird::ValuesQueue + { + public: + MsgQueue(Firebird::Error* error, UCHAR* aMsg, unsigned aMsgLength); + virtual ~MsgQueue(); + + public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + + virtual void FB_CALL enqueue(Firebird::Error* error); + virtual bool FB_CALL dequeue(Firebird::Error* error); + + private: + MemoryPool& pool; + UCHAR* msg; + unsigned msgLength; + Firebird::Array records; + Firebird::uint enqueuePos; + Firebird::uint recordNumber; + }; + +public: + ValuesImpl(Firebird::uint aCount) + : msg(NULL), + msgLength(0), + count(aCount) + { + autoValues = values = FB_NEW(*getDefaultMemoryPool()) ValueImpl[count]; + } + + ValuesImpl(thread_db* tdbb, const Format* format, UCHAR* aMsg, const vec& parameters) + : msg(aMsg), + msgLength(format->fmt_length), + count(format->fmt_count / 2) + { + autoValues = values = FB_NEW(*getDefaultMemoryPool()) ValueImpl[count]; + + for (unsigned i = 0; i < count; ++i) + { + const Parameter* parameter = parameters[i]; + values[i].make(format, i, msg, parameter->prm_name, parameter->prm_nullable); + } + } + + virtual ~ValuesImpl() + { + } + + +private: + // copying is prohibited + ValuesImpl(const ValuesImpl&); + ValuesImpl& operator =(const ValuesImpl&); + +public: + Jrd::ValueImpl* getValue(Firebird::uint index) const + { + fb_assert(index >= 1 && index <= count); + return &values[index - 1]; + } + + void setNull() + { + for (unsigned i = 0; i < count; ++i) + values[i].setNull(); + } + +public: + virtual Firebird::uint FB_CALL getCount() const + { + return count; + } + + virtual Firebird::uint FB_CALL getIndexByName(Firebird::Error* error, const char* name) const; + + virtual Jrd::ValueImpl* FB_CALL getValue(Firebird::Error* error, Firebird::uint index) const + { + if (index >= 1 && index <= count) + return getValue(index); + else + { + //// TODO: localize + const static char* const msg = "Invalid index"; + error->addString(msg, strlen(msg)); + return NULL; + } + } + + virtual Firebird::Value* FB_CALL getValueByName(Firebird::Error* error, + const char* name) const; + + virtual Firebird::ValuesQueue* FB_CALL createQueue(Firebird::Error* error); + +private: + UCHAR* msg; + unsigned msgLength; + Firebird::AutoPtr > autoValues; + ValueImpl* values; + Firebird::uint count; +}; + + +} // namespace Jrd + +#endif // JRD_VALUES_IMPL_H diff --git a/src/jrd/VirtualTable.cpp b/src/jrd/VirtualTable.cpp index 19e3c8ce53..e367698cf7 100644 --- a/src/jrd/VirtualTable.cpp +++ b/src/jrd/VirtualTable.cpp @@ -18,6 +18,7 @@ * * All Rights Reserved. * Contributor(s): ______________________________________. + * Adriano dos Santos Fernandes */ #include "firebird.h" @@ -47,13 +48,21 @@ using namespace Jrd; using namespace Firebird; -void VirtualTable::close(thread_db* tdbb, RecordSource* rsb) +RecordSource* VirtualTable::create(thread_db* tdbb, OptimizerBlk* opt, SSHORT stream) { SET_TDBB(tdbb); - irsb_virtual* impure = (irsb_virtual*) ((UCHAR *) tdbb->getRequest() + rsb->rsb_impure); + CompilerScratch* csb = opt->opt_csb; + CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; - impure->irsb_record_buffer = NULL; + RecordSource* rsb = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) RecordSource; + rsb->rsb_type = rsb_record_stream; + rsb->rsb_stream = stream; + rsb->rsb_relation = csb_tail->csb_relation; + rsb->rsb_impure = CMP_impure(csb, sizeof(irsb_virtual)); + rsb->rsb_record_stream = FB_NEW(*tdbb->getDefaultPool()) VirtualTable(rsb); + + return rsb; } @@ -107,31 +116,30 @@ void VirtualTable::erase(thread_db* tdbb, record_param* rpb) } -bool VirtualTable::get(thread_db* tdbb, RecordSource* rsb) -{ - SET_TDBB(tdbb); - - jrd_req* request = tdbb->getRequest(); - - record_param* const rpb = &request->req_rpb[rsb->rsb_stream]; - irsb_virtual* const impure = (irsb_virtual*) ((UCHAR *) request + rsb->rsb_impure); - - if (!impure->irsb_record_buffer) - return false; - - rpb->rpb_number.increment(); - - return impure->irsb_record_buffer->fetch(rpb->rpb_number.getValue(), rpb->rpb_record); -} - - -void VirtualTable::modify(thread_db*, record_param* /*org_rpb*/, record_param* /*new_rpb*/) +void VirtualTable::modify(thread_db* tdbb, record_param* /*org_rpb*/, record_param* /*new_rpb*/) { ERR_post(Arg::Gds(isc_read_only)); } -void VirtualTable::open(thread_db* tdbb, RecordSource* rsb) +void VirtualTable::store(thread_db* tdbb, record_param* rpb) +{ + ERR_post(Arg::Gds(isc_read_only)); +} + + +unsigned VirtualTable::dump(UCHAR* buffer, unsigned bufferLen) +{ + UCHAR* bufferStart = buffer; + + if (bufferLen > 0) + *buffer++ = isc_info_rsb_virt_sequential; + + return buffer - bufferStart; +} + + +void VirtualTable::open(thread_db* tdbb) { SET_TDBB(tdbb); @@ -139,7 +147,7 @@ void VirtualTable::open(thread_db* tdbb, RecordSource* rsb) jrd_rel* const relation = rsb->rsb_relation; record_param* const rpb = &request->req_rpb[rsb->rsb_stream]; - irsb_virtual* const impure = (irsb_virtual*) ((UCHAR *) request + rsb->rsb_impure); + irsb_virtual* const impure = (irsb_virtual*) ((UCHAR*) request + rsb->rsb_impure); const Record* const record = rpb->rpb_record; const Format* format = NULL; @@ -159,24 +167,35 @@ void VirtualTable::open(thread_db* tdbb, RecordSource* rsb) } -Jrd::RecordSource* VirtualTable::optimize(thread_db* tdbb, OptimizerBlk* opt, SSHORT stream) +void VirtualTable::close(thread_db* tdbb) { SET_TDBB(tdbb); - CompilerScratch* csb = opt->opt_csb; - CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream]; + irsb_virtual* impure = (irsb_virtual*) ((UCHAR*) tdbb->getRequest() + rsb->rsb_impure); - RecordSource* rsb = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) RecordSource; - rsb->rsb_type = rsb_virt_sequential; - rsb->rsb_stream = stream; - rsb->rsb_relation = csb_tail->csb_relation; - rsb->rsb_impure = CMP_impure(csb, sizeof(irsb_virtual)); - - return rsb; + impure->irsb_record_buffer = NULL; } -void VirtualTable::store(thread_db*, record_param*) +bool VirtualTable::get(thread_db* tdbb) { - ERR_post(Arg::Gds(isc_read_only)); + SET_TDBB(tdbb); + + jrd_req* request = tdbb->getRequest(); + + record_param* const rpb = &request->req_rpb[rsb->rsb_stream]; + irsb_virtual* const impure = (irsb_virtual*) ((UCHAR*) request + rsb->rsb_impure); + + if (!impure->irsb_record_buffer) + return false; + + rpb->rpb_number.increment(); + + return impure->irsb_record_buffer->fetch(rpb->rpb_number.getValue(), rpb->rpb_record); +} + + +void VirtualTable::markRecursive() +{ + // Do nothing. } diff --git a/src/jrd/VirtualTable.h b/src/jrd/VirtualTable.h index df2588e755..cb8d989dd5 100644 --- a/src/jrd/VirtualTable.h +++ b/src/jrd/VirtualTable.h @@ -18,6 +18,7 @@ * * All Rights Reserved. * Contributor(s): ______________________________________. + * Adriano dos Santos Fernandes */ #ifndef JRD_VIRTUAL_TABLE_H @@ -25,20 +26,48 @@ namespace Jrd { -// To be refactored to a class -namespace VirtualTable { +class RecordStream //// TODO: create RecordStream.h +{ +public: + virtual ~RecordStream() + { + } -void close(Jrd::thread_db*, Jrd::RecordSource*); -void erase(Jrd::thread_db*, Jrd::record_param*); -void fini(Jrd::jrd_rel*); -bool get(Jrd::thread_db*, Jrd::RecordSource*); -void modify(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*); -void open(Jrd::thread_db*, Jrd::RecordSource*); -Jrd::RecordSource* optimize(Jrd::thread_db*, Jrd::OptimizerBlk*, SSHORT); -void store(Jrd::thread_db*, Jrd::record_param*); +public: + virtual unsigned dump(UCHAR* buffer, unsigned bufferLen) = 0; + virtual void open(thread_db* tdbb) = 0; + virtual void close(thread_db* tdbb) = 0; + virtual bool get(thread_db* tdbb) = 0; + virtual void markRecursive() = 0; +}; + + +class VirtualTable : public RecordStream +{ +public: + VirtualTable(RecordSource* aRsb) + : rsb(aRsb) + { + } + +public: + static RecordSource* create(thread_db*, OptimizerBlk*, SSHORT); + static void erase(thread_db*, record_param*); + static void modify(thread_db*, record_param*, record_param*); + static void store(thread_db*, record_param*); + +public: + virtual unsigned dump(UCHAR* buffer, unsigned bufferLen); + virtual void open(thread_db* tdbb); + virtual void close(thread_db* tdbb); + virtual bool get(thread_db* tdbb); + virtual void markRecursive(); + +private: + RecordSource* rsb; +}; -} // namespace VirtualTable } // namespace Jrd diff --git a/src/jrd/WindowRsb.cpp b/src/jrd/WindowRsb.cpp new file mode 100644 index 0000000000..fa8fc6db9a --- /dev/null +++ b/src/jrd/WindowRsb.cpp @@ -0,0 +1,141 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2009 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/jrd.h" +#include "../jrd/ini.h" +#include "../jrd/rse.h" +#include "../jrd/exe_proto.h" +#include "../jrd/opt_proto.h" +#include "../jrd/rse_proto.h" +#include "../jrd/WindowRsb.h" + +using namespace Jrd; +using namespace Firebird; + + +WindowRsb::WindowRsb(RecordSource* aRsb) + : rsb(aRsb), + next(rsb->rsb_next) +{ +} + + +RecordSource* WindowRsb::create(thread_db* tdbb, OptimizerBlk* opt, RecordSource* next) +{ + SET_TDBB(tdbb); + + CompilerScratch* csb = opt->opt_csb; + + RecordSource* rsb = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) RecordSource; + rsb->rsb_type = rsb_record_stream; + rsb->rsb_next = next; + rsb->rsb_stream = next->rsb_stream; + rsb->rsb_record_stream = FB_NEW(*tdbb->getDefaultPool()) WindowRsb(rsb); + + return rsb; +} + + +unsigned WindowRsb::dump(UCHAR* buffer, unsigned bufferLen) +{ + UCHAR* bufferStart = buffer; + + if (bufferLen > 0) + *buffer++ = isc_info_rsb_window; + + return buffer - bufferStart; +} + + +void WindowRsb::open(thread_db* tdbb) +{ + SET_TDBB(tdbb); + + RSE_open(tdbb, next); + RSE_internal_get_record(tdbb, next, NULL, RSE_get_forward); + RSE_close(tdbb, next); + + RSE_open(tdbb, next->rsb_next); +} + + +void WindowRsb::close(thread_db* tdbb) +{ + SET_TDBB(tdbb); + + RSE_close(tdbb, next->rsb_next); +} + + +bool WindowRsb::get(thread_db* tdbb) +{ + SET_TDBB(tdbb); + + if (!RSE_internal_get_record(tdbb, next->rsb_next, NULL, RSE_get_forward)) + return false; + + jrd_req* request = tdbb->getRequest(); + jrd_nod* node = (jrd_nod*) next->rsb_arg[0]; + jrd_nod* map = node->nod_arg[e_agg_map]; + + jrd_nod* const* ptr = map->nod_arg; + for (const jrd_nod* const* end = ptr + map->nod_count; ptr < end; ptr++) + { + jrd_nod* from = (*ptr)->nod_arg[e_asgn_from]; + impure_value_ex* impure = (impure_value_ex*) ((SCHAR*) request + from->nod_impure); + + switch (from->nod_type) + { + case nod_agg_average: + case nod_agg_average_distinct: + case nod_agg_average2: + case nod_agg_average_distinct2: + case nod_agg_total: + case nod_agg_total_distinct: + case nod_agg_total2: + case nod_agg_total_distinct2: + case nod_agg_min: + case nod_agg_min_indexed: + case nod_agg_max: + case nod_agg_max_indexed: + case nod_agg_count: + case nod_agg_count2: + case nod_agg_count_distinct: + case nod_agg_list: + case nod_agg_list_distinct: + break; + + default: + EXE_assignment(tdbb, *ptr); + break; + } + } + + return true; +} + + +void WindowRsb::markRecursive() +{ + OPT_mark_rsb_recursive(rsb->rsb_next); +} diff --git a/src/jrd/WindowRsb.h b/src/jrd/WindowRsb.h new file mode 100644 index 0000000000..28deb3b59c --- /dev/null +++ b/src/jrd/WindowRsb.h @@ -0,0 +1,54 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2009 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_WINDOW_RSB_H +#define JRD_WINDOW_RSB_H + +#include "../jrd/VirtualTable.h" + +namespace Jrd { + + +class WindowRsb : public RecordStream +{ +public: + WindowRsb(RecordSource* aRsb); + +public: + static RecordSource* create(thread_db*, OptimizerBlk* opt, RecordSource* next); + +public: + virtual unsigned dump(UCHAR* buffer, unsigned bufferLen); + virtual void open(thread_db* tdbb); + virtual void close(thread_db* tdbb); + virtual bool get(thread_db* tdbb); + virtual void markRecursive(); + +private: + RecordSource* rsb; + RecordSource* next; +}; + + +} // namespace Jrd + +#endif // JRD_WINDOW_RSB_H diff --git a/src/jrd/acl.h b/src/jrd/acl.h index 96eb0fb89c..29a368a786 100644 --- a/src/jrd/acl.h +++ b/src/jrd/acl.h @@ -64,7 +64,8 @@ const int id_views = 8; // All views const int id_trigger = 9; // Trigger name const int id_procedure = 10; // Procedure name const int id_sql_role = 11; // SQL role -const int id_max = 12; +const int id_package = 12; // Package name +const int id_max = 13; /* Format of access control list: diff --git a/src/jrd/alt.cpp b/src/jrd/alt.cpp index 31688a626b..659d2f5ba9 100644 --- a/src/jrd/alt.cpp +++ b/src/jrd/alt.cpp @@ -50,11 +50,11 @@ #include "../jrd/alt_proto.h" #include "../jrd/constants.h" -#if !defined(SUPERSERVER) || defined(SUPERCLIENT) +///#if !defined(SUPERSERVER) || defined(SUPERCLIENT) #if !defined(BOOT_BUILD) static ISC_STATUS executeSecurityCommand(ISC_STATUS*, const USER_SEC_DATA*, internal_user_data&); #endif // BOOT_BUILD -#endif +///#endif SLONG API_ROUTINE_VARARG isc_event_block(UCHAR** event_buffer, UCHAR** result_buffer, @@ -819,8 +819,8 @@ void API_ROUTINE CVT_move(const dsc*, dsc*, FPTR_ERROR err) err(isc_random, isc_arg_string, "CVT_move() private API not supported any more", isc_arg_end); } -#if !defined(SUPERSERVER) || defined(SUPERCLIENT) -// AP: isc_*_user entrypoints are used only in any kind of embedded +///#if !defined(SUPERSERVER) || defined(SUPERCLIENT) +// AP: isc_*_user entrypoints are used only in any kind of embedded // server (both posix and windows) and fbclient #ifndef BOOT_BUILD @@ -1224,4 +1224,4 @@ static ISC_STATUS executeSecurityCommand(ISC_STATUS* status, #endif // BOOT_BUILD -#endif // !defined(SUPERSERVER) || defined(SUPERCLIENT) +///#endif // !defined(SUPERSERVER) || defined(SUPERCLIENT) diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 59a775de46..3acbc88622 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -312,9 +312,9 @@ blb* BLB_create2(thread_db* tdbb, else if (to == isc_blob_text && from_charset != to_charset) { if (from_charset == CS_dynamic) - from_charset = tdbb->getAttachment()->att_charset; + from_charset = tdbb->getCharSet(); if (to_charset == CS_dynamic) - to_charset = tdbb->getAttachment()->att_charset; + to_charset = tdbb->getCharSet(); if (to_charset != CS_NONE && from_charset != CS_NONE && to_charset != CS_BINARY && from_charset != CS_BINARY && @@ -1414,9 +1414,9 @@ blb* BLB_open2(thread_db* tdbb, else if (to == isc_blob_text && from_charset != to_charset) { if (from_charset == CS_dynamic) - from_charset = tdbb->getAttachment()->att_charset; + from_charset = tdbb->getCharSet(); if (to_charset == CS_dynamic) - to_charset = tdbb->getAttachment()->att_charset; + to_charset = tdbb->getCharSet(); if (to_charset != CS_NONE && from_charset != CS_NONE && to_charset != CS_BINARY && from_charset != CS_BINARY && diff --git a/src/jrd/blp.h b/src/jrd/blp.h index 5163751b2f..550203b937 100644 --- a/src/jrd/blp.h +++ b/src/jrd/blp.h @@ -227,6 +227,12 @@ static const struct {"exec_stmt", exec_stmt}, {"stmt_expr", two}, {"derived_expr", derived_expr}, + {"procedure2", procedure2}, + {"exec_proc2", exec_proc2}, + {"function2", function2}, + {"window", aggregate}, + {"partition_by", byte_args}, + {"continue_loop", byte_line}, {0, 0} }; diff --git a/src/jrd/blr.h b/src/jrd/blr.h index 7baa16901e..f294c05da0 100644 --- a/src/jrd/blr.h +++ b/src/jrd/blr.h @@ -376,4 +376,13 @@ #define blr_stmt_expr (unsigned char) 190 #define blr_derived_expr (unsigned char) 191 +#define blr_procedure2 (unsigned char) 192 +#define blr_exec_proc2 (unsigned char) 193 +#define blr_function2 (unsigned char) 194 + +#define blr_window (unsigned char) 195 +#define blr_partition_by (unsigned char) 196 + +#define blr_continue_loop (unsigned char) 197 + #endif // JRD_BLR_H diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index ccd00ecfbb..b67febdb5f 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -531,7 +531,7 @@ bool CCH_exclusive_attachment(thread_db* tdbb, USHORT level, SSHORT wait_flag) SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attachment->att_flags & ATT_exclusive) { return true; } @@ -546,7 +546,7 @@ bool CCH_exclusive_attachment(thread_db* tdbb, USHORT level, SSHORT wait_flag) if (level != LCK_none) { - for (Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next) + for (Jrd::Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next) { if (*ptr == attachment) { @@ -698,7 +698,7 @@ pag* CCH_fake(thread_db* tdbb, WIN* window, SSHORT latch_wait) SDW_get_shadows(tdbb); } - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (!attachment->backupStateReadLock(tdbb, latch_wait)) return NULL; @@ -899,7 +899,7 @@ SSHORT CCH_fetch_lock(thread_db* tdbb, WIN* window, USHORT lock_type, SSHORT wai } // Look for the page in the cache. - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (!attachment->backupStateReadLock(tdbb, wait)) return -2; @@ -2284,7 +2284,7 @@ void CCH_release_exclusive(thread_db* tdbb) Database* dbb = tdbb->getDatabase(); dbb->dbb_flags &= ~DBB_exclusive; - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attachment) { attachment->att_flags &= ~ATT_exclusive; } @@ -4001,7 +4001,7 @@ static THREAD_ENTRY_DECLARE cache_reader(THREAD_ENTRY_PARAM arg) // Dummy attachment needed for lock owner identification. tdbb->setDatabase(dbb); - Attachment* const attachment = Attachment::create(dbb); + Jrd::Attachment* const attachment = Attachment::create(dbb); tdbb->setAttachment(attachment); attachment->att_filename = dbb->dbb_filename; Jrd::ContextPoolHolder context(tdbb, dbb->dbb_bufferpool); @@ -4119,7 +4119,7 @@ static THREAD_ENTRY_DECLARE cache_reader(THREAD_ENTRY_PARAM arg) } LCK_fini(tdbb, LCK_OWNER_attachment); - Attachment::destroy(attachment); // no need saving warning error strings here + Jrd::Attachment::destroy(attachment); // no need saving warning error strings here tdbb->setAttachment(NULL); bcb->bcb_flags &= ~BCB_cache_reader; dbb->dbb_reader_fini.post(); @@ -4163,7 +4163,7 @@ static THREAD_ENTRY_DECLARE cache_writer(THREAD_ENTRY_PARAM arg) // Dummy attachment needed for lock owner identification. tdbb->setDatabase(dbb); - Attachment* const attachment = Attachment::create(dbb); + Jrd::Attachment* const attachment = Jrd::Attachment::create(dbb, 0); tdbb->setAttachment(attachment); attachment->att_filename = dbb->dbb_filename; Jrd::ContextPoolHolder context(tdbb, dbb->dbb_bufferpool); @@ -4279,7 +4279,7 @@ static THREAD_ENTRY_DECLARE cache_writer(THREAD_ENTRY_PARAM arg) } LCK_fini(tdbb, LCK_OWNER_attachment); - Attachment::destroy(attachment); // no need saving warning error strings here + Jrd::Attachment::destroy(attachment); // no need saving warning error strings here tdbb->setAttachment(NULL); bcb->bcb_flags &= ~BCB_cache_writer; // Notify the finalization caller that we're finishing. diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index fa0dff7fb7..143b14e227 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -91,6 +91,7 @@ #include "../common/classes/auto.h" #include "../common/utils_proto.h" #include "../dsql/Nodes.h" +#include "../jrd/ValuesImpl.h" using Firebird::AutoSetRestore; @@ -391,7 +392,7 @@ static void verify_trigger_access(thread_db* tdbb, jrd_rel* owner_relation, trig const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); SCL_check_access(tdbb, sec_class, (access->acc_view_id) ? access->acc_view_id : (view ? view->rel_id : 0), - t.request->req_trg_name, NULL, access->acc_mask, + t.request->req_trg_name, NULL, NULL, access->acc_mask, access->acc_type, access->acc_name, access->acc_r_name); } } @@ -429,8 +430,19 @@ void CMP_verify_access(thread_db* tdbb, jrd_req* request) access++) { const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); - SCL_check_access(tdbb, sec_class, access->acc_view_id, NULL, prc->prc_name, - access->acc_mask, access->acc_type, access->acc_name, access->acc_r_name); + + if (prc->prc_name.qualifier.isEmpty()) + { + SCL_check_access(tdbb, sec_class, access->acc_view_id, NULL, + prc->prc_name.identifier, NULL, access->acc_mask, access->acc_type, + access->acc_name, access->acc_r_name); + } + else + { + SCL_check_access(tdbb, sec_class, access->acc_view_id, NULL, + NULL, prc->prc_name.qualifier, access->acc_mask, access->acc_type, + access->acc_name, access->acc_r_name); + } } } else { @@ -466,12 +478,10 @@ void CMP_verify_access(thread_db* tdbb, jrd_req* request) // this request is called immediately by caller (check for empty req_caller). // Currently (in v2.5) this rule will work for EXECUTE STATEMENT only, as // tra_callback_count incremented only by it. - // When external SP's will be introduced we need to decide if they also can - // inherit caller's privileges + // In v3.0, this rule also work for external procedures and triggers. jrd_tra* transaction = tdbb->getTransaction(); - const jrd_req* exec_stmt_caller = - (transaction && transaction->tra_callback_count && !request->req_caller) ? - transaction->tra_callback_caller : NULL; + const bool useCallerPrivs = transaction && transaction->tra_callback_count && + !request->req_caller; for (const AccessItem* access = request->req_access.begin(); access < request->req_access.end(); @@ -479,11 +489,17 @@ void CMP_verify_access(thread_db* tdbb, jrd_req* request) { const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str()); - Firebird::MetaName trgName(exec_stmt_caller ? exec_stmt_caller->req_trg_name : NULL); - Firebird::MetaName prcName(exec_stmt_caller && exec_stmt_caller->req_procedure ? - exec_stmt_caller->req_procedure->prc_name : NULL); + Firebird::MetaName trgName( + useCallerPrivs && transaction->tra_caller_name.type == obj_trigger ? + transaction->tra_caller_name.name : NULL); + Firebird::MetaName prcName( + useCallerPrivs && transaction->tra_caller_name.type == obj_procedure ? + transaction->tra_caller_name.name : NULL); + Firebird::MetaName pkgName( + useCallerPrivs && transaction->tra_caller_name.type == obj_package_header ? + transaction->tra_caller_name.name : NULL); - SCL_check_access(tdbb, sec_class, access->acc_view_id, trgName, prcName, + SCL_check_access(tdbb, sec_class, access->acc_view_id, trgName, prcName, pkgName, access->acc_mask, access->acc_type, access->acc_name, access->acc_r_name); } } @@ -507,7 +523,7 @@ jrd_req* CMP_clone_request(thread_db* tdbb, jrd_req* request, USHORT level, bool SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); fb_assert(dbb); // find the request if we've got it @@ -529,8 +545,17 @@ jrd_req* CMP_clone_request(thread_db* tdbb, jrd_req* request, USHORT level, bool const TEXT* prc_sec_name = (procedure->prc_security_name.length() > 0 ? procedure->prc_security_name.c_str() : NULL); const SecurityClass* sec_class = SCL_get_class(tdbb, prc_sec_name); - SCL_check_access(tdbb, sec_class, 0, NULL, NULL, SCL_execute, - object_procedure, procedure->prc_name); + + if (procedure->prc_name.qualifier.isEmpty()) + { + SCL_check_access(tdbb, sec_class, 0, NULL, NULL, NULL, SCL_execute, + object_procedure, procedure->prc_name.identifier); + } + else + { + SCL_check_access(tdbb, sec_class, 0, NULL, NULL, NULL, SCL_execute, + object_package, procedure->prc_name.qualifier); + } } CMP_verify_access(tdbb, request); @@ -557,6 +582,7 @@ jrd_req* CMP_clone_request(thread_db* tdbb, jrd_req* request, USHORT level, bool clone->req_procedure = request->req_procedure; clone->req_flags = request->req_flags & REQ_FLAGS_CLONE_MASK; clone->req_last_xcp = request->req_last_xcp; + clone->req_charset = request->req_charset; clone->req_id = fb_utils::genUniqueId(); // We are cloning full lists here, not assigning pointers @@ -632,7 +658,9 @@ jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool } #endif - if (internal_flag) { + if (internal_flag) + { + request->req_charset = CS_METADATA; request->req_flags |= req_internal; } @@ -2086,7 +2114,7 @@ jrd_req* CMP_make_request(thread_db* tdbb, CompilerScratch* csb, bool internal_f Database* const dbb = tdbb->getDatabase(); fb_assert(dbb); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); jrd_req* const old_request = tdbb->getRequest(); tdbb->setRequest(NULL); @@ -2153,6 +2181,7 @@ jrd_req* CMP_make_request(thread_db* tdbb, CompilerScratch* csb, bool internal_f request->req_access = csb->csb_access; request->req_external = csb->csb_external; request->req_map_field_info.takeOwnership(csb->csb_map_field_info); + request->req_charset = tdbb->getAttachment()->att_charset; request->req_id = fb_utils::genUniqueId(); // CVC: Unused. @@ -2203,7 +2232,7 @@ jrd_req* CMP_make_request(thread_db* tdbb, CompilerScratch* csb, bool internal_f char buffer[256]; sprintf(buffer, "Called from CMP_make_request():\n\t Incrementing use count of %s\n", - procedure->prc_name->c_str()); + procedure->prc_name->toString().c_str()); JRD_print_procedure_info(tdbb, buffer); } #endif @@ -2393,7 +2422,7 @@ void CMP_decrement_prc_use_count(thread_db* tdbb, jrd_prc* procedure) char buffer[256]; sprintf(buffer, "Called from CMP_decrement():\n\t Decrementing use count of %s\n", - procedure->prc_name->c_str()); + procedure->prc_name->toString().c_str()); JRD_print_procedure_info(tdbb, buffer); } #endif @@ -2434,7 +2463,7 @@ void CMP_release(thread_db* tdbb, jrd_req* request) // release existence locks on references - Attachment* attachment = request->req_attachment; + Jrd::Attachment* attachment = request->req_attachment; if (!attachment || !(attachment->att_flags & ATT_shutdown)) { for (Resource* resource = request->req_resources.begin(); @@ -2505,6 +2534,12 @@ void CMP_release(thread_db* tdbb, jrd_req* request) request->req_sql_text = NULL; + delete request->inputParams; + request->inputParams = NULL; + + delete request->outputParams; + request->outputParams = NULL; + // We have to call the destructor explicitly because of // the delete-by-pool cleanup practice for requests request->req_sorts.~SortOwner(); @@ -5605,7 +5640,7 @@ jrd_nod* CMP_pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* const node, j UserFunction* function = (UserFunction*) node->nod_arg[e_fun_function]; node->nod_arg[e_fun_function] = (jrd_nod*) FUN_resolve(tdbb, csb, function, value); if (!node->nod_arg[e_fun_function]) { - ERR_post(Arg::Gds(isc_funmismat) << Arg::Str(function->fun_name)); + ERR_post(Arg::Gds(isc_funmismat) << Arg::Str(function->fun_name.toString())); } } dsc descriptor_a; @@ -6120,8 +6155,16 @@ static void post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc (procedure->prc_security_name.length() > 0 ? procedure->prc_security_name.c_str() : NULL); // this request must have EXECUTE permission on the stored procedure - CMP_post_access(tdbb, csb, prc_sec_name, 0, SCL_execute, object_procedure, - procedure->prc_name.c_str()); + if (procedure->prc_name.qualifier.isEmpty()) + { + CMP_post_access(tdbb, csb, prc_sec_name, 0, SCL_execute, object_procedure, + procedure->prc_name.identifier.c_str()); + } + else + { + CMP_post_access(tdbb, csb, prc_sec_name, 0, SCL_execute, object_package, + procedure->prc_name.qualifier.c_str()); + } // Add the procedure to list of external objects accessed ExternalAccess temp(procedure->prc_id); diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 2fc54bdc19..8ec5b1bb78 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -254,7 +254,7 @@ const int TRIGGER_TYPE_MASK = (0x3 << TRIGGER_TYPE_SHIFT); const int TRIGGER_TYPE_DML = (0 << TRIGGER_TYPE_SHIFT); const int TRIGGER_TYPE_DB = (1 << TRIGGER_TYPE_SHIFT); -//const int TRIGGER_TYPE_DDL = (2 << TRIGGER_TYPE_SHIFT); +const int TRIGGER_TYPE_DDL = (2 << TRIGGER_TYPE_SHIFT); const int DB_TRIGGER_CONNECT = 0; const int DB_TRIGGER_DISCONNECT = 1; @@ -263,9 +263,104 @@ const int DB_TRIGGER_TRANS_COMMIT = 3; const int DB_TRIGGER_TRANS_ROLLBACK = 4; const int DB_TRIGGER_MAX = 5; +static const char* const DDL_TRIGGER_ACTION_NAMES[] = +{ + "CREATE TABLE", + "ALTER TABLE", + "DROP TABLE", + "CREATE PROCEDURE", + "ALTER PROCEDURE", + "DROP PROCEDURE", + "CREATE FUNCTION", + "ALTER FUNCTION", + "DROP FUNCTION", + "CREATE TRIGGER", + "ALTER TRIGGER", + "DROP TRIGGER", + "", "", "", // gap for TRIGGER_TYPE_MASK - 3 bits + "CREATE EXCEPTION", + "ALTER EXCEPTION", + "DROP EXCEPTION", + "CREATE VIEW", + "ALTER VIEW", + "DROP VIEW", + "CREATE DOMAIN", + "ALTER DOMAIN", + "DROP DOMAIN", + "CREATE ROLE", + "ALTER ROLE", + "DROP ROLE", + "CREATE INDEX", + "ALTER INDEX", + "DROP INDEX", + "CREATE SEQUENCE", + "ALTER SEQUENCE", + "DROP SEQUENCE", + "CREATE USER", + "ALTER USER", + "DROP USER", + "CREATE COLLATION", + "DROP COLLATION", + "ALTER CHARACTER SET", + "CREATE PACKAGE", + "ALTER PACKAGE", + "DROP PACKAGE", + "CREATE PACKAGE BODY", + "DROP PACKAGE BODY" +}; + +const int DDL_TRIGGER_BEFORE = 0; +const int DDL_TRIGGER_AFTER = 1; + +const int DDL_TRIGGER_CREATE_TABLE = 1; +const int DDL_TRIGGER_ALTER_TABLE = 2; +const int DDL_TRIGGER_DROP_TABLE = 3; +const int DDL_TRIGGER_CREATE_PROCEDURE = 4; +const int DDL_TRIGGER_ALTER_PROCEDURE = 5; +const int DDL_TRIGGER_DROP_PROCEDURE = 6; +const int DDL_TRIGGER_CREATE_FUNCTION = 7; +const int DDL_TRIGGER_ALTER_FUNCTION = 8; +const int DDL_TRIGGER_DROP_FUNCTION = 9; +const int DDL_TRIGGER_CREATE_TRIGGER = 10; +const int DDL_TRIGGER_ALTER_TRIGGER = 11; +const int DDL_TRIGGER_DROP_TRIGGER = 12; +// gap for TRIGGER_TYPE_MASK - 3 bits +const int DDL_TRIGGER_CREATE_EXCEPTION = 16; +const int DDL_TRIGGER_ALTER_EXCEPTION = 17; +const int DDL_TRIGGER_DROP_EXCEPTION = 18; +const int DDL_TRIGGER_CREATE_VIEW = 19; +const int DDL_TRIGGER_ALTER_VIEW = 20; +const int DDL_TRIGGER_DROP_VIEW = 21; +const int DDL_TRIGGER_CREATE_DOMAIN = 22; +const int DDL_TRIGGER_ALTER_DOMAIN = 23; +const int DDL_TRIGGER_DROP_DOMAIN = 24; +const int DDL_TRIGGER_CREATE_ROLE = 25; +const int DDL_TRIGGER_ALTER_ROLE = 26; +const int DDL_TRIGGER_DROP_ROLE = 27; +const int DDL_TRIGGER_CREATE_INDEX = 28; +const int DDL_TRIGGER_ALTER_INDEX = 29; +const int DDL_TRIGGER_DROP_INDEX = 30; +const int DDL_TRIGGER_CREATE_SEQUENCE = 31; +const int DDL_TRIGGER_ALTER_SEQUENCE = 32; +const int DDL_TRIGGER_DROP_SEQUENCE = 33; +const int DDL_TRIGGER_CREATE_USER = 34; +const int DDL_TRIGGER_ALTER_USER = 35; +const int DDL_TRIGGER_DROP_USER = 36; +const int DDL_TRIGGER_CREATE_COLLATION = 37; +const int DDL_TRIGGER_DROP_COLLATION = 38; +const int DDL_TRIGGER_ALTER_CHARACTER_SET = 39; +const int DDL_TRIGGER_CREATE_PACKAGE = 40; +const int DDL_TRIGGER_ALTER_PACKAGE = 41; +const int DDL_TRIGGER_DROP_PACKAGE = 42; +const int DDL_TRIGGER_CREATE_PACKAGE_BODY = 43; +const int DDL_TRIGGER_DROP_PACKAGE_BODY = 44; + // that's how database trigger action types are encoded // (TRIGGER_TYPE_DB | type) +// that's how DDL trigger action types are encoded +// (TRIGGER_TYPE_DDL | DDL_TRIGGER_{AFTER | BEFORE} [ | DDL_TRIGGER_??? ...]) + // switches for username and password used when an username and/or password // is specified by the client application #define USERNAME_SWITCH "USER" diff --git a/src/jrd/cvt2.cpp b/src/jrd/cvt2.cpp index fb2ee5e682..69f11c2a78 100644 --- a/src/jrd/cvt2.cpp +++ b/src/jrd/cvt2.cpp @@ -715,6 +715,7 @@ USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Jrd UCHAR* tempptr = temp.getBuffer(length); length = INTL_convert_bytes(tdbb, cs1, tempptr, length, cs2, from_buf, from_len, ERR_post); *address = tempptr; + temp.resize(length); return length; } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index c7d741870a..58f03a5de7 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -175,10 +175,12 @@ public: USHORT dfw_id; // object id, if appropriate USHORT dfw_count; // count of block posts string dfw_name; // name of object + MetaName dfw_package; // package name public: DeferredWork(MemoryPool& p, DeferredWork*** end, - enum dfw_t t, USHORT id, SLONG sn, const string& name) + enum dfw_t t, USHORT id, SLONG sn, const string& name, + const MetaName& package) : dfw_type(t), dfw_end(end), dfw_prev(dfw_end ? *dfw_end : NULL), dfw_next(dfw_prev ? *dfw_prev : NULL), dfw_lock(NULL), dfw_args(p), dfw_sav_number(sn), dfw_id(id), dfw_count(1), dfw_name(p, name) @@ -258,6 +260,7 @@ public: if (dfw_type == work.dfw_type && dfw_id == work.dfw_id && dfw_name == work.dfw_name && + dfw_package == work.dfw_package && dfw_sav_number == work.dfw_sav_number) { return true; @@ -372,7 +375,10 @@ static bool add_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool begin_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool end_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool check_not_null(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool user_management(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool drop_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool grant_privileges(thread_db*, SSHORT, DeferredWork*, jrd_tra*); /* ---------------------------------------------------------------- */ @@ -381,7 +387,7 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* jrd_tra* transaction); static void check_computed_dependencies(thread_db* tdbb, jrd_tra* transaction, const Firebird::MetaName& fieldName); -static void check_dependencies(thread_db*, const TEXT*, const TEXT*, int, jrd_tra*); +static void check_dependencies(thread_db*, const TEXT*, const TEXT*, const TEXT*, int, jrd_tra*); static void check_filename(const Firebird::string&, bool); static void check_system_generator(const TEXT*, const dfw_t); static bool formatsAreEqual(const Format*, const Format*); @@ -442,6 +448,8 @@ static const deferred_task task_table[] = { dfw_create_trigger, create_trigger }, { dfw_delete_trigger, delete_trigger }, { dfw_modify_trigger, modify_trigger }, + { dfw_drop_package_header, drop_package_header }, // packages should be before procedures + { dfw_drop_package_body, drop_package_body }, // packages should be before procedures { dfw_create_procedure, create_procedure }, { dfw_delete_procedure, delete_procedure }, { dfw_modify_procedure, modify_procedure }, @@ -457,6 +465,7 @@ static const deferred_task task_table[] = { dfw_begin_backup, begin_backup }, { dfw_end_backup, end_backup }, { dfw_user_management, user_management }, + { dfw_check_not_null, check_not_null }, { dfw_null, NULL } }; @@ -842,11 +851,12 @@ DeferredWork* DFW_post_system_work(thread_db* tdbb, enum dfw_t type, const dsc* Database::CheckoutLockGuard guard(dbb, dbb->dbb_sys_dfw_mutex); - return DFW_post_work(dbb->dbb_sys_trans, type, desc, id); + return DFW_post_work(dbb->dbb_sys_trans, type, desc, id, ""); } -DeferredWork* DFW_post_work(jrd_tra* transaction, enum dfw_t type, const dsc* desc, USHORT id) +DeferredWork* DFW_post_work(jrd_tra* transaction, enum dfw_t type, const dsc* desc, USHORT id, + const MetaName& package) { /************************************** * @@ -882,7 +892,7 @@ DeferredWork* DFW_post_work(jrd_tra* transaction, enum dfw_t type, const dsc* de } const string name = get_string(desc); - DeferredWork tmp(AutoStorage::getAutoMemoryPool(), 0, type, id, sav_number, name); + DeferredWork tmp(AutoStorage::getAutoMemoryPool(), 0, type, id, sav_number, name, package); DeferredWork* work = sp->hash.lookup(tmp); if (work) { @@ -892,7 +902,7 @@ DeferredWork* DFW_post_work(jrd_tra* transaction, enum dfw_t type, const dsc* de // Not already posted, so do so now. work = FB_NEW(*transaction->tra_pool) - DeferredWork(*transaction->tra_pool, &(job->end), type, id, sav_number, name); + DeferredWork(*transaction->tra_pool, &(job->end), type, id, sav_number, name, package); job->end = work->getNextPtr(); fb_assert(!(*job->end)); sp->hash.add(work); @@ -952,7 +962,7 @@ DeferredWork* DFW_post_work_arg( jrd_tra* transaction, DeferredWork* work, const if (! arg) { arg = FB_NEW(*transaction->tra_pool) - DeferredWork(*transaction->tra_pool, 0, type, id, 0, name); + DeferredWork(*transaction->tra_pool, 0, type, id, 0, name, ""); work->dfw_args.add(arg); } @@ -1470,6 +1480,192 @@ static bool end_backup( thread_db* tdbb, return false; } +static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ +/************************************** + * + * c h e c k _ n o t _ n u l l + * + ************************************** + * + * Scan relation to detect NULLs in fields being changed to NOT NULL. + * + **************************************/ + + SET_TDBB(tdbb); + Database* dbb = tdbb->getDatabase(); + + Lock* relationLock = NULL; + bool releaseRelationLock = false; + + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + try + { + jrd_rel* relation = MET_lookup_relation(tdbb, work->dfw_name); + if (relation->rel_view_rse) + break; + + // Protect relation from modification + relationLock = protect_relation(tdbb, transaction, relation, releaseRelationLock); + + HalfStaticArray fields; + jrd_req* handle = NULL; + + FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS + WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() + { + int fid = MET_lookup_field(tdbb, relation, RFL.RDB$FIELD_NAME, NULL); + + if (!RFL.RDB$NULL_FLAG.NULL && RFL.RDB$NULL_FLAG && + (*relation->rel_fields)[fid]->fld_not_null) + { + fields.add(fid); + } + } + END_FOR + + CMP_release(tdbb, handle); + + UCharBuffer blr; + + blr.add(blr_version5); + blr.add(blr_begin); + blr.add(blr_message); + blr.add(1); // message number + blr.add(fields.getCount() & 0xFF); + blr.add(fields.getCount() << 8); + + for (size_t i = 0; i < fields.getCount(); ++i) + { + blr.add(blr_short); + blr.add(0); + } + + blr.add(blr_for); + blr.add(blr_stall); + blr.add(blr_rse); + blr.add(1); + blr.add(blr_rid); + blr.add(relation->rel_id & 0xFF); + blr.add(relation->rel_id << 8); + blr.add(0); // stream + blr.add(blr_boolean); + + for (size_t i = 0; i < fields.getCount(); ++i) + { + if (i != fields.getCount() - 1) + blr.add(blr_or); + + blr.add(blr_missing); + blr.add(blr_fid); + blr.add(0); // stream + blr.add(fields[i] & 0xFF); + blr.add(fields[i] << 8); + } + + blr.add(blr_end); + + blr.add(blr_send); + blr.add(1); + blr.add(blr_begin); + + for (size_t i = 0; i < fields.getCount(); ++i) + { + blr.add(blr_assignment); + + blr.add(blr_value_if); + blr.add(blr_missing); + blr.add(blr_fid); + blr.add(0); // stream + blr.add(fields[i] & 0xFF); + blr.add(fields[i] << 8); + + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(1); + blr.add(0); + + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(0); + blr.add(0); + + blr.add(blr_parameter); + blr.add(1); // message number + blr.add(i & 0xFF); + blr.add(i << 8); + } + + blr.add(blr_end); + + blr.add(blr_send); + blr.add(1); + blr.add(blr_begin); + + for (size_t i = 0; i < fields.getCount(); ++i) + { + blr.add(blr_assignment); + blr.add(blr_literal); + blr.add(blr_short); + blr.add(0); + blr.add(0); + blr.add(0); + blr.add(blr_parameter); + blr.add(1); // message number + blr.add(i & 0xFF); + blr.add(i << 8); + } + + blr.add(blr_end); + blr.add(blr_end); + blr.add(blr_eoc); + + jrd_req* request = CMP_compile2(tdbb, blr.begin(), TRUE, 0, NULL); + HalfStaticArray hasRecord; + + EXE_start(tdbb, request, transaction); + EXE_receive(tdbb, request, 1, fields.getCount() * sizeof(USHORT), + (UCHAR*) hasRecord.getBuffer(fields.getCount())); + EXE_unwind(tdbb, request); + + CMP_release(tdbb, request); + + for (size_t i = 0; i < fields.getCount(); ++i) + { + if (hasRecord[i]) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_cannot_make_not_null) << + Arg::Str((*relation->rel_fields)[fields[i]]->fld_name)); + } + } + + if (relationLock && releaseRelationLock) + release_protect_lock(tdbb, transaction, relationLock); + } + catch (const Exception&) + { + if (relationLock && releaseRelationLock) + release_protect_lock(tdbb, transaction, relationLock); + + throw; + } + + break; + } + + return false; +} + static bool user_management(thread_db* tdbb, SSHORT phase, DeferredWork* work, @@ -1504,6 +1700,43 @@ static bool user_management(thread_db* tdbb, } +// Drop dependencies of a package header. +static bool drop_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + SET_TDBB(tdbb); + + switch (phase) + { + case 0: + return false; + case 1: + MET_delete_dependencies(tdbb, work->dfw_name, obj_package_body, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_package_header, transaction); + return false; + } + + return false; +} + + +// Drop dependencies of a package body. +static bool drop_package_body(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + SET_TDBB(tdbb); + + switch (phase) + { + case 0: + return false; + case 1: + MET_delete_dependencies(tdbb, work->dfw_name, obj_package_body, transaction); + return false; + } + + return false; +} + + static bool grant_privileges(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { /************************************** @@ -1639,6 +1872,7 @@ static void check_computed_dependencies(thread_db* tdbb, jrd_tra* transaction, static void check_dependencies(thread_db* tdbb, const TEXT* dpdo_name, const TEXT* field_name, + const TEXT* package_name, int dpdo_type, jrd_tra* transaction) { @@ -1656,6 +1890,8 @@ static void check_dependencies(thread_db* tdbb, SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); + const MetaName packageName(package_name); + SLONG dep_counts[obj_type_MAX]; for (int i = 0; i < obj_type_MAX; i++) dep_counts[i] = 0; @@ -1669,6 +1905,7 @@ static void check_dependencies(thread_db* tdbb, WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type AND DEP.RDB$FIELD_NAME EQ field_name + AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') REDUCED TO DEP.RDB$DEPENDENT_NAME if (!REQUEST(irq_ch_f_dpd)) REQUEST(irq_ch_f_dpd) = request; @@ -1693,6 +1930,7 @@ static void check_dependencies(thread_db* tdbb, DEP IN RDB$DEPENDENCIES WITH DEP.RDB$DEPENDED_ON_NAME EQ dpdo_name AND DEP.RDB$DEPENDED_ON_TYPE = dpdo_type + AND DEP.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') REDUCED TO DEP.RDB$DEPENDENT_NAME if (!REQUEST(irq_ch_dpd)) @@ -1763,6 +2001,10 @@ static void check_dependencies(thread_db* tdbb, case obj_index: obj_type = isc_index_name; break; + case obj_package_header: + case obj_package_body: + obj_type = isc_package_name; + break; default: fb_assert(FALSE); break; @@ -1770,7 +2012,7 @@ static void check_dependencies(thread_db* tdbb, ERR_post(Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_no_delete) << /* can not delete */ - Arg::Gds(obj_type) << Arg::Str(dpdo_name) << + Arg::Gds(obj_type) << Arg::Str(QualifiedName(dpdo_name, packageName).toString()) << Arg::Gds(isc_dependency) << Arg::Num(total)); /* there are %ld dependencies */ } } @@ -2445,7 +2687,8 @@ static bool create_procedure(thread_db* tdbb, const bool compile = !work->findArg(dfw_arg_check_blr); get_procedure_dependencies(work, compile, transaction); - jrd_prc* procedure = MET_lookup_procedure(tdbb, work->dfw_name, compile); + jrd_prc* procedure = MET_lookup_procedure(tdbb, + QualifiedName(work->dfw_name, work->dfw_package), compile); if (!procedure) { return false; } @@ -2677,10 +2920,19 @@ static bool create_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, const DeferredWork* const arg = work->findArg(dfw_arg_trg_type); fb_assert(arg); - if (arg && (arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) + if (arg) { - MET_load_trigger(tdbb, NULL, work->dfw_name, - &tdbb->getDatabase()->dbb_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB]); + // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT + if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) + { + MET_load_trigger(tdbb, NULL, work->dfw_name, + &tdbb->getDatabase()->dbb_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB]); + } + else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) + { + MET_load_trigger(tdbb, NULL, work->dfw_name, + &tdbb->getDatabase()->dbb_ddl_triggers); + } } } } @@ -2847,7 +3099,7 @@ static bool delete_collation(thread_db* tdbb, SSHORT phase, DeferredWork* work, return false; case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, obj_collation, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_collation, transaction); return true; case 2: @@ -2887,7 +3139,7 @@ static bool delete_exception(thread_db* tdbb, return false; case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, obj_exception, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_exception, transaction); return true; case 2: @@ -2929,7 +3181,7 @@ static bool delete_generator(thread_db* tdbb, SSHORT phase, DeferredWork* work, return false; case 1: check_system_generator(gen_name, dfw_delete_generator); - check_dependencies(tdbb, gen_name, 0, obj_generator, transaction); + check_dependencies(tdbb, gen_name, NULL, NULL, obj_generator, transaction); return true; case 2: return true; @@ -3010,7 +3262,7 @@ static bool delete_udf(thread_db* tdbb, SSHORT phase, DeferredWork* work, case 0: return false; case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), 0, obj_udf, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_udf, transaction); return true; case 2: return true; @@ -3143,7 +3395,7 @@ static bool delete_field(thread_db* tdbb, Arg::Gds(isc_dependency) << Arg::Num(field_count)); /* Msg310: there are %ld dependencies */ } - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, obj_field, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_field, transaction); case 2: return true; @@ -3200,7 +3452,7 @@ static bool modify_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ // ASF: If there are procedures depending on the domain, it can't be renamed. if (arg && depName != arg->dfw_name.c_str()) - check_dependencies(tdbb, depName.c_str(), NULL, obj_field, transaction); + check_dependencies(tdbb, depName.c_str(), NULL, NULL, obj_field, transaction); MET_delete_dependencies(tdbb, depName, obj_validation, transaction); @@ -3358,7 +3610,7 @@ static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, return false; case 1: - check_dependencies(tdbb, arg->dfw_name.c_str(), NULL, obj_index, transaction); + check_dependencies(tdbb, arg->dfw_name.c_str(), NULL, NULL, obj_index, transaction); return true; case 2: @@ -3580,7 +3832,8 @@ static bool delete_procedure(thread_db* tdbb, return false; case 1: - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, obj_procedure, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, work->dfw_package.c_str(), + obj_procedure, transaction); return true; case 2: @@ -3608,53 +3861,60 @@ static bool delete_procedure(thread_db* tdbb, return true; case 4: - procedure = MET_lookup_procedure_id(tdbb, work->dfw_id, true, true, 0); - if (!procedure) { - return false; - } - - // Do not allow to drop procedure used by user requests - if (procedure->prc_use_count && MET_procedure_in_use(tdbb, procedure)) { - /* - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name)); - */ - gds__log("Deleting procedure %s which is currently in use by active user requests", - work->dfw_name.c_str()); - MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); + procedure = MET_lookup_procedure_id(tdbb, work->dfw_id, true, true, 0); + if (!procedure) { + return false; + } + + QualifiedName name(work->dfw_name, work->dfw_package); + + // Do not allow to drop procedure used by user requests + if (procedure->prc_use_count && MET_procedure_in_use(tdbb, procedure)) + { + /* + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString())); + */ + gds__log("Deleting procedure %s which is currently in use by active user requests", + name.toString().c_str()); + + if (work->dfw_package.isEmpty()) + MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); + + if (procedure->prc_existence_lock) { + LCK_release(tdbb, procedure->prc_existence_lock); + } + (*tdbb->getDatabase()->dbb_procedures)[procedure->prc_id] = NULL; + return false; + } + + old_flags = procedure->prc_flags; + procedure->prc_flags |= PRC_obsolete; + if (procedure->prc_request) + { + if (CMP_clone_is_active(procedure->prc_request)) + { + procedure->prc_flags = old_flags; + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString())); + } + + CMP_release(tdbb, procedure->prc_request); + procedure->prc_request = 0; + } + + /* delete dependency lists */ + + if (work->dfw_package.isEmpty()) + MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); if (procedure->prc_existence_lock) { LCK_release(tdbb, procedure->prc_existence_lock); } - (*tdbb->getDatabase()->dbb_procedures)[procedure->prc_id] = NULL; - return false; - } - - old_flags = procedure->prc_flags; - procedure->prc_flags |= PRC_obsolete; - if (procedure->prc_request) - { - if (CMP_clone_is_active(procedure->prc_request)) - { - procedure->prc_flags = old_flags; - ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name)); - } - - CMP_release(tdbb, procedure->prc_request); - procedure->prc_request = 0; - } - - /* delete dependency lists */ - - MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); - - if (procedure->prc_existence_lock) { - LCK_release(tdbb, procedure->prc_existence_lock); } break; - } + } // switch return false; } @@ -3725,7 +3985,7 @@ static bool delete_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, Arg::Gds(isc_dependency) << Arg::Num(view_count)); /* Msg310: there are %ld dependencies */ } - check_dependencies(tdbb, work->dfw_name.c_str(), NULL, obj_relation, transaction); + check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_relation, transaction); return true; case 2: @@ -3934,7 +4194,7 @@ static bool delete_rfr(thread_db* tdbb, SSHORT phase, DeferredWork* work, if ( (relation = MET_lookup_relation_id(tdbb, work->dfw_id, false)) ) { MET_scan_relation(tdbb, relation); - check_dependencies(tdbb, relation->rel_name.c_str(), work->dfw_name.c_str(), + check_dependencies(tdbb, relation->rel_name.c_str(), work->dfw_name.c_str(), NULL, relation->rel_view_rse ? obj_view : obj_relation, transaction); } @@ -4074,6 +4334,7 @@ static bool delete_trigger(thread_db* tdbb, const DeferredWork* arg = work->findArg(dfw_arg_trg_type); fb_assert(arg); + // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT if (arg && (arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) { MET_release_trigger(tdbb, @@ -4132,6 +4393,12 @@ static bool find_depend_in_dfw(thread_db* tdbb, case obj_expression_index: dfw_type = dfw_delete_expression_index; break; + case obj_package_header: + dfw_type = dfw_drop_package_header; + break; + case obj_package_body: + dfw_type = dfw_drop_package_body; + break; default: fb_assert(false); break; @@ -4142,7 +4409,8 @@ static bool find_depend_in_dfw(thread_db* tdbb, { if ((work->dfw_type == dfw_type || (work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure)) && - work->dfw_name == object_name && (!rel_id || rel_id == work->dfw_id)) + work->dfw_name == object_name && work->dfw_package.isEmpty() && + (!rel_id || rel_id == work->dfw_id)) { if (work->dfw_type == dfw_modify_procedure) { @@ -4306,11 +4574,13 @@ static void get_procedure_dependencies(DeferredWork* work, bool compile, jrd_tra FOR(REQUEST_HANDLE handle) X IN RDB$PROCEDURES WITH - X.RDB$PROCEDURE_NAME EQ work->dfw_name.c_str() + X.RDB$PROCEDURE_NAME EQ work->dfw_name.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') if (!REQUEST(irq_c_prc_dpd)) REQUEST(irq_c_prc_dpd) = handle; blob_id = X.RDB$PROCEDURE_BLR; - procedure = MET_lookup_procedure(tdbb, work->dfw_name, !compile); + procedure = MET_lookup_procedure(tdbb, QualifiedName(work->dfw_name, work->dfw_package), + !compile); END_FOR; @@ -4333,9 +4603,12 @@ static void get_procedure_dependencies(DeferredWork* work, bool compile, jrd_tra { Jrd::ContextPoolHolder context(tdbb, new_pool); - Firebird::MetaName depName(work->dfw_name); + Firebird::MetaName depName(work->dfw_package.isEmpty() ? + work->dfw_name : work->dfw_package); MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &blob_id, compile ? &request : NULL, - NULL, depName, obj_procedure, 0, transaction); + NULL, depName, + (work->dfw_package.isEmpty() ? obj_procedure : obj_package_body), + 0, transaction); if (request) CMP_release(tdbb, request); @@ -4969,6 +5242,7 @@ static bool modify_procedure(thread_db* tdbb, SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); + QualifiedName name(work->dfw_name, work->dfw_package); switch (phase) { @@ -5004,7 +5278,7 @@ static bool modify_procedure(thread_db* tdbb, if (!LCK_convert(tdbb, procedure->prc_existence_lock, LCK_EX, transaction->getLockWait())) { ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name)); + Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString())); } } @@ -5028,16 +5302,16 @@ static bool modify_procedure(thread_db* tdbb, { /* ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name)); + Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString())); */ gds__log("Modifying procedure %s which is currently in use by active user requests", - work->dfw_name.c_str()); + name.toString().c_str()); USHORT prc_alter_count = procedure->prc_alter_count; if (prc_alter_count > MAX_PROC_ALTER) { ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_proc_name) << Arg::Str(work->dfw_name) << + Arg::Gds(isc_proc_name) << Arg::Str(name.toString()) << Arg::Gds(isc_version_err)); /* Msg357: too many versions */ } @@ -5059,8 +5333,10 @@ static bool modify_procedure(thread_db* tdbb, if (procedure->prc_request) { if (CMP_clone_is_active(procedure->prc_request)) + { ERR_post(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name)); + Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString())); + } /* release the request */ @@ -5070,7 +5346,8 @@ static bool modify_procedure(thread_db* tdbb, /* delete dependency lists */ - MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); + if (work->dfw_package.isEmpty()) + MET_delete_dependencies(tdbb, work->dfw_name, obj_procedure, transaction); /* the procedure has just been scanned by MET_lookup_procedure_id and its PRC_scanned flag is set. We are going to reread it @@ -5187,6 +5464,7 @@ static bool modify_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, arg = work->findArg(dfw_arg_trg_type); fb_assert(arg); + // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT if (arg && (arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) { MET_release_trigger(tdbb, diff --git a/src/jrd/dfw_proto.h b/src/jrd/dfw_proto.h index 3a11281aaa..e9a6709bd0 100644 --- a/src/jrd/dfw_proto.h +++ b/src/jrd/dfw_proto.h @@ -38,7 +38,8 @@ void DFW_perform_system_work(Jrd::thread_db*); void DFW_perform_work(Jrd::thread_db*, Jrd::jrd_tra*); void DFW_perform_post_commit_work(Jrd::jrd_tra*); Jrd::DeferredWork* DFW_post_system_work(Jrd::thread_db*, Jrd::dfw_t, const dsc*, USHORT); -Jrd::DeferredWork* DFW_post_work(Jrd::jrd_tra*, Jrd::dfw_t, const dsc*, USHORT); +Jrd::DeferredWork* DFW_post_work(Jrd::jrd_tra*, Jrd::dfw_t, const dsc*, USHORT, + const Firebird::MetaName& package = NULL); Jrd::DeferredWork* DFW_post_work_arg(Jrd::jrd_tra*, Jrd::DeferredWork*, const dsc*, USHORT); Jrd::DeferredWork* DFW_post_work_arg(Jrd::jrd_tra*, Jrd::DeferredWork*, const dsc*, USHORT, Jrd::dfw_t); void DFW_update_index(const TEXT*, USHORT, const Jrd::SelectivityList&, Jrd::jrd_tra*); diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 0d143ec791..59123ee4c4 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -1460,7 +1460,7 @@ ULONG DPM_get_blob(thread_db* tdbb, // Unless this is the only attachment, don't allow the sequential scan // of very large blobs to flush pages used by other attachments. - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attachment && (attachment != dbb->dbb_attachments || attachment->att_next)) { // If the blob has more pages than the page buffer cache then mark diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 5bcf8faa64..c62ec485b7 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -49,175 +49,211 @@ #define DYN_REQUEST(drt) dbb->dbb_dyn_req[drt] -const int drq_l_prot_mask = 0; // lookup protection mask -const int drq_l_user_name = 1; // lookup user name -const int drq_s_rel_con = 2; // store relation constraints -const int drq_s_chk_con = 3; // store check constraints -const int drq_s_ref_con = 4; // store ref constraints -const int drq_f_nxt_con = 5; // find next relation constraint name -const int drq_f_nxt_fld = 6; // find next field name -const int drq_f_nxt_idx = 7; // find next index name -const int drq_f_nxt_trg = 8; // find next trigger name -const int drq_c_unq_nam = 9; // check for unique field names -const int drq_e_rel_con = 10; // erase relation constraints -const int drq_n_idx_seg = 11; // count index segments -const int drq_c_dup_con = 12; // check for duplicate contraint -const int drq_l_intg_con = 13; // lookup an integrity constraint -const int drq_s_dims = 14; // store dimensions -const int drq_s_files = 15; // store files -const int drq_s_filters = 16; // store filters -const int drq_s_gens = 17; // store generators -const int drq_s_funcs = 18; // store functions -const int drq_s_func_args = 19; // store function arguments -const int drq_s_gfields = 20; // store global fields -const int drq_s_lfields = 21; // store local fields -const int drq_s_gfields2 = 22; // store global fields -const int drq_s_rels = 23; // store relations -const int drq_l_rel_name = 24; // lookup relation name -const int drq_l_view_rels = 25; // lookup relations in view -const int drq_s_usr_prvs = 26; // store user privileges -const int drq_s_classes = 27; // store security classes -const int drq_s_sql_lfld = 28; // store sql fields -const int drq_s_sql_gfld = 29; // store sql fields -const int drq_s_triggers = 30; // store triggers -const int drq_s_trg_msgs = 31; // store trigger messages -const int drq_s_view_rels = 32; // store view relations -const int drq_e_dims = 33; // erase dimensions -const int drq_e_filters = 34; // erase filters -const int drq_e_func_args = 35; // erase functions -const int drq_e_funcs = 36; // erase function arguments -const int drq_l_fld_src = 37; // lookup a field source -const int drq_e_gfields = 38; // erase global fields -const int drq_e_indices = 39; // erase indices -const int drq_e_idx_segs = 40; // erase index segments -const int drq_l_dep_flds = 41; // lookup field referenced by view -const int drq_e_lfield = 42; // erase a local field -const int drq_e_rel_con2 = 43; // erase relation constraints -const int drq_e_rel_idxs = 44; // erase indices -const int drq_e_rel_flds = 45; // erase relation fields -const int drq_e_view_rels = 46; // erase view relations -const int drq_e_relation = 47; // erase relation -const int drq_e_rel_con3 = 48; // erase relation constraints -const int drq_e_usr_prvs = 49; // erase user privileges on relation -const int drq_e_shadow = 50; // erase shadow -const int drq_e_trg_msgs = 51; // erase trigger messages -const int drq_e_trigger = 52; // erase trigger -const int drq_l_view_rel2 = 53; // lookup relations in view -const int drq_m_rel_flds = 54; // modify relation fields -const int drq_e_trg_msg = 55; // erase trigger message -const int drq_e_class = 56; // erase security class -const int drq_l_grant1 = 57; // lookup grant -const int drq_l_grant2 = 58; // lookup grant -const int drq_s_grant = 59; // store grant +const int drq_l_prot_mask = 0; // lookup protection mask +const int drq_l_user_name = 1; // lookup user name +const int drq_s_rel_con = 2; // store relation constraints +const int drq_s_chk_con = 3; // store check constraints +const int drq_s_ref_con = 4; // store ref constraints +const int drq_f_nxt_con = 5; // find next relation constraint name +const int drq_f_nxt_fld = 6; // find next field name +const int drq_f_nxt_idx = 7; // find next index name +const int drq_f_nxt_trg = 8; // find next trigger name +const int drq_c_unq_nam = 9; // check for unique field names +const int drq_e_rel_con = 10; // erase relation constraints +const int drq_n_idx_seg = 11; // count index segments +const int drq_c_dup_con = 12; // check for duplicate contraint +const int drq_l_intg_con = 13; // lookup an integrity constraint +const int drq_s_dims = 14; // store dimensions +const int drq_s_files = 15; // store files +const int drq_s_filters = 16; // store filters +const int drq_s_gens = 17; // store generators +const int drq_s_funcs = 18; // store functions +const int drq_s_func_args = 19; // store function arguments +const int drq_s_gfields = 20; // store global fields +const int drq_s_lfields = 21; // store local fields +const int drq_s_gfields2 = 22; // store global fields +const int drq_s_rels = 23; // store relations +const int drq_l_rel_name = 24; // lookup relation name +const int drq_l_view_rels = 25; // lookup relations in view +const int drq_s_usr_prvs = 26; // store user privileges +const int drq_s_classes = 27; // store security classes +const int drq_s_sql_lfld = 28; // store sql fields +const int drq_s_sql_gfld = 29; // store sql fields +const int drq_s_triggers = 30; // store triggers +const int drq_s_trg_msgs = 31; // store trigger messages +const int drq_s_view_rels = 32; // store view relations +const int drq_e_dims = 33; // erase dimensions +const int drq_e_filters = 34; // erase filters +const int drq_e_func_args = 35; // erase functions +const int drq_e_funcs = 36; // erase function arguments +const int drq_l_fld_src = 37; // lookup a field source +const int drq_e_gfields = 38; // erase global fields +const int drq_e_indices = 39; // erase indices +const int drq_e_idx_segs = 40; // erase index segments +const int drq_l_dep_flds = 41; // lookup field referenced by view +const int drq_e_lfield = 42; // erase a local field +const int drq_e_rel_con2 = 43; // erase relation constraints +const int drq_e_rel_idxs = 44; // erase indices +const int drq_e_rel_flds = 45; // erase relation fields +const int drq_e_view_rels = 46; // erase view relations +const int drq_e_relation = 47; // erase relation +const int drq_e_rel_con3 = 48; // erase relation constraints +const int drq_e_usr_prvs = 49; // erase user privileges on relation +const int drq_e_shadow = 50; // erase shadow +///const int drq_e_trg_msgs = 51; // erase trigger messages +///const int drq_e_trigger = 52; // erase trigger +///const int drq_l_view_rel2 = 53; // lookup relations in view +///const int drq_m_rel_flds = 54; // modify relation fields +const int drq_e_trg_msg = 55; // erase trigger message +const int drq_e_class = 56; // erase security class +const int drq_l_grant1 = 57; // lookup grant +const int drq_l_grant2 = 58; // lookup grant +const int drq_s_grant = 59; // store grant const int drq_l_fld_src2 = 60; // lookup a field source const int drq_m_database = 61; // modify database -const int drq_m_gfield = 62; // modify global field -const int drq_m_index = 63; // modify index -const int drq_m_lfield = 64; // modify local field +const int drq_m_gfield = 62; // modify global field +const int drq_m_index = 63; // modify index +const int drq_m_lfield = 64; // modify local field const int drq_m_relation = 65; // modify relation -const int drq_m_trigger = 66; // modify trigger -const int drq_m_trg_msg = 67; // modify trigger message -const int drq_e_grant1 = 68; // erase grant -const int drq_e_grant2 = 69; // erase grant -const int drq_s_indices = 70; // store indices -const int drq_l_lfield = 71; // lookup local field +///const int drq_m_trigger = 66; // modify trigger +const int drq_m_trg_msg = 67; // modify trigger message +const int drq_e_grant1 = 68; // erase grant +const int drq_e_grant2 = 69; // erase grant +const int drq_s_indices = 70; // store indices +const int drq_l_lfield = 71; // lookup local field const int drq_s_idx_segs = 72; // store index segments -const int drq_l_unq_idx = 73; // lookup a unique index -const int drq_l_primary = 74; // lookup a primary something -const int drq_e_trg_msgs2 = 75; // erase trigger messages +const int drq_l_unq_idx = 73; // lookup a unique index +const int drq_l_primary = 74; // lookup a primary something +const int drq_e_trg_msgs2 = 75; // erase trigger messages const int drq_e_trigger2 = 76; // erase trigger -const int drq_s_prcs = 77; // store procedure +///const int drq_s_prcs = 77; // store procedure const int drq_l_prc_name = 78; // lookup procedure name -const int drq_s_prc_usr_prvs = 79; // store procedure priviledges -const int drq_s_prms = 80; // store parameters -const int drq_e_prcs = 81; // erase procedure -const int drq_e_prms = 82; // erase all of procedure's parameters -const int drq_s_prm_src = 83; // store parameter global field -//const int drq_s_intl_info = 84; // store RDB$CHARACTER_FIELDS -const int drq_m_prcs = 85; // modify procedure -//const int drq_s_log_files = 86; // store log files -//const int drq_s_cache = 87; // store cache -const int drq_e_prm = 88; // erase a procedure parameter -const int drq_s_xcp = 89; // store an exception -const int drq_m_xcp = 90; // modify an exception -const int drq_e_prc_prvs = 91; // erase user privileges on procedure -const int drq_e_prc_prv = 92; // erase procedure's privileges -const int drq_e_trg_prv = 93; // erase trigger's privileges -//const int drq_d_log = 94; // drop log -//const int drq_d_cache = 95; // drop cache -//const int drq_l_log_files = 96; // lookup log files -//const int drq_l_cache = 97; // lookup cache -//const int drq_e_sec_class = 98; // delete security classes -//const int drq_l_gfield = 99; // lookup global field -const int drq_g_nxt_con = 100; // generate next relation constraint name -const int drq_g_nxt_fld = 101; // generate next field name -const int drq_g_nxt_idx = 102; // generate next index name -const int drq_g_nxt_trg = 103; // generate next trigger name -const int drq_l_fld_pos = 104; // lookup max field position -const int drq_e_xcp = 105; // drop a exception -const int drq_d_gfields = 106; // drop a global field for procedure param -const int drq_l_shadow = 107; // look up a shadow set -const int drq_l_files = 108; // look up for defined files -const int drq_e_l_idx = 109; // erase indices defined on a local field -//const int drq_l_idx_seg = 110; // Lookup index segments -const int drq_e_l_gfld = 111; // erase global field for a local fields -const int drq_gcg1 = 112; // grantor_can_grant -const int drq_gcg2 = 113; // grantor_can_grant -const int drq_gcg3 = 114; // grantor_can_grant -const int drq_gcg4 = 115; // grantor_can_grant -const int drq_gcg5 = 116; // grantor_can_grant -const int drq_l_view_idx = 117; // table is view? -const int drq_role_gens = 118; // store SQL role -const int drq_get_role_nm = 119; // get SQL role -const int drq_get_role_au = 120; // get SQL role auth -const int drq_del_role_1 = 121; // delete SQL role from rdb$user_privilege -const int drq_drop_role = 122; // delete SQL role from rdb$roles -const int drq_get_rel_owner = 123; // get the owner of any relations -const int drq_get_user_priv = 124; // get the grantor of user privileges or +const int drq_s_prc_usr_prvs = 79; // store procedure privileges +///const int drq_s_prms = 80; // store parameters +///const int drq_e_prcs = 81; // erase procedure +///const int drq_e_prms = 82; // erase all of procedure's parameters +///const int drq_s_prm_src = 83; // store parameter global field +const int drq_s_intl_info = 84; // store RDB$CHARACTER_FIELDS +///const int drq_m_prcs = 85; // modify procedure +const int drq_s_log_files = 86; // store log files +const int drq_s_cache = 87; // store cache +///const int drq_e_prm = 88; // erase a procedure parameter +const int drq_s_xcp = 89; // store an exception +const int drq_m_xcp = 90; // modify an exception +///const int drq_e_prc_prvs = 91; // erase user privileges on procedure +///const int drq_e_prc_prv = 92; // erase procedure's privileges +const int drq_e_trg_prv = 93; // erase trigger's privileges +const int drq_d_log = 94; // drop log +const int drq_d_cache = 95; // drop cache +const int drq_l_log_files = 96; // lookup log files +const int drq_l_cache = 97; // lookup cache +const int drq_e_sec_class = 98; // delete security classes +const int drq_l_gfield = 99; // lookup global field +const int drq_g_nxt_con = 100; // generate next relation constraint name +const int drq_g_nxt_fld = 101; // generate next field name +const int drq_g_nxt_idx = 102; // generate next index name +const int drq_g_nxt_trg = 103; // generate next trigger name +const int drq_l_fld_pos = 104; // lookup max field position +const int drq_e_xcp = 105; // drop an exception +///const int drq_d_gfields = 106; // drop an global field for procedure param +const int drq_l_shadow = 107; // look up a shadow set +const int drq_l_files = 108; // look up for defined files +const int drq_e_l_idx = 109; // erase indices defined on a local field +const int drq_l_idx_seg = 110; // Lookup index segments +const int drq_e_l_gfld = 111; // erase global field for a local fields +const int drq_gcg1 = 112; // grantor_can_grant +const int drq_gcg2 = 113; // grantor_can_grant +const int drq_gcg3 = 114; // grantor_can_grant +const int drq_gcg4 = 115; // grantor_can_grant +const int drq_gcg5 = 116; // grantor_can_grant +const int drq_l_view_idx = 117; // table is view? +const int drq_role_gens = 118; // store SQL role +const int drq_get_role_nm = 119; // get SQL role +const int drq_get_role_au = 120; // get SQL role auth +const int drq_del_role_1 = 121; // delete SQL role from rdb$user_privilege +const int drq_drop_role = 122; // delete SQL role from rdb$roles +const int drq_get_rel_owner = 123; // get the owner of any relations +const int drq_get_user_priv = 124; // get the grantor of user privileges or // the user who was granted the privileges -const int drq_g_rel_constr_nm = 125; // get relation constraint name -const int drq_e_rel_const = 126; // erase relation constraints -const int drq_e_gens = 127; // erase generators -const int drq_s_f_class = 128; // set the security class name for a field -const int drq_s_u_class = 129; // find a unique security class name for a field -const int drq_l_difference = 130; // Look up a backup difference file -const int drq_s_difference = 131; // Store backup difference file -const int drq_d_difference = 132; // Delete backup difference file -const int drq_l_fld_src3 = 133; // lookup a field source -const int drq_e_fld_prvs = 134; // erase user privileges on relation field -const int drq_e_view_prv = 135; // erase view's privileges -const int drq_m_chset = 136; // modify charset -const int drq_m_coll = 137; // modify collation -const int drq_m_bfil = 138; // modify blob filter -const int drq_m_fun = 139; // modify udf -const int drq_m_gen = 140; // modify generator -const int drq_m_prm = 141; // modify procedure's parameter -const int drq_m_rol = 142; // modify sql role -const int drq_m_view = 143; // modify view -const int drq_s_colls = 144; // store collations -const int drq_l_charset = 145; // lookup charset -const int drq_dom_is_array = 146; // lookup domain to see if it's an array -const int drq_l_rel_info = 147; // lookup name and flags of one master relation -const int drq_l_rel_info2 = 148; // lookup names and flags of all master relations -const int drq_l_rel_type = 149; // lookup relation type -const int drq_e_colls = 150; // erase collations -const int drq_l_rfld_coll = 151; // lookup relation field collation -const int drq_l_fld_coll = 152; // lookup field collation -const int drq_l_prp_src = 153; // lookup a procedure parameter source -const int drq_s_prms2 = 154; // store parameters (ODS 11.1) -const int drq_l_prm_coll = 155; // lookup procedure parameter collation -const int drq_s_prms3 = 156; // store parameters (ODS 11.2) -const int drq_d_gfields2 = 157; // drop a global field for procedure param (ODS 11.2) -const int drq_m_map = 158; // modify os=>db names mapping -const int drq_l_idx_name = 159; // lookup index name -const int drq_l_collation = 160; // DSQL/DdlNodes: lookup collation -const int drq_m_charset = 161; // DSQL/DdlNodes: modify character set -const int drq_g_nxt_gen_id = 162; // generate next generator id -const int drq_g_nxt_prc_id = 163; // generate next procedure id -const int drq_g_nxt_xcp_id = 164; // generate next exception id -const int drq_l_xcp_name = 165; // lookup exception name -const int drq_l_gen_name = 166; // lookup generator name -const int drq_e_grant3 = 167; // revoke all on all -const int drq_MAX = 168; +const int drq_g_rel_constr_nm= 125; // get relation constraint name +const int drq_e_rel_const = 126; // erase relation constraints +const int drq_e_gens = 127; // erase generators +const int drq_s_f_class = 128; // set the security class name for a field +const int drq_s_u_class = 129; // find a unique security class name for a field +const int drq_l_difference = 130; // Look up a backup difference file +const int drq_s_difference = 131; // Store backup difference file +const int drq_d_difference = 132; // Delete backup difference file +const int drq_l_fld_src3 = 133; // lookup a field source +const int drq_e_fld_prvs = 134; // erase user privileges on relation field +const int drq_e_view_prv = 135; // erase view's privileges +const int drq_m_chset = 136; // modify charset +const int drq_m_coll = 137; // modify collation +const int drq_m_bfil = 138; // modify blob filter +const int drq_m_fun = 139; // modify udf +const int drq_m_gen = 140; // modify generator +///const int drq_m_prm = 141; // modify procedure's parameter +const int drq_m_rol = 142; // modify sql role +const int drq_m_view = 143; // modify view +const int drq_s_colls = 144; // store collations +const int drq_l_charset = 145; // lookup charset +const int drq_dom_is_array = 146; // lookup domain to see if it's an array +const int drq_l_rel_info = 147; // lookup name and flags of one master relation +const int drq_l_rel_info2 = 148; // lookup names and flags of all master relations +const int drq_l_rel_type = 149; // lookup relation type +const int drq_e_colls = 150; // erase collations +const int drq_l_rfld_coll = 151; // lookup relation field collation +const int drq_l_fld_coll = 152; // lookup field collation +const int drq_l_prp_src = 153; // lookup a procedure parameter source +///const int drq_s_prms2 = 154; // store parameters (ODS 11.1) +const int drq_l_prm_coll = 155; // lookup procedure parameter collation +///const int drq_s_prms3 = 156; // store parameters (ODS 11.2) +///const int drq_d_gfields2 = 157; // drop a global field for procedure param (ODS 11.2) +const int drq_m_map = 158; // modify os=>db names mapping +const int drq_l_idx_name = 159; // lookup index name +const int drq_l_collation = 160; // lookup character set +const int drq_m_charset = 161; // modify character set +const int drq_g_nxt_gen_id = 162; // generate next generator id +const int drq_g_nxt_prc_id = 163; // generate next procedure id +const int drq_g_nxt_xcp_id = 164; // generate next exception id +const int drq_l_xcp_name = 165; // lookup exception name +const int drq_l_gen_name = 166; // lookup generator name +const int drq_e_grant3 = 167; // revoke all on all +const int drq_s_funcs2 = 168; // store functions (CreateAlterFunctionNode) +const int drq_s_func_args2 = 169; // store function arguments (CreateAlterFunctionNode) +const int drq_m_funcs2 = 170; // modify functions (CreateAlterFunctionNode) +const int drq_e_func_args2 = 171; // erase function arguments (CreateAlterFunctionNode) +const int drq_s_prcs2 = 172; +const int drq_s_prms4 = 173; +const int drq_s_prm_src2 = 174; +const int drq_m_prcs2 = 175; +const int drq_e_prms2 = 176; +const int drq_s_triggers2 = 177; +const int drq_m_trigger2 = 178; +const int drq_d_gfields3 = 179; +const int drq_d_gfields4 = 180; +const int drq_e_prcs2 = 181; +const int drq_e_prc_prvs2 = 182; +const int drq_e_prc_prv2 = 183; +const int drq_e_trg_msgs3 = 184; +const int drq_e_trigger3 = 185; +const int drq_e_trg_prv2 = 186; +const int drq_l_view_rel3 = 187; +const int drq_m_rel_flds2 = 188; +const int drq_e_trg_prv3 = 189; +const int drq_s_pkg = 190; // store package +const int drq_e_pkg = 191; // erase package +const int drq_m_pkg_body = 192; // create package body +const int drq_m_pkg_body2 = 193; // drop package body +const int drq_m_pkg_prc = 194; // drop package body +const int drq_m_pkg_fun = 195; // drop package body +const int drq_m_pkg = 196; // alter package +const int drq_l_pkg_funcs = 197; // lookup packaged functions +const int drq_l_pkg_func_args = 198; // lookup packaged function arguments +const int drq_l_pkg_procs = 199; // lookup packaged procedures +const int drq_l_pkg_proc_args = 200; // lookup packaged procedure arguments +const int drq_s_pkg_usr_prvs= 201; // store package privileges +const int drq_e_pkg_prv = 202; // erase package privileges +const int drq_m_pkg_prm_defs = 203; // modify packaged procedure parameters defaults +const int drq_MAX = 204; #endif // JRD_DRQ_H diff --git a/src/jrd/dsc.h b/src/jrd/dsc.h index 98721a3763..86afca0904 100644 --- a/src/jrd/dsc.h +++ b/src/jrd/dsc.h @@ -212,6 +212,14 @@ typedef struct dsc dsc_address = (UCHAR*) address; } + void makeDate(GDS_DATE* address = NULL) + { + clear(); + dsc_dtype = dtype_sql_date; + dsc_length = sizeof(GDS_DATE); + dsc_address = (UCHAR*) address; + } + void makeDouble(double* address = NULL) { clear(); @@ -267,6 +275,15 @@ typedef struct dsc dsc_address = address; } + void makeTime(GDS_TIME* address = NULL) + { + clear(); + dsc_dtype = dtype_sql_time; + dsc_length = sizeof(GDS_TIME); + dsc_scale = 0; + dsc_address = (UCHAR*) address; + } + void makeTimestamp(GDS_TIMESTAMP* address = NULL) { clear(); diff --git a/src/jrd/dyn.epp b/src/jrd/dyn.epp index 5bc493586a..6aef79c033 100644 --- a/src/jrd/dyn.epp +++ b/src/jrd/dyn.epp @@ -42,6 +42,7 @@ #include #include "../jrd/common.h" +#include "../dsql/DdlNodes.h" #include "../jrd/jrd.h" #include "../jrd/ods.h" #include "../jrd/tra.h" @@ -100,7 +101,8 @@ static void set_field_class_name(Global*, const MetaName&, const MetaName&); static void dyn_user(Global*, const UCHAR**); -void DYN_ddl(/*Attachment* attachment,*/ jrd_tra* transaction, USHORT length, const UCHAR* ddl) +void DYN_ddl(/*Attachment* attachment,*/ jrd_tra* transaction, USHORT length, const UCHAR* ddl, + const string& sqlText) { /************************************** * @@ -124,7 +126,7 @@ void DYN_ddl(/*Attachment* attachment,*/ jrd_tra* transaction, USHORT length, co fb_utils::init_status(tdbb->tdbb_status_vector); - Global gbl(transaction); + Global gbl(transaction, sqlText); // Create a pool for DYN to operate in. It will be released when // the routine exits. @@ -402,7 +404,7 @@ void DYN_execute(Global* gbl, case isc_dyn_def_rel: case isc_dyn_def_view: - DYN_define_relation(gbl, ptr); + DYN_define_relation(gbl, ptr, verb == isc_dyn_def_view); break; case isc_dyn_mod_rel: @@ -461,10 +463,6 @@ void DYN_execute(Global* gbl, DYN_modify_function(gbl, ptr); break; - case isc_dyn_delete_function: - DYN_delete_function(gbl, ptr); - break; - case isc_dyn_def_generator: DYN_define_generator(gbl, ptr); break; @@ -489,30 +487,6 @@ void DYN_execute(Global* gbl, DYN_delete_role(gbl, ptr); break; - case isc_dyn_def_procedure: - DYN_define_procedure(gbl, ptr); - break; - - case isc_dyn_mod_procedure: - DYN_modify_procedure(gbl, ptr); - break; - - case isc_dyn_delete_procedure: - DYN_delete_procedure(gbl, ptr); - break; - - case isc_dyn_def_parameter: - DYN_define_parameter(gbl, ptr, procedure_name); - break; - - case isc_dyn_mod_prc_parameter: - DYN_modify_parameter(gbl, ptr); - break; - - case isc_dyn_delete_parameter: - DYN_delete_parameter(gbl, ptr, procedure_name); - break; - case isc_dyn_def_shadow: DYN_define_shadow(gbl, ptr); break; @@ -525,14 +499,6 @@ void DYN_execute(Global* gbl, DYN_define_trigger(gbl, ptr, relation_name, NULL, false); break; - case isc_dyn_mod_trigger: - DYN_modify_trigger(gbl, ptr); - break; - - case isc_dyn_delete_trigger: - DYN_delete_trigger(gbl, ptr); - break; - case isc_dyn_def_trigger_msg: DYN_define_trigger_msg(gbl, ptr, trigger_name); break; @@ -541,10 +507,6 @@ void DYN_execute(Global* gbl, DYN_modify_trigger_msg(gbl, ptr, trigger_name); break; - case isc_dyn_delete_trigger_msg: - DYN_delete_trigger_msg(gbl, ptr, trigger_name); - break; - case isc_dyn_def_global_fld: DYN_define_global_field(gbl, ptr, relation_name, field_name); break; @@ -958,7 +920,7 @@ USHORT DYN_put_text_blob(Global* gbl, const UCHAR** ptr, bid* blob_id) p += 2; *p++ = isc_bpb_source_interp; *p++ = 1; - *p++ = tdbb->getAttachment()->att_charset; + *p++ = tdbb->getCharSet(); *p++ = isc_bpb_target_type; *p++ = 2; @@ -1097,6 +1059,11 @@ static void grant( Global* gbl, const UCHAR** ptr) GET_STRING(ptr, object); break; + case isc_dyn_pkg_name: + obj_type = obj_package_header; + GET_STRING(ptr, object); + break; + case isc_dyn_fld_name: GET_STRING(ptr, field); break; @@ -1165,6 +1132,11 @@ static void grant( Global* gbl, const UCHAR** ptr) } break; + case isc_dyn_grant_package: + user_type = obj_package_header; + GET_STRING(ptr, user); + break; + case isc_dyn_grant_proc: user_type = obj_procedure; GET_STRING(ptr, user); @@ -1822,6 +1794,11 @@ static void revoke_permission(Global* gbl, const UCHAR** ptr) GET_STRING(ptr, object); break; + case isc_dyn_pkg_name: + obj_type = obj_package_header; + GET_STRING(ptr, object); + break; + case isc_dyn_fld_name: GET_STRING(ptr, field); break; @@ -1888,6 +1865,11 @@ static void revoke_permission(Global* gbl, const UCHAR** ptr) } break; + case isc_dyn_grant_package: + user_type = obj_package_header; + GET_STRING(ptr, user); + break; + case isc_dyn_grant_proc: user_type = obj_procedure; GET_STRING(ptr, user); @@ -2295,7 +2277,7 @@ static void store_privilege(Global* gbl, } -static void dyn_user(Global* /*gbl*/, const UCHAR** ptr) +static void dyn_user(Global* gbl, const UCHAR** ptr) { /************************************** * @@ -2311,8 +2293,6 @@ static void dyn_user(Global* /*gbl*/, const UCHAR** ptr) Database* const dbb = tdbb->getDatabase(); jrd_tra* const tra = tdbb->getTransaction(); - Database::Checkout dcoHolder(dbb); - ISC_STATUS_ARRAY status; try { @@ -2323,7 +2303,7 @@ static void dyn_user(Global* /*gbl*/, const UCHAR** ptr) string text; GET_STRING(ptr, text); - switch(verb) + switch (verb) { case isc_dyn_user_add: text.upper(); @@ -2397,8 +2377,37 @@ static void dyn_user(Global* /*gbl*/, const UCHAR** ptr) } } + int ddlAction = 0; + + switch (userData->operation) + { + case ADD_OPER: + ddlAction = DDL_TRIGGER_CREATE_USER; + break; + + case MOD_OPER: + ddlAction = DDL_TRIGGER_ALTER_USER; + break; + + case DEL_OPER: + ddlAction = DDL_TRIGGER_DROP_USER; + break; + } + + if (ddlAction != 0) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + ddlAction, userData->user_name, gbl->sqlText); + } + USHORT id = tra->getUserManagement()->put(userData); DFW_post_work(tra, dfw_user_management, NULL, id); + + if (ddlAction != 0) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + ddlAction, userData->user_name, gbl->sqlText); + } } catch (const Exception& e) { diff --git a/src/jrd/dyn.h b/src/jrd/dyn.h index 89caa26bdc..bac0f0fcf6 100644 --- a/src/jrd/dyn.h +++ b/src/jrd/dyn.h @@ -46,13 +46,15 @@ class jrd_tra; class Global { public: - Global(jrd_tra* t) //, const UCHAR* dyn, size_t length) - : gbl_transaction(t)//, gbl_length(length), gbl_end(dyn + length) + Global(jrd_tra* t, const Firebird::string& aSqlText) //, const UCHAR* dyn, size_t length) + : gbl_transaction(t),// gbl_length(length), gbl_end(dyn + length) + sqlText(aSqlText) { } jrd_tra* const gbl_transaction; //size_t gbl_length; // length of BLR stream //const UCHAR* const gbl_end; // end of BLR sream + Firebird::string sqlText; }; class dyn_fld diff --git a/src/jrd/dyn_def.epp b/src/jrd/dyn_def.epp index c6dd8d3b1b..2ba03e9330 100644 --- a/src/jrd/dyn_def.epp +++ b/src/jrd/dyn_def.epp @@ -78,6 +78,7 @@ #include "../common/utils_proto.h" #include "../jrd/IntlManager.h" #include "../jrd/IntlUtil.h" +#include "../dsql/DdlNodes.h" using MsgFormat::SafeArg; @@ -118,7 +119,6 @@ static const UCHAR who_blr[] = }; -static void check_unique_name(thread_db*, Global*, const Firebird::MetaName&, int); static bool get_who(thread_db*, Global*, Firebird::MetaName&); static bool is_it_user_name(Global*, const Firebird::MetaName&, thread_db*); @@ -163,6 +163,9 @@ void DYN_define_collation( Global* gbl, const UCHAR** ptr) DYN_error_punt(false, 212); /* msg 212: "Zero length identifiers not allowed" */ + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_COLLATION, collation_name, gbl->sqlText); + jrd_req* request = CMP_find_request(tdbb, drq_s_colls, DYN_REQUESTS); bool b_ending_store = false; @@ -392,6 +395,9 @@ void DYN_define_collation( Global* gbl, const UCHAR** ptr) } throw; } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_COLLATION, collation_name, gbl->sqlText); } @@ -915,11 +921,14 @@ void DYN_define_exception( Global* gbl, const UCHAR** ptr) // msg 212: "Zero length identifiers not allowed" } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_EXCEPTION, exception_name, gbl->sqlText); + bool b_ending_store = false; try { - check_unique_name(tdbb, gbl, exception_name, obj_exception); + DYN_UTIL_check_unique_name(tdbb, gbl->gbl_transaction, exception_name, obj_exception); jrd_req* request = CMP_find_request(tdbb, drq_s_xcp, DYN_REQUESTS); @@ -946,7 +955,7 @@ void DYN_define_exception( Global* gbl, const UCHAR** ptr) { try { - SINT64 xcp_id = DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_xcp_id, "RDB$EXCEPTIONS"); + SINT64 xcp_id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_xcp_id, "RDB$EXCEPTIONS"); xcp_id %= (MAX_SSHORT + 1); @@ -995,7 +1004,7 @@ void DYN_define_exception( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_s_xcp) = request; } - } + } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (b_ending_store) { @@ -1004,6 +1013,9 @@ void DYN_define_exception( Global* gbl, const UCHAR** ptr) } throw; } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_EXCEPTION, exception_name, gbl->sqlText); } @@ -1345,6 +1357,9 @@ void DYN_define_function( Global* gbl, const UCHAR** ptr) // msg 212: "Zero length identifiers not allowed" } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_FUNCTION, function_name, gbl->sqlText); + jrd_req* request = CMP_find_request(tdbb, drq_s_funcs, DYN_REQUESTS); bool b_ending_store = false; @@ -1361,6 +1376,7 @@ void DYN_define_function( Global* gbl, const UCHAR** ptr) X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$ENGINE_NAME.NULL = TRUE; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) @@ -1418,6 +1434,9 @@ void DYN_define_function( Global* gbl, const UCHAR** ptr) } throw; } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_FUNCTION, function_name, gbl->sqlText); } @@ -1575,11 +1594,14 @@ void DYN_define_generator( Global* gbl, const UCHAR** ptr) /* msg 212: "Zero length identifiers not allowed" */ } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_SEQUENCE, generator_name, gbl->sqlText); + bool b_ending_store = false; try { - check_unique_name(tdbb, gbl, generator_name, obj_generator); + DYN_UTIL_check_unique_name(tdbb, gbl->gbl_transaction, generator_name, obj_generator); jrd_req* request = CMP_find_request(tdbb, drq_s_gens, DYN_REQUESTS); @@ -1589,7 +1611,7 @@ void DYN_define_generator( Global* gbl, const UCHAR** ptr) { try { - SINT64 gen_id = DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_gen_id, "RDB$GENERATORS"); + SINT64 gen_id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_gen_id, "RDB$GENERATORS"); gen_id %= (MAX_SSHORT + 1); @@ -1631,7 +1653,7 @@ void DYN_define_generator( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_s_gens) = request; } - } + } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (b_ending_store) { @@ -1646,6 +1668,9 @@ void DYN_define_generator( Global* gbl, const UCHAR** ptr) DYN_error_punt(true, 9); /* msg 9: "DEFINE GENERATOR unexpected dyn verb" */ } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_SEQUENCE, generator_name, gbl->sqlText); } @@ -1684,6 +1709,9 @@ void DYN_define_global_field(Global* gbl, /* msg 212: "Zero length identifiers not allowed" */ } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_DOMAIN, global_field_name, gbl->sqlText); + jrd_req* request = CMP_find_request(tdbb, drq_s_gfields, DYN_REQUESTS); bool b_ending_store = false; @@ -1903,7 +1931,7 @@ void DYN_define_global_field(Global* gbl, DYN_REQUEST(drq_s_gfields) = request; } - } + } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (b_ending_store) { @@ -1913,6 +1941,9 @@ void DYN_define_global_field(Global* gbl, } throw; } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_DOMAIN, global_field_name, gbl->sqlText); } @@ -1960,13 +1991,16 @@ void DYN_define_index(Global* gbl, /* msg 212: "Zero length identifiers not allowed" */ } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_INDEX, index_name, gbl->sqlText); + jrd_req* request = NULL; SSHORT id = -1; try { id = drq_l_idx_name; - check_unique_name(tdbb, gbl, index_name, obj_index); + DYN_UTIL_check_unique_name(tdbb, gbl->gbl_transaction, index_name, obj_index); request = CMP_find_request(tdbb, drq_s_indices, DYN_REQUESTS); id = drq_s_indices; @@ -2509,6 +2543,9 @@ void DYN_define_index(Global* gbl, fb_assert(false); } } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_INDEX, index_name, gbl->sqlText); } @@ -2845,651 +2882,7 @@ void DYN_define_local_field(Global* gbl, } -void DYN_define_parameter( Global* gbl, const UCHAR** ptr, Firebird::MetaName* procedure_name) -{ -/************************************** - * - * D Y N _ d e f i n e _ p a r a m e t e r - * - ************************************** - * - * Functional description - * Define the parameters for a stored procedure. - * - **************************************/ - // Leave these as USHORT. Don't convert the *_null ones to bool. - USHORT f_length, f_type, f_charlength, f_seg_length, - f_scale_null, f_subtype_null, f_seg_length_null, - f_charlength_null, f_precision_null, f_charset_null, f_collation_null, f_notnull_null; - SSHORT id; - SSHORT f_subtype, f_scale, f_precision; - SSHORT f_charset, f_collation; - USHORT f_notnull; - const UCHAR* default_value_ptr = NULL; - const UCHAR* default_source_ptr = NULL; - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - Firebird::MetaName parameter_name; - GET_STRING(ptr, parameter_name); - - if (parameter_name.length() == 0) { - DYN_error_punt(false, 212); - /* msg 212: "Zero length identifiers not allowed" */ - } - - jrd_req* request = CMP_find_request(tdbb, drq_s_prms, DYN_REQUESTS); - - try { - - id = -1; - - f_length = f_type = f_subtype = f_charlength = f_scale = f_seg_length = 0; - f_charset = f_collation = f_precision = 0; - f_notnull = FALSE; - f_scale_null = f_subtype_null = f_charlength_null = f_seg_length_null = TRUE; - f_precision_null = f_charset_null = f_collation_null = f_notnull_null = TRUE; - id = drq_s_prms; - - Firebird::MetaName prc_name, rel_name, fld_name; - prm_mech_t mechanism = prm_mech_normal; - bool explicit_domain = false; - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURE_PARAMETERS USING - strcpy(P.RDB$PARAMETER_NAME, parameter_name.c_str()); - if (procedure_name) { - P.RDB$PROCEDURE_NAME.NULL = FALSE; - strcpy(P.RDB$PROCEDURE_NAME, procedure_name->c_str()); - prc_name = *procedure_name; - } - else - P.RDB$PROCEDURE_NAME.NULL = TRUE; - P.RDB$PARAMETER_NUMBER.NULL = TRUE; - P.RDB$PARAMETER_TYPE.NULL = TRUE; - P.RDB$FIELD_SOURCE.NULL = TRUE; - P.RDB$SYSTEM_FLAG = 0; - P.RDB$SYSTEM_FLAG.NULL = FALSE; - P.RDB$DESCRIPTION.NULL = TRUE; - - UCHAR verb; - while ((verb = *(*ptr)++) != isc_dyn_end) - { - switch (verb) - { - case isc_dyn_system_flag: - P.RDB$SYSTEM_FLAG = (SSHORT)DYN_get_number(ptr); - P.RDB$SYSTEM_FLAG.NULL = FALSE; - break; - - case isc_dyn_prm_number: - P.RDB$PARAMETER_NUMBER = (SSHORT)DYN_get_number(ptr); - P.RDB$PARAMETER_NUMBER.NULL = FALSE; - break; - - case isc_dyn_prm_type: - P.RDB$PARAMETER_TYPE = (SSHORT)DYN_get_number(ptr); - P.RDB$PARAMETER_TYPE.NULL = FALSE; - break; - - case isc_dyn_prc_name: - GET_STRING(ptr, P.RDB$PROCEDURE_NAME); - P.RDB$PROCEDURE_NAME.NULL = FALSE; - prc_name = P.RDB$PROCEDURE_NAME; - break; - - case isc_dyn_fld_not_null: - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) < ODS_11_1) - { - // Feature not supported on ODS version older than %d.%d - ERR_post(Arg::Gds(isc_dsql_feature_not_supported_ods) << Arg::Num(11) << - Arg::Num(1)); - } - - f_notnull = TRUE; - f_notnull_null = FALSE; - break; - - case isc_dyn_fld_source: - GET_STRING(ptr, P.RDB$FIELD_SOURCE); - P.RDB$FIELD_SOURCE.NULL = FALSE; - explicit_domain = true; - break; - - case isc_dyn_fld_length: - f_length = (USHORT)DYN_get_number(ptr); - break; - - case isc_dyn_fld_type: - f_type = (USHORT)DYN_get_number(ptr); - switch (f_type) - { - case blr_short: - f_length = 2; - break; - - case blr_long: - case blr_float: - case blr_sql_time: - case blr_sql_date: - f_length = 4; - break; - - case blr_int64: - case blr_quad: - case blr_timestamp: - case blr_double: - case blr_d_float: - f_length = 8; - break; - - default: - if (f_type == blr_blob) - f_length = 8; - break; - } - break; - - case isc_dyn_fld_scale: - f_scale = (SSHORT)DYN_get_number(ptr); - f_scale_null = FALSE; - break; - - case isc_dyn_fld_precision: - f_precision = (SSHORT)DYN_get_number(ptr); - f_precision_null = FALSE; - break; - - case isc_dyn_fld_sub_type: - f_subtype = (SSHORT)DYN_get_number(ptr); - f_subtype_null = FALSE; - break; - - case isc_dyn_fld_char_length: - f_charlength = (USHORT)DYN_get_number(ptr); - f_charlength_null = FALSE; - break; - - case isc_dyn_fld_character_set: - f_charset = (SSHORT)DYN_get_number(ptr); - f_charset_null = FALSE; - break; - - case isc_dyn_fld_collation: - f_collation = (SSHORT)DYN_get_number(ptr); - f_collation_null = FALSE; - break; - - case isc_dyn_fld_segment_length: - f_seg_length = (USHORT)DYN_get_number(ptr); - f_seg_length_null = FALSE; - break; - - case isc_dyn_description: - DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION); - P.RDB$DESCRIPTION.NULL = FALSE; - break; - - case isc_dyn_fld_default_value: - default_value_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - case isc_dyn_fld_default_source: - default_source_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - case isc_dyn_prm_mechanism: - mechanism = (prm_mech_t) DYN_get_number(ptr); - break; - - case isc_dyn_rel_name: - GET_STRING(ptr, rel_name); - break; - - case isc_dyn_fld_name: - GET_STRING(ptr, fld_name); - break; - - default: - --(*ptr); - DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, procedure_name); - } - } - - if (P.RDB$FIELD_SOURCE.NULL) { - /* Need to store dummy global field */ - jrd_req* old_request = request; - id = drq_s_prm_src; - request = CMP_find_request(tdbb, drq_s_prm_src, DYN_REQUESTS); - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PS IN RDB$FIELDS USING - - // ODS_11_1 - P.RDB$NULL_FLAG.NULL = f_notnull_null; - P.RDB$NULL_FLAG = f_notnull; - - PS.RDB$SYSTEM_FLAG = 0; - PS.RDB$SYSTEM_FLAG.NULL = FALSE; - DYN_UTIL_generate_field_name(tdbb, gbl, PS.RDB$FIELD_NAME); - strcpy(P.RDB$FIELD_SOURCE, PS.RDB$FIELD_NAME); - P.RDB$FIELD_SOURCE.NULL = FALSE; - PS.RDB$FIELD_LENGTH = f_length; - PS.RDB$FIELD_TYPE = f_type; - PS.RDB$FIELD_SUB_TYPE = f_subtype; - PS.RDB$FIELD_SUB_TYPE.NULL = f_subtype_null; - PS.RDB$FIELD_SCALE = f_scale; - PS.RDB$FIELD_SCALE.NULL = f_scale_null; - PS.RDB$FIELD_PRECISION = f_precision; - PS.RDB$FIELD_PRECISION.NULL = f_precision_null; - PS.RDB$SEGMENT_LENGTH = f_seg_length; - PS.RDB$SEGMENT_LENGTH.NULL = f_seg_length_null; - PS.RDB$CHARACTER_LENGTH = f_charlength; - PS.RDB$CHARACTER_LENGTH.NULL = f_charlength_null; - PS.RDB$CHARACTER_SET_ID = f_charset; - PS.RDB$CHARACTER_SET_ID.NULL = f_charset_null; - - PS.RDB$COLLATION_ID = f_collation; - PS.RDB$COLLATION_ID.NULL = f_collation_null; - - PS.RDB$DEFAULT_VALUE.NULL = (default_value_ptr == NULL) ? TRUE : FALSE; - if (default_value_ptr) { - DYN_put_blr_blob(gbl, &default_value_ptr, &PS.RDB$DEFAULT_VALUE); - } - - PS.RDB$DEFAULT_SOURCE.NULL = (default_source_ptr == NULL) ? TRUE : FALSE; - if (default_source_ptr) { - DYN_put_text_blob(gbl, &default_source_ptr, &PS.RDB$DEFAULT_SOURCE); - } - END_STORE; - if (!DYN_REQUEST(drq_s_prm_src)) - DYN_REQUEST(drq_s_prm_src) = request; - id = drq_s_prms; - request = old_request; - } - END_STORE; - - if (!DYN_REQUEST(drq_s_prms)) { - DYN_REQUEST(drq_s_prms) = request; - } - - request = NULL; - id = -1; - - if (explicit_domain) - { - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) - { - request = CMP_find_request(tdbb, drq_s_prms2, DYN_REQUESTS); - id = drq_s_prms2; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURE_PARAMETERS WITH - P.RDB$PROCEDURE_NAME EQ prc_name.c_str() AND - P.RDB$PARAMETER_NAME EQ parameter_name.c_str() - - if (!DYN_REQUEST(drq_s_prms2)) - DYN_REQUEST(drq_s_prms2) = request; - - MODIFY P USING - P.RDB$COLLATION_ID.NULL = f_collation_null; - P.RDB$COLLATION_ID = f_collation; - - P.RDB$DEFAULT_VALUE.NULL = (default_value_ptr == NULL) ? TRUE : FALSE; - if (default_value_ptr) - DYN_put_blr_blob(gbl, &default_value_ptr, &P.RDB$DEFAULT_VALUE); - - P.RDB$DEFAULT_SOURCE.NULL = (default_source_ptr == NULL) ? TRUE : FALSE; - if (default_source_ptr) - DYN_put_text_blob(gbl, &default_source_ptr, &P.RDB$DEFAULT_SOURCE); - - P.RDB$NULL_FLAG.NULL = f_notnull_null; - P.RDB$NULL_FLAG = f_notnull; - - P.RDB$PARAMETER_MECHANISM.NULL = FALSE; - P.RDB$PARAMETER_MECHANISM = (USHORT) mechanism; - END_MODIFY - END_FOR - - if (!DYN_REQUEST(drq_s_prms2)) - DYN_REQUEST(drq_s_prms2) = request; - } - else - { - // Feature not supported on ODS version older than %d.%d - ERR_post(Arg::Gds(isc_dsql_feature_not_supported_ods) << Arg::Num(11) << Arg::Num(1)); - } - } - - if (rel_name.hasData() && fld_name.hasData()) - { - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2) - { - request = CMP_find_request(tdbb, drq_s_prms3, DYN_REQUESTS); - id = drq_s_prms3; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURE_PARAMETERS WITH - P.RDB$PROCEDURE_NAME EQ prc_name.c_str() AND - P.RDB$PARAMETER_NAME EQ parameter_name.c_str() - - if (!DYN_REQUEST(drq_s_prms3)) - DYN_REQUEST(drq_s_prms3) = request; - - MODIFY P USING - P.RDB$RELATION_NAME.NULL = FALSE; - strcpy(P.RDB$RELATION_NAME, rel_name.c_str()); - - P.RDB$FIELD_NAME.NULL = FALSE; - strcpy(P.RDB$FIELD_NAME, fld_name.c_str()); - END_MODIFY - END_FOR - - if (!DYN_REQUEST(drq_s_prms3)) - DYN_REQUEST(drq_s_prms3) = request; - } - else - { - request = NULL; - id = -1; - - ERR_post(Arg::Gds(isc_dsql_feature_not_supported_ods) << Arg::Num(11) << Arg::Num(2)); - } - } - - } - catch (const Firebird::Exception& ex) - { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - - switch (id) - { - case drq_s_prms: - case drq_s_prm_src: - case drq_s_prms2: - case drq_s_prms3: - DYN_rundown_request(request, id); - // fall down - case -1: - DYN_error_punt(true, 136); - // msg 136: "STORE RDB$PROCEDURE_PARAMETERS failed" - } - - DYN_rundown_request(request, -1); - - /* Control should never reach this point, - because id should always have one of the values tested above. */ - fb_assert(0); - DYN_error_punt(true, 0); - } -} - - -void DYN_define_procedure( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ d e f i n e _ p r o c e d u r e - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement. - * - **************************************/ - Firebird::MetaName procedure_name; - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - GET_STRING(ptr, procedure_name); - - if (procedure_name.length() == 0) - DYN_error_punt(false, 212); - /* msg 212: "Zero length identifiers not allowed" */ - - jrd_req* request = NULL; - SSHORT id = -1; - - try { - - id = drq_l_prc_name; - check_unique_name(tdbb, gbl, procedure_name, obj_procedure); - - bool sql_prot = false; - prc_t prc_type = prc_legacy; - SSHORT sys_flag = 0, inputs = -1, outputs = -1; - const UCHAR* blr_ptr = NULL; - const UCHAR* description_ptr = NULL; - const UCHAR* source_ptr = NULL; - const UCHAR* debug_info_ptr = NULL; - Firebird::MetaName security_class; - - UCHAR verb; - while ((verb = *(*ptr)++) != isc_dyn_end) - { - switch (verb) - { - case isc_dyn_system_flag: - sys_flag = (SSHORT) DYN_get_number(ptr); - break; - - case isc_dyn_prc_blr: - blr_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - case isc_dyn_description: - description_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - case isc_dyn_prc_source: - source_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - case isc_dyn_prc_inputs: - inputs = (SSHORT) DYN_get_number(ptr); - break; - - case isc_dyn_prc_outputs: - outputs = (SSHORT) DYN_get_number(ptr); - break; - - case isc_dyn_prc_type: - prc_type = (prc_t) DYN_get_number(ptr); - break; - - case isc_dyn_security_class: - GET_STRING(ptr, security_class); - break; - - case isc_dyn_rel_sql_protection: - sql_prot = (bool) DYN_get_number(ptr); - break; - - case isc_dyn_debug_info: - debug_info_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - default: - --(*ptr); - DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, &procedure_name); - } - } - - request = CMP_find_request(tdbb, drq_s_prcs, DYN_REQUESTS); - id = drq_s_prcs; - - int faults = 0; - const UCHAR* temp_ptr; - - while (true) - { - try - { - SINT64 prc_id = - DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_prc_id, "RDB$PROCEDURES"); - - prc_id %= (MAX_SSHORT + 1); - - if (!prc_id) - continue; - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURES - - P.RDB$PROCEDURE_ID = prc_id; - strcpy(P.RDB$PROCEDURE_NAME, procedure_name.c_str()); - - P.RDB$PROCEDURE_BLR.NULL = TRUE; - P.RDB$PROCEDURE_SOURCE.NULL = TRUE; - P.RDB$SECURITY_CLASS.NULL = TRUE; - P.RDB$DESCRIPTION.NULL = TRUE; - P.RDB$PROCEDURE_INPUTS.NULL = TRUE; - P.RDB$PROCEDURE_OUTPUTS.NULL = TRUE; - - P.RDB$SYSTEM_FLAG.NULL = FALSE; - P.RDB$SYSTEM_FLAG = sys_flag; - - if (blr_ptr) { - P.RDB$PROCEDURE_BLR.NULL = FALSE; - temp_ptr = blr_ptr; - DYN_put_blr_blob(gbl, &temp_ptr, &P.RDB$PROCEDURE_BLR); - } - - if (source_ptr) { - P.RDB$PROCEDURE_SOURCE.NULL = FALSE; - temp_ptr = source_ptr; - DYN_put_text_blob(gbl, &temp_ptr, &P.RDB$PROCEDURE_SOURCE); - } - - if (description_ptr) { - P.RDB$DESCRIPTION.NULL = FALSE; - temp_ptr = description_ptr; - DYN_put_text_blob(gbl, &temp_ptr, &P.RDB$DESCRIPTION); - } - - if (inputs >= 0) { - P.RDB$PROCEDURE_INPUTS.NULL = FALSE; - P.RDB$PROCEDURE_INPUTS = inputs; - } - - if (outputs >= 0) { - P.RDB$PROCEDURE_OUTPUTS.NULL = FALSE; - P.RDB$PROCEDURE_OUTPUTS = outputs; - } - - if (security_class.length()) { - P.RDB$SECURITY_CLASS.NULL = FALSE; - GET_STRING(security_class.c_str(), P.RDB$SECURITY_CLASS); - } - - END_STORE; - - break; - } - catch (const Firebird::status_exception& ex) - { - DYN_rundown_request(request, id); - - if (ex.value()[1] != isc_no_dup) - throw; - - if (++faults > MAX_SSHORT) - throw; - - fb_utils::init_status(tdbb->tdbb_status_vector); - } - } - - if (!DYN_REQUEST(drq_s_prcs)) - DYN_REQUEST(drq_s_prcs) = request; - - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) - { - jrd_req* sub_request = NULL; - - FOR(REQUEST_HANDLE sub_request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ procedure_name.c_str() - - MODIFY P USING - P.RDB$PROCEDURE_TYPE = prc_type; - P.RDB$PROCEDURE_TYPE.NULL = FALSE; - - P.RDB$VALID_BLR = TRUE; - P.RDB$VALID_BLR.NULL = FALSE; - - P.RDB$DEBUG_INFO.NULL = (debug_info_ptr == NULL) ? TRUE : FALSE; - if (debug_info_ptr) - DYN_put_blr_blob(gbl, &debug_info_ptr, &P.RDB$DEBUG_INFO); - END_MODIFY; - END_FOR; - - CMP_release(tdbb, sub_request); - } - - if (sql_prot) { - Firebird::MetaName owner_name; - if (!get_who(tdbb, gbl, owner_name)) - DYN_error_punt(true, 134); - /* msg 134: "STORE RDB$PROCEDURES failed" */ - - for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++) { - request = CMP_find_request(tdbb, drq_s_prc_usr_prvs, DYN_REQUESTS); - id = drq_s_prc_usr_prvs; - - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$USER_PRIVILEGES - strcpy(X.RDB$RELATION_NAME, procedure_name.c_str()); - strcpy(X.RDB$USER, owner_name.c_str()); - X.RDB$USER_TYPE = obj_user; - X.RDB$OBJECT_TYPE = obj_procedure; - X.RDB$PRIVILEGE[0] = *p; - X.RDB$PRIVILEGE[1] = 0; - END_STORE; - - if (!DYN_REQUEST(drq_s_prc_usr_prvs)) - DYN_REQUEST(drq_s_prc_usr_prvs) = request; - } - } - - } - catch (const Firebird::Exception& ex) - { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - if (id == drq_s_prcs) { - DYN_rundown_request(request, id); - DYN_error_punt(true, 134); - /* msg 134: "STORE RDB$PROCEDURES failed" */ - } - else if (id == drq_s_prc_usr_prvs) { - DYN_rundown_request(request, id); - DYN_error_punt(true, 25); - /* msg 25: "STORE RDB$USER_PRIVILEGES failed defining a relation" */ - } - - DYN_rundown_request(request, -1); - - if (id == drq_l_prc_name) { - DYN_error_punt(true, 134); - /* msg 134: "STORE RDB$PROCEDURES failed" */ - } - - /* Control should never reach this point, because id should have - one of the values tested-for above. */ - fb_assert(false); - DYN_error_punt(true, 0); - } -} - - -void DYN_define_relation( Global* gbl, const UCHAR** ptr) +void DYN_define_relation(Global* gbl, const UCHAR** ptr, bool view) { /************************************** * @@ -3515,6 +2908,9 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr) /* msg 212: "Zero length identifiers not allowed" */ } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + (view ? DDL_TRIGGER_CREATE_VIEW : DDL_TRIGGER_CREATE_TABLE), relation_name, gbl->sqlText); + jrd_req* request = NULL; SSHORT id = -1; @@ -3523,7 +2919,7 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr) try { id = drq_l_rel_name; - check_unique_name(tdbb, gbl, relation_name, obj_relation); + DYN_UTIL_check_unique_name(tdbb, gbl->gbl_transaction, relation_name, obj_relation); bool sql_prot = false; rel_t rel_type = rel_persistent; @@ -3709,6 +3105,7 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr) } if (sql_prot) + { for (const TEXT* p = ALL_PRIVILEGES; *p; p++) { request = CMP_find_request(tdbb, drq_s_usr_prvs, DYN_REQUESTS); id = drq_s_usr_prvs; @@ -3727,6 +3124,7 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_s_usr_prvs)) DYN_REQUEST(drq_s_usr_prvs) = request; } + } } catch (const Firebird::Exception& ex) @@ -3763,6 +3161,9 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr) fb_assert(false); DYN_error_punt(true, 0); } + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + (view ? DDL_TRIGGER_CREATE_VIEW : DDL_TRIGGER_CREATE_TABLE), relation_name, gbl->sqlText); } @@ -3799,6 +3200,9 @@ void DYN_define_role( Global* gbl, const UCHAR** ptr) Firebird::MetaName role_name; GET_STRING(ptr, role_name); + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_CREATE_ROLE, role_name, gbl->sqlText); + if (role_name == owner_name) { /************************************************ ** @@ -3880,6 +3284,9 @@ void DYN_define_role( Global* gbl, const UCHAR** ptr) /* msg 8: "DEFINE ROLE failed" */ } + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_CREATE_ROLE, role_name, gbl->sqlText); + return; do_err_punt: @@ -4330,6 +3737,7 @@ void DYN_define_shadow( Global* gbl, const UCHAR** ptr) } +//// TODO: remove this function after rework on constraints and views triggers void DYN_define_trigger(Global* gbl, const UCHAR** ptr, const Firebird::MetaName* relation_name, @@ -4391,6 +3799,9 @@ void DYN_define_trigger(Global* gbl, X.RDB$TRIGGER_INACTIVE.NULL = FALSE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; + // ODS_12_0 + X.RDB$ENTRYPOINT.NULL = TRUE; + X.RDB$ENGINE_NAME.NULL = TRUE; /* currently, we make no difference between ignoring permissions in order to define this trigger and ignoring permissions checks when the @@ -4477,6 +3888,18 @@ void DYN_define_trigger(Global* gbl, DYN_skip_attribute(ptr); break; + case isc_dyn_func_entry_point: + // ODS_12_0 + GET_STRING(ptr, X.RDB$ENTRYPOINT); + X.RDB$ENTRYPOINT.NULL = FALSE; + break; + + case isc_dyn_def_engine: + // ODS_12_0 + GET_STRING(ptr, X.RDB$ENGINE_NAME); + X.RDB$ENGINE_NAME.NULL = FALSE; + break; + default: --(*ptr); MetaTmp(X.RDB$RELATION_NAME) @@ -4727,131 +4150,6 @@ void DYN_define_view_relation( Global* gbl, const UCHAR** ptr, const Firebird::M } -static void check_unique_name(thread_db* tdbb, - Global* gbl, - const Firebird::MetaName& object_name, - int object_type) -{ -/************************************** - * - * c h e c k _ u n i q u e _ n a m e - * - ************************************** - * - * Functional description - * Check if an object already exists. - * If yes then return error. - * - **************************************/ - - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - - USHORT error_code = 0; - jrd_req* request = NULL; - - try - { - switch (object_type) - { - case obj_relation: - case obj_procedure: - request = CMP_find_request(tdbb, drq_l_rel_name, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - EREL IN RDB$RELATIONS WITH EREL.RDB$RELATION_NAME EQ object_name.c_str() - - if (!DYN_REQUEST(drq_l_rel_name)) - DYN_REQUEST(drq_l_rel_name) = request; - - error_code = 132; - END_FOR; - - if (!DYN_REQUEST(drq_l_rel_name)) - DYN_REQUEST(drq_l_rel_name) = request; - - if (!error_code) - { - request = CMP_find_request(tdbb, drq_l_prc_name, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - EPRC IN RDB$PROCEDURES WITH EPRC.RDB$PROCEDURE_NAME EQ object_name.c_str() - - if (!DYN_REQUEST(drq_l_prc_name)) - DYN_REQUEST(drq_l_prc_name) = request; - - error_code = 135; - END_FOR; - - if (!DYN_REQUEST(drq_l_prc_name)) - DYN_REQUEST(drq_l_prc_name) = request; - } - break; - - case obj_index: - request = CMP_find_request(tdbb, drq_l_idx_name, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - EIDX IN RDB$INDICES WITH EIDX.RDB$INDEX_NAME EQ object_name.c_str() - - if (!DYN_REQUEST(drq_l_idx_name)) - DYN_REQUEST(drq_l_idx_name) = request; - - error_code = 251; - END_FOR; - - if (!DYN_REQUEST(drq_l_idx_name)) - DYN_REQUEST(drq_l_idx_name) = request; - break; - - case obj_exception: - request = CMP_find_request(tdbb, drq_l_xcp_name, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - EXCP IN RDB$EXCEPTIONS WITH EXCP.RDB$EXCEPTION_NAME EQ object_name.c_str() - - if (!DYN_REQUEST(drq_l_xcp_name)) - DYN_REQUEST(drq_l_xcp_name) = request; - - error_code = 253; - END_FOR; - - if (!DYN_REQUEST(drq_l_xcp_name)) - DYN_REQUEST(drq_l_xcp_name) = request; - break; - - case obj_generator: - request = CMP_find_request(tdbb, drq_l_gen_name, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - EGEN IN RDB$GENERATORS WITH EGEN.RDB$GENERATOR_NAME EQ object_name.c_str() - - if (!DYN_REQUEST(drq_l_gen_name)) - DYN_REQUEST(drq_l_gen_name) = request; - - error_code = 254; - END_FOR; - - if (!DYN_REQUEST(drq_l_gen_name)) - DYN_REQUEST(drq_l_gen_name) = request; - break; - - default: - fb_assert(false); - } - } - catch (const Firebird::Exception&) - { - DYN_rundown_request(request, -1); - throw; - } - - if (error_code) { - DYN_error_punt(false, error_code, object_name.c_str()); - } -} - - static bool get_who( thread_db* tdbb, Global* gbl, Firebird::MetaName& output_name) { /************************************** diff --git a/src/jrd/dyn_del.epp b/src/jrd/dyn_del.epp index dcbae06db7..13ce0ae149 100644 --- a/src/jrd/dyn_del.epp +++ b/src/jrd/dyn_del.epp @@ -31,6 +31,7 @@ #include #include "../jrd/common.h" +#include "../dsql/DdlNodes.h" #include "../jrd/jrd.h" #include "../jrd/ods.h" #include "../jrd/tra.h" @@ -73,7 +74,6 @@ static void delete_f_key_constraint(thread_db*, Global*, const Firebird::MetaName&, const Firebird::MetaName&); static void delete_gfield_for_lfield(Global*, const Firebird::MetaName&); static bool delete_index_segment_records(Global*, const Firebird::MetaName&); -static bool delete_security_class2(Global*, const Firebird::MetaName&); void DYN_delete_collation(Global* gbl, const UCHAR** ptr) @@ -112,6 +112,9 @@ void DYN_delete_collation(Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_e_colls)) DYN_REQUEST(drq_e_colls) = request; + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_COLLATION, collName, gbl->sqlText); + if (!COLL.RDB$SYSTEM_FLAG.NULL && COLL.RDB$SYSTEM_FLAG == 1) { DYN_rundown_request(request, -1); @@ -169,12 +172,14 @@ void DYN_delete_collation(Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_l_prm_coll)) DYN_REQUEST(drq_l_prm_coll) = request2; - fb_utils::exact_name_limit(PRM.RDB$PROCEDURE_NAME, sizeof(PRM.RDB$PROCEDURE_NAME)); fb_utils::exact_name_limit(PRM.RDB$PARAMETER_NAME, sizeof(PRM.RDB$PARAMETER_NAME)); DYN_rundown_request(request2, -1); - DYN_error_punt(false, 243, SafeArg() << COLL.RDB$COLLATION_NAME << - PRM.RDB$PROCEDURE_NAME << PRM.RDB$PARAMETER_NAME); + DYN_error_punt(false, 243, SafeArg() << + COLL.RDB$COLLATION_NAME << + QualifiedName(PRM.RDB$PROCEDURE_NAME, + (PRM.RDB$PACKAGE_NAME.NULL ? NULL : PRM.RDB$PACKAGE_NAME)).toString().c_str() << + PRM.RDB$PARAMETER_NAME); // msg 243: "Collation %s is used in procedure %s (parameter name %s) and cannot be dropped" END_FOR; @@ -215,7 +220,12 @@ void DYN_delete_collation(Global* gbl, const UCHAR** ptr) // msg 234: "ERASE RDB$COLLATIONS failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_COLLATION, collName, gbl->sqlText); + } + else { DYN_error_punt(false, 152, collName.c_str()); // msg 152: "Collation %s not found" @@ -324,12 +334,16 @@ void DYN_delete_exception( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_e_xcp) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_EXCEPTION, t, gbl->sqlText); + ERASE X; END_FOR; if (!DYN_REQUEST(drq_e_xcp)) DYN_REQUEST(drq_e_xcp) = request; - } + } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -337,7 +351,13 @@ void DYN_delete_exception( Global* gbl, const UCHAR** ptr) /* msg 143: "ERASE EXCEPTION failed" */ } - if (!found) { + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_EXCEPTION, t, gbl->sqlText); + } + else + { DYN_error_punt(false, 144); /* msg 144: "Exception not found" */ } @@ -403,82 +423,6 @@ void DYN_delete_filter( Global* gbl, const UCHAR** ptr) } -void DYN_delete_function( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ d e l e t e _ f u n c t i o n - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement that - * deletes a user defined function. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = CMP_find_request(tdbb, drq_e_func_args, DYN_REQUESTS); - USHORT id = drq_e_func_args; - - bool found = false; - Firebird::MetaName f; - GET_STRING(ptr, f); - - try { - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - FA IN RDB$FUNCTION_ARGUMENTS WITH FA.RDB$FUNCTION_NAME EQ f.c_str() - if (!DYN_REQUEST(drq_e_func_args)) - DYN_REQUEST(drq_e_func_args) = request; - - ERASE FA; - END_FOR; - if (!DYN_REQUEST(drq_e_func_args)) - DYN_REQUEST(drq_e_func_args) = request; - - request = CMP_find_request(tdbb, drq_e_funcs, DYN_REQUESTS); - id = drq_e_funcs; - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_NAME EQ f.c_str() - if (!DYN_REQUEST(drq_e_funcs)) - DYN_REQUEST(drq_e_funcs) = request; - - ERASE X; - found = true; - END_FOR; - if (!DYN_REQUEST(drq_e_funcs)) - DYN_REQUEST(drq_e_funcs) = request; - } - catch (const Firebird::Exception& ex) { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - if (id == drq_e_func_args) - { - DYN_error_punt(true, 39); - /* msg 39: "ERASE RDB$FUNCTION_ARGUMENTS failed" */ - } - else - { - DYN_error_punt(true, 40); - /* msg 40: "ERASE RDB$FUNCTIONS failed" */ - } - } - - if (!found) - { - DYN_error_punt(false, 41, f.c_str()); - /* msg 41: "Function %s not found" */ - } - - if (*(*ptr)++ != isc_dyn_end) { - DYN_unsupported_verb(); - } -} - - void DYN_delete_generator(Global* gbl, const UCHAR**ptr) { /************************************** @@ -514,12 +458,15 @@ void DYN_delete_generator(Global* gbl, const UCHAR**ptr) DYN_REQUEST(drq_e_gens) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_SEQUENCE, t, gbl->sqlText); + ERASE X; END_FOR; if (!DYN_REQUEST(drq_e_gens)) DYN_REQUEST(drq_e_gens) = request; - } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); @@ -528,7 +475,13 @@ void DYN_delete_generator(Global* gbl, const UCHAR**ptr) /* msg 213: "ERASE GENERATOR failed" */ } - if (!found) { + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_SEQUENCE, t, gbl->sqlText); + } + else + { DYN_error_punt(false, 214, t.c_str()); /* msg 214: "Generator %s not found" */ } @@ -553,13 +506,30 @@ void DYN_delete_global_field( Global* gbl, const UCHAR** ptr) thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); - jrd_req* request = CMP_find_request(tdbb, drq_l_fld_src, DYN_REQUESTS); + jrd_req* request = CMP_find_request(tdbb, drq_e_gfields, DYN_REQUESTS); bool found = false; try { GET_STRING(ptr, f); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) + X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ f.c_str() + if (!DYN_REQUEST(drq_e_gfields)) + DYN_REQUEST(drq_e_gfields) = request; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_DOMAIN, f, gbl->sqlText); + + delete_dimension_records(gbl, f); + ERASE X; + found = true; + END_FOR + if (!DYN_REQUEST(drq_e_gfields)) + DYN_REQUEST(drq_e_gfields) = request; + + request = CMP_find_request(tdbb, drq_l_fld_src, DYN_REQUESTS); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) Y IN RDB$RELATION_FIELDS WITH Y.RDB$FIELD_SOURCE EQ f.c_str() if (!DYN_REQUEST(drq_l_fld_src)) @@ -578,7 +548,6 @@ void DYN_delete_global_field( Global* gbl, const UCHAR** ptr) request = CMP_find_request(tdbb, drq_l_prp_src, DYN_REQUESTS); - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$FIELD_SOURCE EQ f.c_str() @@ -590,29 +559,17 @@ void DYN_delete_global_field( Global* gbl, const UCHAR** ptr) fb_utils::exact_name_limit(X.RDB$PARAMETER_NAME, sizeof(X.RDB$PARAMETER_NAME)); DYN_rundown_request(request, -1); - DYN_error_punt(false, 239, SafeArg() << X.RDB$FIELD_SOURCE << X.RDB$PROCEDURE_NAME << - X.RDB$PARAMETER_NAME); + DYN_error_punt(false, 239, SafeArg() << X.RDB$FIELD_SOURCE << + QualifiedName(X.RDB$PROCEDURE_NAME, + (X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)).toString().c_str() << + X.RDB$PARAMETER_NAME); // msg 239: "Domain %s is used in procedure %s (parameter name %s) and cannot be dropped" END_FOR if (!DYN_REQUEST(drq_l_prp_src)) DYN_REQUEST(drq_l_prp_src) = request; - request = CMP_find_request(tdbb, drq_e_gfields, DYN_REQUESTS); - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ f.c_str() - if (!DYN_REQUEST(drq_e_gfields)) - DYN_REQUEST(drq_e_gfields) = request; - - delete_dimension_records(gbl, f); - ERASE X; - found = true; - END_FOR - if (!DYN_REQUEST(drq_e_gfields)) - DYN_REQUEST(drq_e_gfields) = request; - } + } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -620,7 +577,13 @@ void DYN_delete_global_field( Global* gbl, const UCHAR** ptr) /* msg 44: "ERASE RDB$FIELDS failed" */ } - if (!found) { + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_DOMAIN, f, gbl->sqlText); + } + else + { DYN_error_punt(false, 89); // msg 89: "Domain not found" } @@ -666,12 +629,17 @@ void DYN_delete_index( Global* gbl, const UCHAR** ptr) rel_name = IDX.RDB$RELATION_NAME; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_INDEX, idx_name, gbl->sqlText); + is_expression = !IDX.RDB$EXPRESSION_BLR.NULL; ERASE IDX; END_FOR; if (!DYN_REQUEST(drq_e_indices)) DYN_REQUEST(drq_e_indices) = request; - } + + } //try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -679,7 +647,12 @@ void DYN_delete_index( Global* gbl, const UCHAR** ptr) /* msg 47: "ERASE RDB$INDICES failed" */ } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_INDEX, idx_name, gbl->sqlText); + } + else { DYN_error_punt(false, 48); /* msg 48: "Index not found" */ @@ -886,7 +859,7 @@ void DYN_delete_local_field(Global* gbl, if (!RFR.RDB$SECURITY_CLASS.NULL && !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) { - delete_security_class2(gbl, RFR.RDB$SECURITY_CLASS); + DYN_delete_security_class2(gbl->gbl_transaction, RFR.RDB$SECURITY_CLASS); } found = true; @@ -948,318 +921,6 @@ void DYN_delete_local_field(Global* gbl, } -void DYN_delete_parameter(Global* gbl, - const UCHAR** ptr, - Firebird::MetaName* proc_name) -{ -/************************************** - * - * D Y N _ d e l e t e _ p a r a m e t e r - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement that - * deletes a stored procedure parameter. - * - **************************************/ - Firebird::MetaName name; - - GET_STRING(ptr, name); - if (**ptr == isc_dyn_prc_name) { - GET_STRING(ptr, *proc_name); - } - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = CMP_find_request(tdbb, drq_e_prm, DYN_REQUESTS); - USHORT id = drq_e_prms; - - bool found = false; - - try { - - jrd_req* old_request = NULL; - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PP IN RDB$PROCEDURE_PARAMETERS WITH PP.RDB$PROCEDURE_NAME EQ proc_name->c_str() - AND PP.RDB$PARAMETER_NAME EQ name.c_str() - if (!DYN_REQUEST(drq_e_prm)) - DYN_REQUEST(drq_e_prm) = request; - found = true; - - /* get rid of parameters in rdb$fields */ - - if (!PP.RDB$FIELD_SOURCE.NULL) { - old_request = request; - const USHORT old_id = id; - request = CMP_find_request(tdbb, drq_d_gfields, DYN_REQUESTS); - id = drq_d_gfields; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - FLD IN RDB$FIELDS - WITH FLD.RDB$FIELD_NAME EQ PP.RDB$FIELD_SOURCE AND - FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX - if (!DYN_REQUEST(drq_d_gfields)) - DYN_REQUEST(drq_d_gfields) = request; - - bool erase = true; - - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2) - { - jrd_req* request2 = CMP_find_request(tdbb, drq_d_gfields2, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction) - PP2 IN RDB$PROCEDURE_PARAMETERS - WITH PP2.RDB$PROCEDURE_NAME = PP.RDB$PROCEDURE_NAME AND - PP2.RDB$PARAMETER_NAME = PP.RDB$PARAMETER_NAME - - if (!DYN_REQUEST(drq_d_gfields2)) - DYN_REQUEST(drq_d_gfields2) = request2; - - if (!PP2.RDB$RELATION_NAME.NULL && !PP2.RDB$FIELD_NAME.NULL) - erase = false; - END_FOR; - - if (!DYN_REQUEST(drq_d_gfields2)) - DYN_REQUEST(drq_d_gfields2) = request2; - } - - if (erase) - ERASE FLD; - END_FOR; - - if (!DYN_REQUEST(drq_d_gfields)) - DYN_REQUEST(drq_d_gfields) = request; - - request = old_request; - id = old_id; - } - ERASE PP; - - END_FOR; - if (!DYN_REQUEST(drq_e_prm)) { - DYN_REQUEST(drq_e_prm) = request; - } - } // try - catch (const Firebird::Exception& ex) - { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - if (id == drq_e_prms) { - DYN_error_punt(true, 138); - /* msg 138: "ERASE RDB$PROCEDURE_PARAMETERS failed" */ - } - else { - DYN_error_punt(true, 35); - /* msg 35: "ERASE RDB$FIELDS failed" */ - } - } - - if (!found) { - DYN_error_punt(false, 146, SafeArg() << name.c_str() << proc_name->c_str()); - /* msg 146: "Parameter %s in procedure %s not found" */ - } - - if (*(*ptr)++ != isc_dyn_end) { - DYN_unsupported_verb(); - } -} - - -void DYN_delete_procedure( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ d e l e t e _ p r o c e d u r e - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement that - * deletes a stored procedure. - * - **************************************/ - Firebird::MetaName name; - - GET_STRING(ptr, name); - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - tdbb->tdbb_flags |= TDBB_prc_being_dropped; - if (MET_lookup_procedure(tdbb, name, true) == 0) - { - tdbb->tdbb_flags &= ~TDBB_prc_being_dropped; - DYN_error_punt(false, 140, name.c_str()); - /* msg 140: "Procedure %s not found" */ - } - - tdbb->tdbb_flags &= ~TDBB_prc_being_dropped; - - jrd_req* request = CMP_find_request(tdbb, drq_e_prms, DYN_REQUESTS); - USHORT id = drq_e_prms; - - try { - - jrd_req* old_request = NULL; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PP IN RDB$PROCEDURE_PARAMETERS WITH PP.RDB$PROCEDURE_NAME EQ name.c_str() - - if (!DYN_REQUEST(drq_e_prms)) - DYN_REQUEST(drq_e_prms) = request; - - /* get rid of parameters in rdb$fields */ - - if (!PP.RDB$FIELD_SOURCE.NULL) { - old_request = request; - const USHORT old_id = id; - request = CMP_find_request(tdbb, drq_d_gfields, DYN_REQUESTS); - id = drq_d_gfields; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - FLD IN RDB$FIELDS - WITH FLD.RDB$FIELD_NAME EQ PP.RDB$FIELD_SOURCE AND - FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX - - if (!DYN_REQUEST(drq_d_gfields)) - DYN_REQUEST(drq_d_gfields) = request; - - bool erase = true; - - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2) - { - jrd_req* request2 = NULL; - - FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction) - PP2 IN RDB$PROCEDURE_PARAMETERS - WITH PP2.RDB$PROCEDURE_NAME = PP.RDB$PROCEDURE_NAME AND - PP2.RDB$PARAMETER_NAME = PP.RDB$PARAMETER_NAME - - if (!PP2.RDB$RELATION_NAME.NULL && !PP2.RDB$FIELD_NAME.NULL) - erase = false; - END_FOR; - - CMP_release(tdbb, request2); - } - - if (erase) - ERASE FLD; - END_FOR; - - if (!DYN_REQUEST(drq_d_gfields)) - DYN_REQUEST(drq_d_gfields) = request; - - request = old_request; - id = old_id; - } - - ERASE PP; - END_FOR; - if (!DYN_REQUEST(drq_e_prms)) { - DYN_REQUEST(drq_e_prms) = request; - } - - request = CMP_find_request(tdbb, drq_e_prcs, DYN_REQUESTS); - id = drq_e_prcs; - - bool found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ name.c_str() - - if (!DYN_REQUEST(drq_e_prcs)) { - DYN_REQUEST(drq_e_prcs) = request; - } - - ERASE P; - - if (!P.RDB$SECURITY_CLASS.NULL) { - delete_security_class2(gbl, P.RDB$SECURITY_CLASS); - } - - found = true; - END_FOR; - - if (!DYN_REQUEST(drq_e_prcs)) { - DYN_REQUEST(drq_e_prcs) = request; - } - - if (!found) { - goto dyn_punt_140; - } - - request = CMP_find_request(tdbb, drq_e_prc_prvs, DYN_REQUESTS); - id = drq_e_prc_prvs; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$RELATION_NAME EQ name.c_str() - AND PRIV.RDB$OBJECT_TYPE = obj_procedure - - if (!DYN_REQUEST(drq_e_prc_prvs)) - DYN_REQUEST(drq_e_prc_prvs) = request; - - ERASE PRIV; - END_FOR; - - if (!DYN_REQUEST(drq_e_prc_prvs)) - DYN_REQUEST(drq_e_prc_prvs) = request; - - request = CMP_find_request(tdbb, drq_e_prc_prv, DYN_REQUESTS); - id = drq_e_prc_prv; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$USER EQ name.c_str() - AND PRIV.RDB$USER_TYPE = obj_procedure - - if (!DYN_REQUEST(drq_e_prc_prv)) - DYN_REQUEST(drq_e_prc_prv) = request; - - ERASE PRIV; - END_FOR; - - if (!DYN_REQUEST(drq_e_prc_prv)) - DYN_REQUEST(drq_e_prc_prv) = request; - - } // try - catch (const Firebird::Exception& ex) - { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - - switch (id) - { - case drq_e_prms: - DYN_error_punt(true, 138); - /* msg 138: "ERASE RDB$PROCEDURE_PARAMETERS failed" */ - break; - case drq_e_prcs: - DYN_error_punt(true, 139); - /* msg 139: "ERASE RDB$PROCEDURES failed" */ - break; - case drq_d_gfields: - DYN_error_punt(true, 35); - /* msg 35: "ERASE RDB$FIELDS failed" */ - break; - default: - DYN_error_punt(true, 62); - /* msg 62: "ERASE RDB$USER_PRIVILEGES failed" */ - break; - } - } - - if (*(*ptr)++ != isc_dyn_end) { - DYN_unsupported_verb(); - } - - return; - -dyn_punt_140: - DYN_error_punt(false, 140, name.c_str()); - /* msg 140: "Procedure %s not found" */ -} - - void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaName* relation) { /************************************** @@ -1285,11 +946,41 @@ void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaNa GET_STRING(ptr, relation_name); jrd_req* req2 = 0; - jrd_req* request = CMP_find_request(tdbb, drq_e_rel_con2, DYN_REQUESTS); - USHORT id = drq_e_rel_con2; + jrd_req* request = CMP_find_request(tdbb, drq_e_relation, DYN_REQUESTS); + USHORT id = drq_e_relation; + + bool found = false; + bool view = false; try { + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) + R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME EQ relation_name.c_str() + if (!DYN_REQUEST(drq_e_relation)) + DYN_REQUEST(drq_e_relation) = request; + + view = !R.RDB$VIEW_BLR.NULL; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE), relation_name, gbl->sqlText); + + ERASE R; + + if (!R.RDB$SECURITY_CLASS.NULL && + !strncmp(R.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + { + DYN_delete_security_class2(gbl->gbl_transaction, R.RDB$SECURITY_CLASS); + } + + found = true; + END_FOR; + + if (!DYN_REQUEST(drq_e_relation)) + DYN_REQUEST(drq_e_relation) = request; + + request = CMP_find_request(tdbb, drq_e_rel_con2, DYN_REQUESTS); + id = drq_e_rel_con2; + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) CRT IN RDB$RELATION_CONSTRAINTS WITH CRT.RDB$RELATION_NAME EQ relation_name.c_str() AND @@ -1373,7 +1064,7 @@ void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaNa if (!RFR.RDB$SECURITY_CLASS.NULL && !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) { - delete_security_class2(gbl, RFR.RDB$SECURITY_CLASS); + DYN_delete_security_class2(gbl->gbl_transaction, RFR.RDB$SECURITY_CLASS); } delete_gfield_for_lfield(gbl, RFR.RDB$FIELD_SOURCE); @@ -1400,26 +1091,6 @@ void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaNa request = CMP_find_request(tdbb, drq_e_relation, DYN_REQUESTS); id = drq_e_relation; - bool found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME EQ relation_name.c_str() - if (!DYN_REQUEST(drq_e_relation)) - DYN_REQUEST(drq_e_relation) = request; - - ERASE R; - - if (!R.RDB$SECURITY_CLASS.NULL && - !strncmp(R.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) - { - delete_security_class2(gbl, R.RDB$SECURITY_CLASS); - } - - found = true; - END_FOR; - - if (!DYN_REQUEST(drq_e_relation)) - DYN_REQUEST(drq_e_relation) = request; - if (!found) { goto dyn_punt_61; } @@ -1498,6 +1169,7 @@ void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaNa if (!DYN_REQUEST(drq_e_view_prv)) { DYN_REQUEST(drq_e_view_prv) = request; } + } // try catch (const Firebird::Exception& ex) { @@ -1550,6 +1222,12 @@ void DYN_delete_relation( Global* gbl, const UCHAR** ptr, const Firebird::MetaNa DYN_error_punt(true, number); } + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + (view ? DDL_TRIGGER_DROP_VIEW : DDL_TRIGGER_DROP_TABLE), relation_name, gbl->sqlText); + } + while (*(*ptr)++ != isc_dyn_end) { --(*ptr); @@ -1616,6 +1294,10 @@ void DYN_delete_role( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_drop_role) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_DROP_ROLE, role_name, gbl->sqlText); + const Firebird::MetaName role_owner(XX.RDB$OWNER_NAME); if (tdbb->getAttachment()->locksmith() || role_owner == user) @@ -1667,7 +1349,6 @@ void DYN_delete_role( Global* gbl, const UCHAR** ptr) // only owner of SQL role or USR_locksmith could drop SQL role goto do_punt; } - } // try catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); @@ -1678,7 +1359,12 @@ void DYN_delete_role( Global* gbl, const UCHAR** ptr) DYN_error_punt(true, number); } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_DROP_ROLE, role_name, gbl->sqlText); + } + else { DYN_error_punt(false, 155, role_name.c_str()); // msg 155: "Role %s not found" @@ -1708,7 +1394,7 @@ void DYN_delete_security_class( Global* gbl, const UCHAR** ptr) GET_STRING(ptr, security_class); - if (!delete_security_class2(gbl, security_class)) + if (!DYN_delete_security_class2(gbl->gbl_transaction, security_class)) { DYN_error_punt(false, 75); /* msg 75: "Security class not found" */ @@ -1772,240 +1458,6 @@ void DYN_delete_shadow( Global* gbl, const UCHAR** ptr) } -void DYN_delete_trigger( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ d e l e t e _ t r i g g e r - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement that - * deletes a trigger. - * - **************************************/ - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = CMP_find_request(tdbb, drq_e_trg_msgs, DYN_REQUESTS); - USHORT id = drq_e_trg_msgs; - - Firebird::MetaName t; - GET_STRING(ptr, t); - - try { - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - TM IN RDB$TRIGGER_MESSAGES - CROSS TR IN RDB$TRIGGERS - WITH TM.RDB$TRIGGER_NAME EQ t.c_str() AND - TR.RDB$TRIGGER_NAME EQ TM.RDB$TRIGGER_NAME - - if (!DYN_REQUEST(drq_e_trg_msgs)) - DYN_REQUEST(drq_e_trg_msgs) = request; - - if (TR.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith()) - ERR_post(Arg::Gds(isc_adm_task_denied)); - - ERASE TM; - END_FOR; - - if (!DYN_REQUEST(drq_e_trg_msgs)) - DYN_REQUEST(drq_e_trg_msgs) = request; - - request = CMP_find_request(tdbb, drq_e_trigger, DYN_REQUESTS); - id = drq_e_trigger; - - Firebird::MetaName r; - - bool found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ t.c_str() - - if (!DYN_REQUEST(drq_e_trigger)) - DYN_REQUEST(drq_e_trigger) = request; - - if (X.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith()) - ERR_post(Arg::Gds(isc_adm_task_denied)); - - r = X.RDB$RELATION_NAME; - ERASE X; - found = true; - END_FOR; - - if (!DYN_REQUEST(drq_e_trigger)) - DYN_REQUEST(drq_e_trigger) = request; - - if (!found) { - goto dyn_punt_147; - } - - request = CMP_find_request(tdbb, drq_e_trg_prv, DYN_REQUESTS); - id = drq_e_trg_prv; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$USER EQ t.c_str() - AND PRIV.RDB$USER_TYPE = obj_trigger - - if (!DYN_REQUEST(drq_e_trg_prv)) - DYN_REQUEST(drq_e_trg_prv) = request; - - ERASE PRIV; - END_FOR; - - if (!DYN_REQUEST(drq_e_trg_prv)) - DYN_REQUEST(drq_e_trg_prv) = request; - -/* clear the update flags on the fields if this is the last remaining - trigger that changes a view */ - - request = CMP_find_request(tdbb, drq_l_view_rel2, DYN_REQUESTS); - id = drq_l_view_rel2; - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - FIRST 1 V IN RDB$VIEW_RELATIONS - CROSS F IN RDB$RELATION_FIELDS CROSS T IN RDB$TRIGGERS - WITH V.RDB$VIEW_NAME EQ r.c_str() AND - F.RDB$RELATION_NAME EQ V.RDB$VIEW_NAME AND - F.RDB$RELATION_NAME EQ T.RDB$RELATION_NAME - - if (!DYN_REQUEST(drq_l_view_rel2)) - DYN_REQUEST(drq_l_view_rel2) = request; - - found = true; - END_FOR; - - if (!DYN_REQUEST(drq_l_view_rel2)) - DYN_REQUEST(drq_l_view_rel2) = request; - - if (!found) { - request = CMP_find_request(tdbb, drq_m_rel_flds, DYN_REQUESTS); - id = drq_m_rel_flds; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - F IN RDB$RELATION_FIELDS WITH F.RDB$RELATION_NAME EQ r.c_str() - - if (!DYN_REQUEST(drq_m_rel_flds)) - DYN_REQUEST(drq_m_rel_flds) = request; - - MODIFY F USING - F.RDB$UPDATE_FLAG = FALSE; - END_MODIFY; - END_FOR; - - if (!DYN_REQUEST(drq_m_rel_flds)) - DYN_REQUEST(drq_m_rel_flds) = request; - } - - } // try - catch (const Firebird::Exception& ex) - { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - switch (id) - { - case drq_e_trg_msgs: - DYN_error_punt(true, 65); - /* msg 65: "ERASE RDB$TRIGGER_MESSAGES failed" */ - break; - case drq_e_trigger: - DYN_error_punt(true, 66); - /* msg 66: "ERASE RDB$TRIGGERS failed" */ - break; - case drq_e_trg_prv: - DYN_error_punt(true, 62); - /* msg 62: "ERASE RDB$USER_PRIVILEGES failed" */ - break; - default: - DYN_error_punt(true, 68); - /* msg 68: "MODIFY RDB$VIEW_RELATIONS failed" */ - break; - } - } - - if (*(*ptr)++ != isc_dyn_end) { - DYN_unsupported_verb(); - } - - return; - -dyn_punt_147: - DYN_error_punt(false, 147, t.c_str()); - /* msg 147: "Trigger %s not found" */ -} - - -void DYN_delete_trigger_msg( Global* gbl, const UCHAR** ptr, Firebird::MetaName* trigger_name) -{ -/************************************** - * - * D Y N _ d e l e t e _ t r i g g e r _ m s g - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement that - * deletes an trigger message. - * - **************************************/ - Firebird::MetaName t; - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - const int number = DYN_get_number(ptr); - if (trigger_name) - t = *trigger_name; - else if (*(*ptr)++ == isc_dyn_trg_name) - GET_STRING(ptr, t); - else - { - DYN_error_punt(false, 70); - // msg 70: "TRIGGER NAME expected" - } - - jrd_req* request = CMP_find_request(tdbb, drq_e_trg_msg, DYN_REQUESTS); - - bool found = false; - - try { - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$TRIGGER_MESSAGES - WITH X.RDB$TRIGGER_NAME EQ t.c_str() AND X.RDB$MESSAGE_NUMBER EQ number - - if (!DYN_REQUEST(drq_e_trg_msg)) - DYN_REQUEST(drq_e_trg_msg) = request; - - found = true; - ERASE X; - END_FOR; - - if (!DYN_REQUEST(drq_e_trg_msg)) - DYN_REQUEST(drq_e_trg_msg) = request; - - } - catch (const Firebird::Exception& ex) { - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - DYN_error_punt(true, 71); - /* msg 71: "ERASE TRIGGER MESSAGE failed" */ - } - - if (!found) - { - DYN_error_punt(false, 72); - /* msg 72: "Trigger Message not found" */ - } - - if (*(*ptr)++ != isc_dyn_end) { - DYN_unsupported_verb(); - } -} - - static bool delete_constraint_records(Global* gbl, const Firebird::MetaName& constraint_name, const Firebird::MetaName& relation_name) @@ -2272,11 +1724,11 @@ static bool delete_index_segment_records(Global* gbl, } -static bool delete_security_class2( Global* gbl, const Firebird::MetaName& security_class) +bool DYN_delete_security_class2(jrd_tra* transaction, const Firebird::MetaName& security_class) { /************************************** * - * d e l e t e _ s e c u r i t y _ c l a s s 2 + * D Y N _ d e l e t e _ s e c u r i t y _ c l a s s 2 * ************************************** * @@ -2295,7 +1747,7 @@ static bool delete_security_class2( Global* gbl, const Firebird::MetaName& secur try { - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) SC IN RDB$SECURITY_CLASSES WITH SC.RDB$SECURITY_CLASS EQ security_class.c_str() @@ -2321,4 +1773,3 @@ static bool delete_security_class2( Global* gbl, const Firebird::MetaName& secur return found; } - diff --git a/src/jrd/dyn_df_proto.h b/src/jrd/dyn_df_proto.h index 3d424cc8c5..7b689e982f 100644 --- a/src/jrd/dyn_df_proto.h +++ b/src/jrd/dyn_df_proto.h @@ -37,9 +37,7 @@ void DYN_define_global_field(Jrd::Global*, const UCHAR**, const Firebird::MetaNa void DYN_define_index(Jrd::Global*, const UCHAR**, const Firebird::MetaName*, UCHAR, Firebird::MetaName*, Firebird::MetaName*, Firebird::MetaName*, UCHAR*); void DYN_define_local_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*, Firebird::MetaName*); -void DYN_define_parameter(Jrd::Global*, const UCHAR**, Firebird::MetaName*); -void DYN_define_procedure(Jrd::Global*, const UCHAR**); -void DYN_define_relation(Jrd::Global*, const UCHAR**); +void DYN_define_relation(Jrd::Global*, const UCHAR**, bool view); void DYN_define_role(Jrd::Global*, const UCHAR**); void DYN_define_security_class(Jrd::Global*, const UCHAR**); void DYN_define_shadow(Jrd::Global*, const UCHAR**); diff --git a/src/jrd/dyn_dl_proto.h b/src/jrd/dyn_dl_proto.h index 01ae355599..8d894771ee 100644 --- a/src/jrd/dyn_dl_proto.h +++ b/src/jrd/dyn_dl_proto.h @@ -29,19 +29,15 @@ void DYN_delete_constraint(Jrd::Global*, const UCHAR**, const Firebird::MetaName void DYN_delete_dimensions(Jrd::Global*, const UCHAR**); //, const Firebird::MetaName*, Firebird::MetaName*); void DYN_delete_exception(Jrd::Global*, const UCHAR**); void DYN_delete_filter(Jrd::Global*, const UCHAR**); -void DYN_delete_function(Jrd::Global*, const UCHAR**); void DYN_delete_generator(Jrd::Global*, const UCHAR**); void DYN_delete_global_field(Jrd::Global*, const UCHAR**); void DYN_delete_index(Jrd::Global*, const UCHAR**); -void DYN_delete_local_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*); //, Firebird::MetaName*); -void DYN_delete_parameter(Jrd::Global*, const UCHAR**, Firebird::MetaName*); -void DYN_delete_procedure(Jrd::Global*, const UCHAR**); +void DYN_delete_local_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*); void DYN_delete_relation(Jrd::Global*, const UCHAR**, const Firebird::MetaName*); void DYN_delete_role(Jrd::Global*, const UCHAR**); void DYN_delete_security_class(Jrd::Global*, const UCHAR**); +bool DYN_delete_security_class2(Jrd::jrd_tra* transaction, const Firebird::MetaName& security_class); void DYN_delete_shadow(Jrd::Global*, const UCHAR**); -void DYN_delete_trigger(Jrd::Global*, const UCHAR**); -void DYN_delete_trigger_msg(Jrd::Global*, const UCHAR**, Firebird::MetaName*); #endif // JRD_DYN_DL_PROTO_H diff --git a/src/jrd/dyn_md_proto.h b/src/jrd/dyn_md_proto.h index ae54718f6e..bdea9b5a97 100644 --- a/src/jrd/dyn_md_proto.h +++ b/src/jrd/dyn_md_proto.h @@ -34,11 +34,8 @@ void DYN_modify_generator(Jrd::Global*, const UCHAR**); void DYN_modify_global_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*, Firebird::MetaName*); void DYN_modify_index(Jrd::Global*, const UCHAR**); void DYN_modify_local_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*); -void DYN_modify_parameter(Jrd::Global*, const UCHAR**); -void DYN_modify_procedure(Jrd::Global*, const UCHAR**); void DYN_modify_relation(Jrd::Global*, const UCHAR**); void DYN_modify_role(Jrd::Global*, const UCHAR**); -void DYN_modify_trigger(Jrd::Global*, const UCHAR**); void DYN_modify_trigger_msg(Jrd::Global*, const UCHAR**, Firebird::MetaName*); void DYN_modify_sql_field(Jrd::Global*, const UCHAR**, const Firebird::MetaName*); void DYN_modify_view(Jrd::Global*, const UCHAR**); diff --git a/src/jrd/dyn_mod.epp b/src/jrd/dyn_mod.epp index 4329bfae8f..09c8bf39aa 100644 --- a/src/jrd/dyn_mod.epp +++ b/src/jrd/dyn_mod.epp @@ -315,7 +315,7 @@ void DYN_modify_database( Global* gbl, const UCHAR** ptr) if (!DBB.RDB$CHARACTER_SET_NAME.NULL) { - AlterCharSetNode node(*tdbb->getDefaultPool(), + AlterCharSetNode node(*tdbb->getDefaultPool(), "", DBB.RDB$CHARACTER_SET_NAME, collation); node.execute(tdbb, gbl->gbl_transaction); } @@ -366,11 +366,11 @@ void DYN_modify_exception( Global* gbl, const UCHAR** ptr) jrd_req* request = CMP_find_request(tdbb, drq_m_xcp, DYN_REQUESTS); bool found = false; - try { - MetaName t; GET_STRING(ptr, t); + try { + found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$EXCEPTIONS @@ -380,6 +380,10 @@ void DYN_modify_exception( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_m_xcp) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_EXCEPTION, t, gbl->sqlText); + MODIFY X UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) @@ -406,7 +410,7 @@ void DYN_modify_exception( Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_m_xcp)) DYN_REQUEST(drq_m_xcp) = request; - } + } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -414,7 +418,12 @@ void DYN_modify_exception( Global* gbl, const UCHAR** ptr) // msg 145: "MODIFY EXCEPTION failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_EXCEPTION, t, gbl->sqlText); + } + else { DYN_error_punt(false, 144); // msg 144: "Exception not found" @@ -503,14 +512,25 @@ void DYN_modify_function(Global* gbl, const UCHAR** ptr) GET_STRING(ptr, t); try { - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FUNCTIONS - WITH X.RDB$FUNCTION_NAME EQ t + WITH X.RDB$FUNCTION_NAME EQ t AND + X.RDB$PACKAGE_NAME MISSING if (!DYN_REQUEST(drq_m_fun)) DYN_REQUEST(drq_m_fun) = request; + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_FUNCTION, t, gbl->sqlText); + + if (!X.RDB$ENGINE_NAME.NULL) + { + //// TODO: localize + status_exception::raise( + Arg::Gds(isc_random) << + Arg::Str("Cannot alter new style function with ALTER EXTERNAL FUNCTION")); + } + found = true; MODIFY X UCHAR verb; @@ -552,7 +572,12 @@ void DYN_modify_function(Global* gbl, const UCHAR** ptr) // msg 92: "MODIFY RDB$FUNCTIONS failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_FUNCTION, t, gbl->sqlText); + } + else { DYN_error_punt(false, 41, t); // msg 41: "Function %s not found" @@ -575,7 +600,6 @@ void DYN_modify_generator(Global* gbl, const UCHAR** ptr) GET_STRING(ptr, t); try { - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ t @@ -584,6 +608,10 @@ void DYN_modify_generator(Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_m_gen) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_SEQUENCE, t, gbl->sqlText); + MODIFY X UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) @@ -614,7 +642,12 @@ void DYN_modify_generator(Global* gbl, const UCHAR** ptr) // msg 94: "MODIFY GENERATOR failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_SEQUENCE, t, gbl->sqlText); + } + else { DYN_error_punt(false, 214, t); // msg 214: "Generator %s not found" @@ -661,7 +694,6 @@ void DYN_modify_global_field(Global* gbl, GET_STRING(ptr, orig_dom.dyn_fld_name); - found = false; int field_adjusted_count = 0; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) @@ -672,6 +704,9 @@ void DYN_modify_global_field(Global* gbl, found = true; + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_DOMAIN, orig_dom.dyn_fld_name, gbl->sqlText); + DSC_make_descriptor(&orig_dom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, @@ -1145,7 +1180,7 @@ void DYN_modify_global_field(Global* gbl, if (!DYN_REQUEST(drq_m_gfield)) DYN_REQUEST(drq_m_gfield) = request; - } + } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -1153,7 +1188,12 @@ void DYN_modify_global_field(Global* gbl, /* msg 87: "MODIFY RDB$FIELDS failed" */ } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_DOMAIN, orig_dom.dyn_fld_name, gbl->sqlText); + } + else { DYN_error_punt(false, 89); /* msg 89: "Global field not found" */ @@ -1185,7 +1225,6 @@ void DYN_modify_index( Global* gbl, const UCHAR** ptr) GET_STRING(ptr, name); - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.c_str() @@ -1193,6 +1232,10 @@ void DYN_modify_index( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_m_index) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_INDEX, name, gbl->sqlText); + MODIFY IDX USING UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) @@ -1231,7 +1274,7 @@ void DYN_modify_index( Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_m_index)) DYN_REQUEST(drq_m_index) = request; - } + } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -1239,7 +1282,12 @@ void DYN_modify_index( Global* gbl, const UCHAR** ptr) // msg 91: "MODIFY RDB$INDICES failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_INDEX, name, gbl->sqlText); + } + else { DYN_error_punt(false, 48); /* msg 48: "Index not found" */ @@ -1278,9 +1326,11 @@ void DYN_modify_local_field(Global* gbl, bool sfflag, qnflag, qhflag, esflag, dflag, system_flag, scflag, nnflag, ntflag, npflag; sfflag = qnflag = qhflag = esflag = dflag = scflag = npflag = nnflag = ntflag = false; + TriState nullFlag; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) + { switch (verb) { case isc_dyn_rel_name: @@ -1338,10 +1388,19 @@ void DYN_modify_local_field(Global* gbl, DYN_skip_attribute(ptr); break; + case isc_dyn_fld_not_null: + nullFlag = true; + break; + + case isc_dyn_fld_null: + nullFlag = false; + break; + default: --(*ptr); DYN_execute(gbl, ptr, relation_name, NULL, NULL, NULL, NULL); } + } jrd_req* request = CMP_find_request(tdbb, drq_m_lfield, DYN_REQUESTS); bool found = false; @@ -1425,6 +1484,9 @@ void DYN_modify_local_field(Global* gbl, else FLD.RDB$DESCRIPTION.NULL = TRUE; } + + if (nullFlag.isAssigned()) + FLD.RDB$NULL_FLAG = (SSHORT) nullFlag.asBool(); END_MODIFY; END_FOR; @@ -1449,231 +1511,6 @@ void DYN_modify_local_field(Global* gbl, } -// *************************************** -// D Y N _ m o d i f y _ p a r a m e t e r -// *************************************** -// The sole objective of this function is to allow changing the comment -// in procedure's parameters without going into other changes. -void DYN_modify_parameter(Global* gbl, const UCHAR** ptr) -{ - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = CMP_find_request(tdbb, drq_m_prm, DYN_REQUESTS); - bool found = false; - - SqlIdentifier t; - GET_STRING(ptr, t); - SqlIdentifier p; - - try { - if (**ptr == isc_dyn_prc_name) - { - ++*ptr; - GET_STRING(ptr, p); - } - else - DYN_unsupported_verb(); - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$PROCEDURE_PARAMETERS - WITH X.RDB$PARAMETER_NAME EQ t - AND X.RDB$PROCEDURE_NAME EQ p - - if (!DYN_REQUEST(drq_m_prm)) - DYN_REQUEST(drq_m_prm) = request; - - found = true; - MODIFY X - UCHAR verb; - while ((verb = *(*ptr)++) != isc_dyn_end) - { - switch (verb) - { - case isc_dyn_description: - if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION)) - X.RDB$DESCRIPTION.NULL = FALSE; - else - X.RDB$DESCRIPTION.NULL = TRUE; - break; - - default: - DYN_unsupported_verb(); - } - } - END_MODIFY; - END_FOR; - - if (!DYN_REQUEST(drq_m_prm)) - DYN_REQUEST(drq_m_prm) = request; - } - catch (const Exception& ex) { - stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - DYN_error_punt(true, 100); - // msg 100: "MODIFY RDB$PROCEDURE_PARAMETERS failed" - } - - if (!found) - { - DYN_error_punt(false, 146, SafeArg() << t << p); - // msg : "Parameter %s in procedure %s not found" - } -} - - -void DYN_modify_procedure( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ m o d i f y _ p r o c e d u r e - * - ************************************** - * - * Functional description - * Execute a dynamic ddl statement. - * - **************************************/ - MetaName procedure_name; - - GET_STRING(ptr, procedure_name); - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = NULL; - bool found = false; - bool only_description = false; - - try { - - request = CMP_find_request(tdbb, drq_m_prcs, DYN_REQUESTS); - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME = procedure_name.c_str() - - if (!DYN_REQUEST(drq_m_prcs)) - DYN_REQUEST(drq_m_prcs) = request; - - found = true; - - /* Set NULL flags to TRUE only for fields which must be specified in the DYN string. - Retain existing values on other fields (RDB$DESCRIPTION, RDB$SECURITY_CLASS), - unless explicitly changed in the DYN string */ - MODIFY P - - if (**ptr == isc_dyn_description) - { - ++*ptr; - if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION)) - P.RDB$DESCRIPTION.NULL = FALSE; - else - P.RDB$DESCRIPTION.NULL = TRUE; - - if (**ptr == isc_dyn_end) - only_description = true; - } - - if (!only_description) - { - P.RDB$SYSTEM_FLAG = 0; - P.RDB$SYSTEM_FLAG.NULL = FALSE; - P.RDB$PROCEDURE_BLR.NULL = TRUE; - P.RDB$PROCEDURE_SOURCE.NULL = TRUE; - P.RDB$PROCEDURE_INPUTS.NULL = TRUE; - P.RDB$PROCEDURE_OUTPUTS.NULL = TRUE; - - // ODS_11_1 fields - P.RDB$DEBUG_INFO.NULL = TRUE; - - P.RDB$PROCEDURE_TYPE = prc_legacy; - P.RDB$PROCEDURE_TYPE.NULL = FALSE; - - P.RDB$VALID_BLR = TRUE; - P.RDB$VALID_BLR.NULL = FALSE; - } - - UCHAR verb; - while ((verb = *(*ptr)++) != isc_dyn_end) - switch (verb) - { - case isc_dyn_system_flag: - P.RDB$SYSTEM_FLAG = DYN_get_number(ptr); - P.RDB$SYSTEM_FLAG.NULL = FALSE; - break; - - case isc_dyn_prc_blr: - if (DYN_put_blr_blob(gbl, ptr, &P.RDB$PROCEDURE_BLR)) - P.RDB$PROCEDURE_BLR.NULL = FALSE; - else - P.RDB$PROCEDURE_BLR.NULL = TRUE; - break; - - case isc_dyn_description: - if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION)) - P.RDB$DESCRIPTION.NULL = FALSE; - else - P.RDB$DESCRIPTION.NULL = TRUE; - break; - - case isc_dyn_prc_source: - if (DYN_put_text_blob(gbl, ptr, &P.RDB$PROCEDURE_SOURCE)) - P.RDB$PROCEDURE_SOURCE.NULL = FALSE; - else - P.RDB$PROCEDURE_SOURCE.NULL = TRUE; - break; - - case isc_dyn_prc_inputs: - P.RDB$PROCEDURE_INPUTS = DYN_get_number(ptr); - P.RDB$PROCEDURE_INPUTS.NULL = FALSE; - break; - - case isc_dyn_prc_outputs: - P.RDB$PROCEDURE_OUTPUTS = DYN_get_number(ptr); - P.RDB$PROCEDURE_OUTPUTS.NULL = FALSE; - break; - - case isc_dyn_security_class: - GET_STRING(ptr, P.RDB$SECURITY_CLASS); - P.RDB$SECURITY_CLASS.NULL = FALSE; - break; - - case isc_dyn_debug_info: - DYN_put_blr_blob(gbl, ptr, &P.RDB$DEBUG_INFO); // ODS_11_1 field - P.RDB$DEBUG_INFO.NULL = FALSE; - break; - - case isc_dyn_prc_type: - P.RDB$PROCEDURE_TYPE = DYN_get_number(ptr); // ODS_11_1 field - break; - - default: - --(*ptr); - DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, &procedure_name); - } - - END_MODIFY; - END_FOR; - - if (!DYN_REQUEST(drq_m_prcs)) - DYN_REQUEST(drq_m_prcs) = request; - } - catch (const Exception& ex) { - stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - DYN_error_punt(true, 141); - /* msg 141: "MODIFY RDB$PROCEDURES failed" */ - } - - if (!found) { - DYN_error_punt(false, 140, procedure_name.c_str()); - /* msg 140: "Procedure %s not found" */ - } -} - - void DYN_modify_relation( Global* gbl, const UCHAR** ptr) { /************************************** @@ -1698,13 +1535,15 @@ void DYN_modify_relation( Global* gbl, const UCHAR** ptr) try { - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ name.c_str() if (!DYN_REQUEST(drq_m_relation)) DYN_REQUEST(drq_m_relation) = request; + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_TABLE, name, gbl->sqlText); + if (!REL.RDB$VIEW_BLR.NULL) DYN_error_punt(false, 177); @@ -1756,7 +1595,7 @@ void DYN_modify_relation( Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_m_relation)) DYN_REQUEST(drq_m_relation) = request; - } + } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -1764,7 +1603,12 @@ void DYN_modify_relation( Global* gbl, const UCHAR** ptr) /* msg 99: "MODIFY RDB$RELATIONS failed" */ } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_TABLE, name, gbl->sqlText); + } + else { DYN_error_punt(false, 101); /* msg 101: "Relation field not found" */ @@ -1787,7 +1631,6 @@ void DYN_modify_role(Global* gbl, const UCHAR** ptr) GET_STRING(ptr, t); try { - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$ROLES WITH X.RDB$ROLE_NAME EQ t @@ -1796,6 +1639,10 @@ void DYN_modify_role(Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_m_rol) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_ROLE, t, gbl->sqlText); + MODIFY X UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) @@ -1826,7 +1673,12 @@ void DYN_modify_role(Global* gbl, const UCHAR** ptr) // msg 158: "MODIFY RDB$ROLES failed" } - if (!found) + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_ROLE, t, gbl->sqlText); + } + else { DYN_error_punt(false, 155, t); // msg 155: "Role %s not found" @@ -1834,192 +1686,6 @@ void DYN_modify_role(Global* gbl, const UCHAR** ptr) } -void DYN_modify_trigger( Global* gbl, const UCHAR** ptr) -{ -/************************************** - * - * D Y N _ m o d i f y _ t r i g g e r - * - ************************************** - * - * Functional description - * Modify a trigger for a relation. - * - **************************************/ - MetaName trigger_name; - - thread_db* tdbb = JRD_get_thread_data(); - Database* dbb = tdbb->getDatabase(); - - jrd_req* request = CMP_find_request(tdbb, drq_m_trigger, DYN_REQUESTS); - bool found = false; - bool only_description = false; - - try { - - GET_STRING(ptr, trigger_name); - const UCHAR* source = NULL; - const UCHAR* blr = NULL; - const UCHAR* debug_info_ptr = NULL; - - found = false; - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) - X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ trigger_name.c_str() - - if (!DYN_REQUEST(drq_m_trigger)) { - DYN_REQUEST(drq_m_trigger) = request; - } - - if (X.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith()) - ERR_post(Arg::Gds(isc_adm_task_denied)); - - // CVC: I think that we'll do well by hiding our automatic triggers from this function. - // Why would a user want to fiddle with triggers that were generated automatically? - - if (!X.RDB$SYSTEM_FLAG.NULL) - { - switch (X.RDB$SYSTEM_FLAG) - { - case fb_sysflag_check_constraint: - case fb_sysflag_referential_constraint: - case fb_sysflag_view_check: - DYN_error_punt(false, 109); - // msg 109: "Triggers created automatically cannot be modified" - break; - default: - break; - } - } - - found = true; - - MODIFY X - - if (**ptr == isc_dyn_description) - { - ++*ptr; - if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION)) - X.RDB$DESCRIPTION.NULL = FALSE; - else - X.RDB$DESCRIPTION.NULL = TRUE; - - if (**ptr == isc_dyn_end) - only_description = true; - } - - UCHAR verb; - while ((verb = *(*ptr)++) != isc_dyn_end) - switch (verb) - { - case isc_dyn_trg_name: - { - MetaName new_trigger_name; - GET_STRING(ptr, new_trigger_name); - if (new_trigger_name.length() == 0) - DYN_error_punt(false, 212); - // msg 212: "Zero length identifiers not allowed" - strcpy (X.RDB$TRIGGER_NAME, new_trigger_name.c_str()); - } - break; - - case isc_dyn_trg_type: - X.RDB$TRIGGER_TYPE = DYN_get_number(ptr); - X.RDB$TRIGGER_TYPE.NULL = FALSE; - break; - - case isc_dyn_trg_sequence: - X.RDB$TRIGGER_SEQUENCE = DYN_get_number(ptr); - X.RDB$TRIGGER_SEQUENCE.NULL = FALSE; - break; - - case isc_dyn_trg_inactive: - X.RDB$TRIGGER_INACTIVE = DYN_get_number(ptr); - X.RDB$TRIGGER_INACTIVE.NULL = FALSE; - break; - - case isc_dyn_rel_name: - GET_STRING(ptr, X.RDB$RELATION_NAME); - X.RDB$RELATION_NAME.NULL = FALSE; - break; - - case isc_dyn_trg_blr: - blr = *ptr; - DYN_skip_attribute(ptr); - if (DYN_put_blr_blob(gbl, &blr, &X.RDB$TRIGGER_BLR)) - X.RDB$TRIGGER_BLR.NULL = FALSE; - else - X.RDB$TRIGGER_BLR.NULL = TRUE; - break; - - case isc_dyn_trg_source: - source = *ptr; - DYN_skip_attribute(ptr); - if (DYN_put_text_blob(gbl, &source, &X.RDB$TRIGGER_SOURCE)) - X.RDB$TRIGGER_SOURCE.NULL = FALSE; - else - X.RDB$TRIGGER_SOURCE.NULL = TRUE; - break; - - case isc_dyn_description: - if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION)) - X.RDB$DESCRIPTION.NULL = FALSE; - else - X.RDB$DESCRIPTION.NULL = TRUE; - break; - - case isc_dyn_debug_info: - debug_info_ptr = *ptr; - DYN_skip_attribute(ptr); - break; - - default: - --(*ptr); - DYN_execute(gbl, ptr, NULL, NULL, &trigger_name, NULL, NULL); - } - END_MODIFY; - END_FOR; - - if (!DYN_REQUEST(drq_m_trigger)) - DYN_REQUEST(drq_m_trigger) = request; - - if (!only_description) - { - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) - { - jrd_req* sub_request = NULL; - - FOR(REQUEST_HANDLE sub_request TRANSACTION_HANDLE gbl->gbl_transaction) - TRG IN RDB$TRIGGERS WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str() - - MODIFY TRG USING - TRG.RDB$VALID_BLR = TRUE; - TRG.RDB$VALID_BLR.NULL = FALSE; - - TRG.RDB$DEBUG_INFO.NULL = (debug_info_ptr == NULL) ? TRUE : FALSE; - if (debug_info_ptr) - DYN_put_blr_blob(gbl, &debug_info_ptr, &TRG.RDB$DEBUG_INFO); - END_MODIFY; - END_FOR; - - CMP_release(tdbb, sub_request); - } - } - - } - catch (const Exception& ex) { - stuff_exception(tdbb->tdbb_status_vector, ex); - DYN_rundown_request(request, -1); - DYN_error_punt(true, 102); - /* msg 102: "MODIFY TRIGGER failed" */ - } - - if (!found) { - DYN_error_punt(false, 147, trigger_name.c_str()); - /* msg 147: "Trigger %s not found" */ - } -} - - void DYN_modify_trigger_msg( Global* gbl, const UCHAR** ptr, MetaName* trigger_name) { /************************************** @@ -2122,7 +1788,6 @@ void DYN_modify_view( Global* gbl, const UCHAR** ptr) request = CMP_find_request(tdbb, drq_m_view, DYN_REQUESTS); - found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ view_name.c_str() @@ -2132,6 +1797,10 @@ void DYN_modify_view( Global* gbl, const UCHAR** ptr) DYN_REQUEST(drq_m_view) = request; found = true; + + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_VIEW, view_name, gbl->sqlText); + bool only_description = false; MODIFY REL @@ -2226,7 +1895,7 @@ void DYN_modify_view( Global* gbl, const UCHAR** ptr) if (!DYN_REQUEST(drq_m_view)) DYN_REQUEST(drq_m_view) = request; - } + } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); @@ -2234,7 +1903,13 @@ void DYN_modify_view( Global* gbl, const UCHAR** ptr) /* msg 99: "MODIFY RDB$RELATIONS failed" */ } - if (!found) { + if (found) + { + DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_VIEW, view_name, gbl->sqlText); + } + else + { DYN_error_punt(false, 54, view_name.c_str()); /* msg 54: "View %s not found" */ } diff --git a/src/jrd/dyn_proto.h b/src/jrd/dyn_proto.h index 23fe0f771a..83f34b3ff0 100644 --- a/src/jrd/dyn_proto.h +++ b/src/jrd/dyn_proto.h @@ -30,7 +30,8 @@ namespace Jrd class jrd_tra; } -void DYN_ddl(/*Jrd::Attachment*,*/ Jrd::jrd_tra*, USHORT, const UCHAR*); +void DYN_ddl(/*Jrd::Attachment*,*/ Jrd::jrd_tra*, USHORT, const UCHAR*, + const Firebird::string& sqlText); #endif // JRD_DYN_PROTO_H diff --git a/src/jrd/dyn_ut_proto.h b/src/jrd/dyn_ut_proto.h index 5bce1cfcfd..28cb54c06f 100644 --- a/src/jrd/dyn_ut_proto.h +++ b/src/jrd/dyn_ut_proto.h @@ -39,7 +39,9 @@ void DYN_UTIL_generate_field_position(Jrd::thread_db*, Jrd::Global*, void DYN_UTIL_generate_field_name(Jrd::thread_db*, Jrd::Global*, TEXT*); void DYN_UTIL_generate_field_name(Jrd::thread_db*, Jrd::Global*, Firebird::MetaName&); void DYN_UTIL_generate_constraint_name(Jrd::thread_db*, Jrd::Global*, Firebird::MetaName&); -SINT64 DYN_UTIL_gen_unique_id(Jrd::thread_db*, Jrd::Global*, SSHORT, const char*); +void DYN_UTIL_check_unique_name(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, + const Firebird::MetaName& object_name, int object_type); +SINT64 DYN_UTIL_gen_unique_id(Jrd::thread_db*, SSHORT, const char*); bool DYN_UTIL_is_array(Jrd::thread_db*, Jrd::Global*, const Firebird::MetaName&); void DYN_UTIL_copy_domain(Jrd::thread_db*, Jrd::Global* gbl, const Firebird::MetaName&, const Firebird::MetaName&); diff --git a/src/jrd/dyn_util.epp b/src/jrd/dyn_util.epp index 3a563165ba..8080adabc4 100644 --- a/src/jrd/dyn_util.epp +++ b/src/jrd/dyn_util.epp @@ -105,9 +105,132 @@ static const UCHAR prot_blr[] = }; +void DYN_UTIL_check_unique_name(thread_db* tdbb, jrd_tra* transaction, + const Firebird::MetaName& object_name, int object_type) +{ +/************************************** + * + * D Y N _ U T I L _ c h e c k _ u n i q u e _ n a m e + * + ************************************** + * + * Functional description + * Check if an object already exists. + * If yes then return error. + * + **************************************/ + + SET_TDBB(tdbb); + Database* dbb = tdbb->getDatabase(); + + USHORT error_code = 0; + jrd_req* request = NULL; + + try + { + switch (object_type) + { + case obj_relation: + case obj_procedure: + request = CMP_find_request(tdbb, drq_l_rel_name, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + EREL IN RDB$RELATIONS WITH EREL.RDB$RELATION_NAME EQ object_name.c_str() + + if (!DYN_REQUEST(drq_l_rel_name)) + DYN_REQUEST(drq_l_rel_name) = request; + + error_code = 132; + END_FOR; + + if (!DYN_REQUEST(drq_l_rel_name)) + DYN_REQUEST(drq_l_rel_name) = request; + + if (!error_code) + { + request = CMP_find_request(tdbb, drq_l_prc_name, DYN_REQUESTS); + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + EPRC IN RDB$PROCEDURES + WITH EPRC.RDB$PROCEDURE_NAME EQ object_name.c_str() AND + EPRC.RDB$PACKAGE_NAME MISSING + + if (!DYN_REQUEST(drq_l_prc_name)) + DYN_REQUEST(drq_l_prc_name) = request; + + error_code = 135; + END_FOR; + + if (!DYN_REQUEST(drq_l_prc_name)) + DYN_REQUEST(drq_l_prc_name) = request; + } + break; + + case obj_index: + request = CMP_find_request(tdbb, drq_l_idx_name, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + EIDX IN RDB$INDICES WITH EIDX.RDB$INDEX_NAME EQ object_name.c_str() + + if (!DYN_REQUEST(drq_l_idx_name)) + DYN_REQUEST(drq_l_idx_name) = request; + + error_code = 251; + END_FOR; + + if (!DYN_REQUEST(drq_l_idx_name)) + DYN_REQUEST(drq_l_idx_name) = request; + break; + + case obj_exception: + request = CMP_find_request(tdbb, drq_l_xcp_name, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + EXCP IN RDB$EXCEPTIONS WITH EXCP.RDB$EXCEPTION_NAME EQ object_name.c_str() + + if (!DYN_REQUEST(drq_l_xcp_name)) + DYN_REQUEST(drq_l_xcp_name) = request; + + error_code = 253; + END_FOR; + + if (!DYN_REQUEST(drq_l_xcp_name)) + DYN_REQUEST(drq_l_xcp_name) = request; + break; + + case obj_generator: + request = CMP_find_request(tdbb, drq_l_gen_name, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + EGEN IN RDB$GENERATORS WITH EGEN.RDB$GENERATOR_NAME EQ object_name.c_str() + + if (!DYN_REQUEST(drq_l_gen_name)) + DYN_REQUEST(drq_l_gen_name) = request; + + error_code = 254; + END_FOR; + + if (!DYN_REQUEST(drq_l_gen_name)) + DYN_REQUEST(drq_l_gen_name) = request; + break; + + default: + fb_assert(false); + } + } + catch (const Firebird::Exception&) + { + DYN_rundown_request(request, -1); + throw; + } + + if (error_code) { + DYN_error_punt(false, error_code, object_name.c_str()); + } +} + SINT64 DYN_UTIL_gen_unique_id(thread_db* tdbb, - Global* /*gbl*/, SSHORT id, const char* generator_name) { @@ -193,7 +316,7 @@ void DYN_UTIL_generate_constraint_name( thread_db* tdbb, Global* gbl, Firebird:: do { buffer.printf("INTEG_%" SQUADFORMAT, - DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_con, "RDB$CONSTRAINT_NAME")); + DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_con, "RDB$CONSTRAINT_NAME")); request = CMP_find_request(tdbb, drq_f_nxt_con, DYN_REQUESTS); id = drq_f_nxt_con; @@ -265,7 +388,7 @@ void DYN_UTIL_generate_field_name( thread_db* tdbb, Global* gbl, Firebird::MetaN do { buffer.printf("RDB$%" SQUADFORMAT, - DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_fld, "RDB$FIELD_NAME")); + DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fld, "RDB$FIELD_NAME")); request = CMP_find_request(tdbb, drq_f_nxt_fld, DYN_REQUESTS); id = drq_f_nxt_fld; @@ -383,7 +506,7 @@ void DYN_UTIL_generate_index_name(thread_db* tdbb, Global* gbl, } buffer.printf(format, - DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_idx, "RDB$INDEX_NAME")); + DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_idx, "RDB$INDEX_NAME")); request = CMP_find_request(tdbb, drq_f_nxt_idx, DYN_REQUESTS); id = drq_f_nxt_idx; @@ -436,7 +559,7 @@ void DYN_UTIL_generate_trigger_name( thread_db* tdbb, Global* gbl, Firebird::Met do { buffer.printf("CHECK_%" SQUADFORMAT, - DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_trg, "RDB$TRIGGER_NAME")); + DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_trg, "RDB$TRIGGER_NAME")); request = CMP_find_request(tdbb, drq_f_nxt_trg, DYN_REQUESTS); id = drq_f_nxt_trg; @@ -527,6 +650,7 @@ bool DYN_UTIL_find_field_source(thread_db* tdbb, VRL IN RDB$VIEW_RELATIONS CROSS PPR IN RDB$PROCEDURE_PARAMETERS WITH VRL.RDB$RELATION_NAME EQ PPR.RDB$PROCEDURE_NAME AND + PPR.RDB$PACKAGE_NAME MISSING AND // ASF: not sure about this VRL.RDB$VIEW_NAME EQ view_name.c_str() AND VRL.RDB$VIEW_CONTEXT EQ context AND PPR.RDB$PARAMETER_NAME EQ local_name diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 3648b8de65..f3e09158a5 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -134,7 +134,6 @@ static dsc* add_datetime(const dsc*, const jrd_nod*, impure_value*); static dsc* add_sql_date(const dsc*, const jrd_nod*, impure_value*); static dsc* add_sql_time(const dsc*, const jrd_nod*, impure_value*); static dsc* add_timestamp(const dsc*, const jrd_nod*, impure_value*); -static void adjust_text_descriptor(thread_db*, dsc*); static dsc* binary_value(thread_db*, const jrd_nod*, impure_value*); static dsc* cast(thread_db*, dsc*, const jrd_nod*, impure_value*); static void compute_agg_distinct(thread_db*, jrd_nod*); @@ -232,7 +231,7 @@ dsc* EVL_assign_to(thread_db* tdbb, jrd_nod* node) convert the charset to the declared charset of the process. */ INTL_ASSIGN_DSC(&impure->vlu_desc, - tdbb->getAttachment()->att_charset, COLLATE_NONE); + tdbb->getCharSet(), COLLATE_NONE); } return &impure->vlu_desc; @@ -864,7 +863,7 @@ dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node) impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; if (impure->vlu_desc.dsc_dtype == dtype_text) - adjust_text_descriptor(tdbb, &impure->vlu_desc); + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); USHORT* impure_flags = (USHORT*) ((UCHAR *) request + (IPTR) message->nod_arg[e_msg_impure_flags] + @@ -945,7 +944,7 @@ dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node) if (!relation || !(relation->rel_flags & REL_system)) { if (impure->vlu_desc.dsc_dtype == dtype_text) - adjust_text_descriptor(tdbb, &impure->vlu_desc); + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); } return &impure->vlu_desc; @@ -1102,7 +1101,7 @@ dsc* EVL_expr(thread_db* tdbb, jrd_nod* const node) impure->vlu_desc = impure2->vlu_desc; if (impure->vlu_desc.dsc_dtype == dtype_text) - adjust_text_descriptor(tdbb, &impure->vlu_desc); + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); if (!(impure2->vlu_flags & VLU_checked)) { @@ -1399,7 +1398,7 @@ bool EVL_field(jrd_rel* relation, Record* record, USHORT id, dsc* desc) } -USHORT EVL_group(thread_db* tdbb, RecordSource* rsb, jrd_nod *const node, USHORT state) +USHORT EVL_group(thread_db* tdbb, RecordSource* rsb, jrd_nod* const node, USHORT state) { /************************************** * @@ -2776,63 +2775,6 @@ return_result: } -static void adjust_text_descriptor(thread_db* tdbb, dsc* desc) -{ -/************************************** - * - * a d j u s t _ t e x t _ d e s c r i p t o r - * - ************************************** - * - * Functional description - * This function receives a text descriptor with - * dsc_length = numberOfCharacters * maxBytesPerChar - * and change dsc_length to number of bytes used by the string. - * - **************************************/ - if (desc->dsc_dtype == dtype_text) - { - SET_TDBB(tdbb); - - USHORT ttype = INTL_TTYPE(desc); - - CharSet* charSet = INTL_charset_lookup(tdbb, ttype); - - if (charSet->isMultiByte()) - { - Firebird::HalfStaticArray buffer; - - if (charSet->getFlags() & CHARSET_LEGACY_SEMANTICS) - { - desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address, TEXT_LEN(desc), - buffer.getBuffer(TEXT_LEN(desc) * charSet->maxBytesPerChar()), 0, - TEXT_LEN(desc)); - - const ULONG maxLength = TEXT_LEN(desc) / charSet->maxBytesPerChar(); - ULONG charLength = charSet->length(desc->dsc_length, desc->dsc_address, true); - - while (charLength > maxLength) - { - if (desc->dsc_address[desc->dsc_length - 1] == *charSet->getSpace()) - { - --desc->dsc_length; - --charLength; - } - else - break; - } - } - else - { - desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address, - TEXT_LEN(desc), buffer.getBuffer(TEXT_LEN(desc)), 0, - TEXT_LEN(desc) / charSet->maxBytesPerChar()); - } - } - } -} - - static dsc* binary_value(thread_db* tdbb, const jrd_nod* node, impure_value* impure) { /************************************** @@ -2990,7 +2932,7 @@ static dsc* cast(thread_db* tdbb, dsc* value, const jrd_nod* node, impure_value* MOV_move(tdbb, value, &impure->vlu_desc); if (impure->vlu_desc.dsc_dtype == dtype_text) - adjust_text_descriptor(tdbb, &impure->vlu_desc); + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); return &impure->vlu_desc; } diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index dd0ad9f78b..2d2736ad35 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -108,6 +108,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "../dsql/Nodes.h" +#include "../jrd/ValuesImpl.h" using namespace Jrd; @@ -199,7 +200,7 @@ static void execute_looper(thread_db*, jrd_req*, jrd_tra*, jrd_req::req_s); static void exec_sql(thread_db*, jrd_req*, DSC *); static void execute_procedure(thread_db*, jrd_nod*); static jrd_nod* execute_statement(thread_db*, jrd_req*, jrd_nod*); -static jrd_req* execute_triggers(thread_db*, trig_vec**, record_param*, record_param*, +static void execute_triggers(thread_db*, trig_vec**, record_param*, record_param*, jrd_req::req_ta, SSHORT); static void get_string(thread_db*, jrd_req*, jrd_nod*, Firebird::string&); static void looper_seh(thread_db*, jrd_req*); @@ -569,8 +570,6 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, jrd_req::req return; } - jrd_req* trigger = NULL; - if (tdbb->getDatabase()->dbb_triggers[type]) { jrd_tra* old_transaction = tdbb->getTransaction(); @@ -578,7 +577,7 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, jrd_req::req try { - trigger = execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type], + execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type], NULL, NULL, trigger_action, ALL_TRIGS); tdbb->setTransaction(old_transaction); } @@ -588,9 +587,46 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, jrd_req::req throw; } } +} - if (trigger) - trigger_failure(tdbb, trigger); + +// Execute DDL triggers. +void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTriggers, int action) +{ + // Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run. + + if (tdbb->getDatabase()->dbb_ddl_triggers) + { + jrd_tra* old_transaction = tdbb->getTransaction(); + tdbb->setTransaction(transaction); + + try + { + trig_vec triggers; + trig_vec* triggersPtr = &triggers; + + for (trig_vec::iterator i = tdbb->getDatabase()->dbb_ddl_triggers->begin(); + i != tdbb->getDatabase()->dbb_ddl_triggers->end(); + ++i) + { + if ((i->type & (1LL << action)) && + ((preTriggers && (i->type & 0x1) == 0) || (!preTriggers && (i->type & 0x1) == 0x1))) + { + triggers.add() = *i; + } + } + + execute_triggers(tdbb, &triggersPtr, NULL, NULL, + jrd_req::req_trigger_ddl, ALL_TRIGS); + + tdbb->setTransaction(old_transaction); + } + catch (...) + { + tdbb->setTransaction(old_transaction); + throw; + } + } } @@ -611,7 +647,7 @@ jrd_req* EXE_find_request(thread_db* tdbb, jrd_req* request, bool validate) SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); /* I found a core file from my test runs that came from a NULL request - * but have no idea what test was running. Let's bugcheck so we can @@ -722,16 +758,25 @@ void EXE_receive(thread_db* tdbb, try { - if (request->req_message->nod_type == nod_stall -#ifdef SCROLLABLE_CURSORS - || request->req_flags & req_fetch_required -#endif - ) - execute_looper(tdbb, request, transaction, jrd_req::req_sync); + const bool external = request->req_procedure && request->req_procedure->prc_external; - if (!(request->req_flags & req_active) || request->req_operation != jrd_req::req_send) + if (external) + execute_looper(tdbb, request, transaction, jrd_req::req_sync); + else { - ERR_post(Arg::Gds(isc_req_sync)); + if (request->req_message->nod_type == nod_stall +#ifdef SCROLLABLE_CURSORS + || request->req_flags & req_fetch_required +#endif + ) + { + execute_looper(tdbb, request, transaction, jrd_req::req_sync); + } + + if (!(request->req_flags & req_active) || request->req_operation != jrd_req::req_send) + { + ERR_post(Arg::Gds(isc_req_sync)); + } } const jrd_nod* message = request->req_message; @@ -740,9 +785,8 @@ void EXE_receive(thread_db* tdbb, if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number]) ERR_post(Arg::Gds(isc_req_sync)); - if (length != format->fmt_length) { + if (length != format->fmt_length) ERR_post(Arg::Gds(isc_port_len) << Arg::Num(length) << Arg::Num(format->fmt_length)); - } memcpy(buffer, (SCHAR*) request + message->nod_impure, length); @@ -756,8 +800,7 @@ void EXE_receive(thread_db* tdbb, if (desc->isBlob()) { - const bid* id = (bid*) - ((UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address); + const bid* id = (bid*) (buffer + (ULONG)(IPTR)desc->dsc_address); if (transaction->tra_blobs->locate(id->bid_temp_id())) { @@ -774,7 +817,8 @@ void EXE_receive(thread_db* tdbb, } } - execute_looper(tdbb, request, transaction, jrd_req::req_proceed); + if (!external) + execute_looper(tdbb, request, transaction, jrd_req::req_proceed); } //try catch (const Firebird::Exception&) @@ -898,27 +942,37 @@ void EXE_send(thread_db* tdbb, #endif jrd_tra* transaction = request->req_transaction; + const bool external = request->req_procedure && request->req_procedure->prc_external; - switch (node->nod_type) + if (external) { - case nod_message: - message = node; - break; - case nod_select: + fb_assert(request->req_top_node->nod_type == nod_list); + message = request->req_top_node->nod_arg[e_extproc_input_message]; // input message + fb_assert(message->nod_type == nod_message); + } + else + { + switch (node->nod_type) { - jrd_nod** ptr = node->nod_arg; - for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++) + case nod_message: + message = node; + break; + case nod_select: { - message = (*ptr)->nod_arg[e_send_message]; - if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg) { - request->req_next = *ptr; - break; + jrd_nod** ptr = node->nod_arg; + for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++) + { + message = (*ptr)->nod_arg[e_send_message]; + if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg) { + request->req_next = *ptr; + break; + } } } + break; + default: + BUGCHECK(167); /* msg 167 invalid SEND request */ } - break; - default: - BUGCHECK(167); /* msg 167 invalid SEND request */ } const Format* format = (Format*) message->nod_arg[e_msg_format]; @@ -975,7 +1029,8 @@ void EXE_send(thread_db* tdbb, } } - execute_looper(tdbb, request, transaction, jrd_req::req_proceed); + if (!external) + execute_looper(tdbb, request, transaction, jrd_req::req_proceed); #ifdef SCROLLABLE_CURSORS if (save_next) { @@ -1148,6 +1203,9 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) release_blobs(tdbb, request); } + delete request->resultSet; + request->resultSet = NULL; + if (request->req_proc_sav_point && (request->req_flags & req_proc_fetch)) release_proc_save_points(request); @@ -1236,19 +1294,28 @@ inline void PreModifyEraseTriggers(thread_db* tdbb, * storing active rpb in chain. * ******************************************************/ - if (! tdbb->getTransaction()->tra_rpblist) { + if (!tdbb->getTransaction()->tra_rpblist) + { tdbb->getTransaction()->tra_rpblist = FB_NEW(*tdbb->getTransaction()->tra_pool) traRpbList(*tdbb->getTransaction()->tra_pool); } + const int rpblevel = tdbb->getTransaction()->tra_rpblist->PushRpb(rpb); - jrd_req* trigger = NULL; - if ((*trigs) && (which_trig != POST_TRIG)) { - trigger = execute_triggers(tdbb, trigs, rpb, rec, op, PRE_TRIG); + + if (*trigs && which_trig != POST_TRIG) + { + try + { + execute_triggers(tdbb, trigs, rpb, rec, op, PRE_TRIG); + } + catch (const Exception&) + { + tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel); + throw; + } } + tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel); - if (trigger) { - trigger_failure(tdbb, trigger); - } } static jrd_nod* erase(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) @@ -1329,12 +1396,10 @@ static jrd_nod* erase(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) } /* Handle post operation trigger */ - jrd_req* trigger; - if (relation->rel_post_erase && which_trig != PRE_TRIG && - (trigger = execute_triggers(tdbb, &relation->rel_post_erase, - rpb, NULL, jrd_req::req_trigger_delete, POST_TRIG))) + if (relation->rel_post_erase && which_trig != PRE_TRIG) { - trigger_failure(tdbb, trigger); + execute_triggers(tdbb, &relation->rel_post_erase, rpb, NULL, jrd_req::req_trigger_delete, + POST_TRIG); } /* call IDX_erase (which checks constraints) after all post erase triggers @@ -1476,6 +1541,8 @@ static void execute_procedure(thread_db* tdbb, jrd_nod* node) } } + jrd_prc* procedure = (jrd_prc*) node->nod_arg[e_esp_procedure]; + USHORT in_msg_length = 0; UCHAR* in_msg = NULL; jrd_nod* in_message = node->nod_arg[e_esp_in_msg]; @@ -1485,16 +1552,16 @@ static void execute_procedure(thread_db* tdbb, jrd_nod* node) in_msg = (UCHAR*) request + in_message->nod_impure; } + const Format* format = NULL; USHORT out_msg_length = 0; UCHAR* out_msg = NULL; jrd_nod* out_message = node->nod_arg[e_esp_out_msg]; if (out_message) { - const Format* format = (Format*) out_message->nod_arg[e_msg_format]; + format = (Format*) out_message->nod_arg[e_msg_format]; out_msg_length = format->fmt_length; out_msg = (UCHAR*) request + out_message->nod_impure; } - jrd_prc* procedure = (jrd_prc*) node->nod_arg[e_esp_procedure]; jrd_req* proc_request = EXE_find_request(tdbb, procedure->prc_request, false); // trace procedure execution start @@ -1503,13 +1570,12 @@ static void execute_procedure(thread_db* tdbb, jrd_nod* node) Firebird::Array temp_buffer; if (!out_message) { - const Format* format = (Format*) procedure->prc_output_msg->nod_arg[e_msg_format]; + format = (Format*) procedure->prc_output_msg->nod_arg[e_msg_format]; out_msg_length = format->fmt_length; out_msg = temp_buffer.getBuffer(out_msg_length + FB_DOUBLE_ALIGN - 1); out_msg = (UCHAR*) FB_ALIGN((U_IPTR) out_msg, FB_DOUBLE_ALIGN); } - /* Catch errors so we can unwind cleanly */ try { @@ -1521,12 +1587,15 @@ static void execute_procedure(thread_db* tdbb, jrd_nod* node) transaction->tra_save_point->sav_number : 0; proc_request->req_timestamp = request->req_timestamp; + EXE_start(tdbb, proc_request, transaction); if (in_message) { - EXE_send(tdbb, proc_request, 0, in_msg_length, in_msg); + EXE_send(tdbb, proc_request, 0, in_msg_length, + reinterpret_cast(in_msg)); } - EXE_receive(tdbb, proc_request, 1, out_msg_length, out_msg); + EXE_receive(tdbb, proc_request, 1, out_msg_length, + reinterpret_cast(out_msg)); /* Clean up all savepoints started during execution of the procedure */ @@ -1656,15 +1725,27 @@ static jrd_nod* execute_statement(thread_db* tdbb, jrd_req* request, jrd_nod* no } } - if (stmt) { - stmt->close(tdbb); + if (request->req_operation == jrd_req::req_unwind) + { + jrd_nod* parent = node->nod_parent; + if (parent && parent->nod_type == nod_label && + (request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) && + (request->req_flags & req_continue_loop)) + { + request->req_flags &= ~req_continue_loop; + request->req_operation = jrd_req::req_sync; + return node; + } } + if (stmt) + stmt->close(tdbb); + return node->nod_parent; } -static jrd_req* execute_triggers(thread_db* tdbb, +static void execute_triggers(thread_db* tdbb, trig_vec** triggers, record_param* old_rpb, record_param* new_rpb, @@ -1681,16 +1762,14 @@ static jrd_req* execute_triggers(thread_db* tdbb, * if any blow up. * **************************************/ - if (!*triggers) { - return NULL; - } + if (!*triggers) + return; SET_TDBB(tdbb); jrd_tra* transaction = (tdbb->getRequest() ? tdbb->getRequest()->req_transaction : tdbb->getTransaction()); trig_vec* vector = *triggers; - jrd_req* result = NULL; Record* const old_rec = old_rpb ? old_rpb->rpb_record : NULL; Record* const new_rec = new_rpb ? new_rpb->rpb_record : NULL; @@ -1715,7 +1794,6 @@ static jrd_req* execute_triggers(thread_db* tdbb, memset(null_rec->rec_data, 0xFF, n); } - jrd_req* trigger = NULL; const Firebird::TimeStamp timestamp(Firebird::TimeStamp::getCurrentTimeStamp()); try @@ -1723,61 +1801,69 @@ static jrd_req* execute_triggers(thread_db* tdbb, for (trig_vec::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr) { ptr->compile(tdbb); - trigger = EXE_find_request(tdbb, ptr->request, false); - trigger->req_rpb[0].rpb_record = old_rec ? old_rec : null_rec; - trigger->req_rpb[1].rpb_record = new_rec ? new_rec : null_rec; - if (old_rec && trigger_action != jrd_req::req_trigger_insert) + if (ptr->extTrigger) { - trigger->req_rpb[0].rpb_number = old_rpb->rpb_number; - trigger->req_rpb[0].rpb_number.setValid(true); + //// TODO: trace stuff + + ptr->extTrigger->execute(tdbb, (Firebird::ExternalTrigger::Action) trigger_action, + old_rpb, new_rpb); } else - trigger->req_rpb[0].rpb_number.setValid(false); - - if (new_rec && !(which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_insert)) { - if (which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_update) - new_rpb->rpb_number = old_rpb->rpb_number; + jrd_req* trigger = EXE_find_request(tdbb, ptr->request, false); + trigger->req_rpb[0].rpb_record = old_rec ? old_rec : null_rec; + trigger->req_rpb[1].rpb_record = new_rec ? new_rec : null_rec; - trigger->req_rpb[1].rpb_number = new_rpb->rpb_number; - trigger->req_rpb[1].rpb_number.setValid(true); + if (old_rec && trigger_action != jrd_req::req_trigger_insert) + { + trigger->req_rpb[0].rpb_number = old_rpb->rpb_number; + trigger->req_rpb[0].rpb_number.setValid(true); + } + else + trigger->req_rpb[0].rpb_number.setValid(false); + + if (new_rec && !(which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_insert)) + { + if (which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_update) + new_rpb->rpb_number = old_rpb->rpb_number; + + trigger->req_rpb[1].rpb_number = new_rpb->rpb_number; + trigger->req_rpb[1].rpb_number.setValid(true); + } + else + trigger->req_rpb[1].rpb_number.setValid(false); + + if (tdbb->getRequest()) + trigger->req_timestamp = tdbb->getRequest()->req_timestamp; + else + trigger->req_timestamp = timestamp; + + trigger->req_trigger_action = trigger_action; + + TraceTrigExecute trace(tdbb, trigger, which_trig); + + EXE_start(tdbb, trigger, transaction); + + trigger->req_attachment = NULL; + trigger->req_flags &= ~req_in_use; + trigger->req_timestamp.invalidate(); + + const bool ok = (trigger->req_operation != jrd_req::req_unwind); + trace.finish(ok ? res_successful : res_failed); + + if (!ok) + { + trigger_failure(tdbb, trigger); + break; + } } - else - trigger->req_rpb[1].rpb_number.setValid(false); - - if (tdbb->getRequest()) - trigger->req_timestamp = tdbb->getRequest()->req_timestamp; - else - trigger->req_timestamp = timestamp; - - trigger->req_trigger_action = trigger_action; - - TraceTrigExecute trace(tdbb, trigger, which_trig); - - EXE_start(tdbb, trigger, transaction); - - trigger->req_attachment = NULL; - trigger->req_flags &= ~req_in_use; - trigger->req_timestamp.invalidate(); - - const bool ok = (trigger->req_operation != jrd_req::req_unwind); - trace.finish(ok ? res_successful : res_failed); - - if (!ok) - { - result = trigger; - break; - } - trigger = NULL; } delete null_rec; if (vector != *triggers) { MET_release_triggers(tdbb, &vector); } - - return result; } catch (const Firebird::Exception& ex) { @@ -1785,11 +1871,8 @@ static jrd_req* execute_triggers(thread_db* tdbb, if (vector != *triggers) { MET_release_triggers(tdbb, &vector); } - if (!trigger) { - throw; // trigger probally fails to compile - } - Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); - return trigger; + + throw; } } @@ -1823,7 +1906,7 @@ static void stuff_stack_trace(const jrd_req* request) } else if (req->req_procedure) { name = "At procedure '"; - name += req->req_procedure->prc_name.c_str(); + name += req->req_procedure->prc_name.toString().c_str(); } if (! name.isEmpty()) @@ -1917,9 +2000,111 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) // Execute stuff until we drop - while (node && !(request->req_flags & req_stall)) + bool runExternal = request->req_procedure && request->req_procedure->prc_external; + + while (runExternal || (node && !(request->req_flags & req_stall))) { try { + if (request->req_procedure && request->req_procedure->prc_external) + { + if (runExternal) + runExternal = false; + else + break; + + switch (request->req_operation) + { + case jrd_req::req_evaluate: + request->req_message = request->req_top_node->nod_arg[0]; // input message + request->req_flags |= req_stall; + request->req_operation = jrd_req::req_receive; + break; + + case jrd_req::req_sync: + { + fb_assert(request->req_top_node->nod_type == nod_list); + + jrd_nod* outMsgNode = request->req_top_node->nod_arg[e_extproc_output_message]; + fb_assert(outMsgNode->nod_type == nod_message); + + const Format* outFormat = (Format*) outMsgNode->nod_arg[e_msg_format]; + UCHAR* outMsg = (UCHAR*) request + outMsgNode->nod_impure; + + if (!request->resultSet) + { + // input message + jrd_nod* inMsgNode = request->req_top_node->nod_arg[e_extproc_input_message]; + fb_assert(inMsgNode->nod_type == nod_message); + fb_assert(request->req_message == inMsgNode); + + jrd_nod* list = request->req_top_node->nod_arg[e_extproc_input_assign]; + fb_assert(list->nod_type == nod_asn_list || + (list->nod_type == nod_list && list->nod_count == 0)); + + const Format* format = (Format*) inMsgNode->nod_arg[e_msg_format]; + + // clear the flags from the input message + USHORT* impure_flags = (USHORT*) ((UCHAR *) request + + (IPTR) request->req_message->nod_arg[e_msg_impure_flags]); + memset(impure_flags, 0, sizeof(USHORT) * format->fmt_count); + + // clear the flags from the output message + impure_flags = (USHORT*) ((UCHAR *) request + + (IPTR) outMsgNode->nod_arg[e_msg_impure_flags]); + memset(impure_flags, 0, sizeof(USHORT) * outFormat->fmt_count); + + // validate input parameters + for (unsigned i = 0; i < list->nod_count; ++i) + EXE_assignment(tdbb, list->nod_arg[i]); + + if (!request->inputParams) + { + UCHAR* inMsg = (UCHAR*) request + request->req_message->nod_impure; + + request->inputParams = FB_NEW(*getDefaultMemoryPool()) ValuesImpl(tdbb, + format, inMsg, *request->req_procedure->prc_input_fields); + } + + if (!request->outputParams) + { + request->outputParams = FB_NEW(*getDefaultMemoryPool()) ValuesImpl(tdbb, + outFormat, outMsg, *request->req_procedure->prc_output_fields); + request->outputParams->setNull(); + } + + request->resultSet = request->req_procedure->prc_external->open(tdbb, + request->inputParams, request->outputParams); + } + + request->req_message = outMsgNode; + bool result = request->resultSet->fetch(tdbb); + + // end flag + const dsc& eofDesc = outFormat->fmt_desc[outFormat->fmt_count - 1]; + fb_assert(eofDesc.dsc_dtype == dtype_short); + *((SSHORT*) (UCHAR*) (outMsg + (IPTR) eofDesc.dsc_address)) = (SSHORT) result; + + jrd_nod* list = request->req_top_node->nod_arg[e_extproc_output_assign]; + fb_assert(list->nod_type == nod_asn_list || + (list->nod_type == nod_list && list->nod_count == 0)); + + if (result) + { + // validate output parameters + for (unsigned i = 0; i < list->nod_count; ++i) + EXE_assignment(tdbb, list->nod_arg[i]); + } + + break; + } + + default: + fb_assert(false); + break; + } + + goto end; + } if (request->req_operation == jrd_req::req_evaluate && (--tdbb->tdbb_quantum < 0)) { @@ -2029,6 +2214,21 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) break; } request->req_operation = jrd_req::req_return; + + case jrd_req::req_unwind: + { + jrd_nod* parent = node->nod_parent; + if (parent && parent->nod_type == nod_label && + (request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) && + (request->req_flags & req_continue_loop)) + { + request->req_flags &= ~req_continue_loop; + request->req_operation = jrd_req::req_sync; + break; + } + // fall into + } + default: RSE_close(tdbb, (RecordSource*) node->nod_arg[e_for_rsb]); node = node->nod_parent; @@ -2320,15 +2520,16 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) case jrd_req::req_unwind: { - if (request->req_flags & req_leave) + if (request->req_flags & (req_leave | req_continue_loop)) { // Although the req_operation is set to req_unwind, - // it's not an error case if req_leave bit is set. - // req_leave bit indicates that we hit an EXIT or - // BREAK/LEAVE statement in the SP/trigger code. + // it's not an error case if req_leave/req_continue_loop bit is set. + // req_leave/req_continue_loop bit indicates that we hit an EXIT or + // BREAK/LEAVE/CONTINUE statement in the SP/trigger code. // Do not perform the error handling stuff. - if (transaction != dbb->dbb_sys_trans) { + if (transaction != dbb->dbb_sys_trans) + { memcpy(&count, (SCHAR*) request + node->nod_impure, sizeof(SLONG)); for (const Savepoint* save_point = transaction->tra_save_point; @@ -2491,12 +2692,14 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) break; case jrd_req::req_unwind: + fb_assert(!(request->req_flags & req_continue_loop)); if ((request->req_label == (USHORT)(IPTR) node->nod_arg[e_lbl_label]) && (request->req_flags & (req_leave | req_error_handler))) { request->req_flags &= ~req_leave; request->req_operation = jrd_req::req_return; } + // fall into default: node = node->nod_parent; @@ -2510,6 +2713,13 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) node = node->nod_parent; break; + case nod_continue_loop: + request->req_flags |= req_continue_loop; + request->req_operation = jrd_req::req_unwind; + request->req_label = (USHORT)(IPTR) node->nod_arg[0]; + node = node->nod_parent; + break; + case nod_list: { impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure); @@ -2540,6 +2750,21 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) request->req_operation = jrd_req::req_evaluate; break; + case jrd_req::req_unwind: + { + jrd_nod* parent = node->nod_parent; + if (parent && parent->nod_type == nod_label && + (request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) && + (request->req_flags & req_continue_loop)) + { + request->req_flags &= ~req_continue_loop; + request->req_operation = jrd_req::req_evaluate; + node = node->nod_arg[0]; + break; + } + // fall into + } + default: node = node->nod_parent; } @@ -2734,10 +2959,23 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) #endif case nod_set_generator: - case nod_set_generator2: - if (request->req_operation == jrd_req::req_evaluate) { + case nod_set_generator2: + if (request->req_operation == jrd_req::req_evaluate) + { + SSHORT genId = (SSHORT)(IPTR) node->nod_arg[e_gen_id]; + + MetaName genName; + MET_lookup_generator_id(tdbb, genId, genName); + + DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_BEFORE, + DDL_TRIGGER_ALTER_SEQUENCE, genName, *request->req_sql_text); + dsc* desc = EVL_expr(tdbb, node->nod_arg[e_gen_value]); - DPM_gen_id(tdbb, (IPTR) node->nod_arg[e_gen_id], true, MOV_get_int64(desc, 0)); + DPM_gen_id(tdbb, genId, true, MOV_get_int64(desc, 0)); + + DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_AFTER, + DDL_TRIGGER_ALTER_SEQUENCE, genName, *request->req_sql_text); + request->req_operation = jrd_req::req_return; } node = node->nod_parent; @@ -2881,6 +3119,7 @@ jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node) release_blobs(tdbb, request); } +end: request->req_next = node; tdbb->setTransaction(old_transaction); tdbb->setRequest(old_request); @@ -3043,12 +3282,10 @@ static jrd_nod* modify(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) new_rpb->rpb_number = org_rpb->rpb_number; new_rpb->rpb_number.setValid(true); - jrd_req* trigger; - if (relation->rel_post_modify && which_trig != PRE_TRIG && - (trigger = execute_triggers(tdbb, &relation->rel_post_modify, - org_rpb, new_rpb, jrd_req::req_trigger_update, POST_TRIG))) + if (relation->rel_post_modify && which_trig != PRE_TRIG) { - trigger_failure(tdbb, trigger); + execute_triggers(tdbb, &relation->rel_post_modify, org_rpb, new_rpb, + jrd_req::req_trigger_update, POST_TRIG); } /* now call IDX_modify_check_constrints after all post modify triggers @@ -3639,8 +3876,7 @@ static void set_error(thread_db* tdbb, const xcp_repeat* exception, jrd_nod* msg DSC* desc = EVL_expr(tdbb, msg_node); if (desc && !(request->req_flags & req_null)) { - length = MOV_make_string(desc, tdbb->getAttachment()->att_charset, &string, - &temp, sizeof(temp)); + length = MOV_make_string(desc, tdbb->getCharSet(), &string, &temp, sizeof(temp)); length = MIN(length, sizeof(message) - 1); /* dimitr: or should we throw an error here, i.e. @@ -3761,8 +3997,6 @@ static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) * Execute a STORE statement. * **************************************/ - jrd_req* trigger; - SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); BLKCHK(node, type_nod); @@ -3793,11 +4027,10 @@ static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) if (transaction != dbb->dbb_sys_trans) ++transaction->tra_save_point->sav_verb_count; - if (relation->rel_pre_store && (which_trig != POST_TRIG) && - (trigger = execute_triggers(tdbb, &relation->rel_pre_store, - NULL, rpb, jrd_req::req_trigger_insert, PRE_TRIG))) + if (relation->rel_pre_store && which_trig != POST_TRIG) { - trigger_failure(tdbb, trigger); + execute_triggers(tdbb, &relation->rel_pre_store, NULL, rpb, + jrd_req::req_trigger_insert, PRE_TRIG); } if (node->nod_arg[e_sto_validate]) { @@ -3834,11 +4067,10 @@ static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig) rpb->rpb_number.setValid(true); - if (relation->rel_post_store && (which_trig != PRE_TRIG) && - (trigger = execute_triggers(tdbb, &relation->rel_post_store, - NULL, rpb, jrd_req::req_trigger_insert, POST_TRIG))) + if (relation->rel_post_store && which_trig != PRE_TRIG) { - trigger_failure(tdbb, trigger); + execute_triggers(tdbb, &relation->rel_post_store, NULL, rpb, + jrd_req::req_trigger_insert, POST_TRIG); } /* CVC: Increment the counter only if we called VIO/EXT_store() and diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 1ad1e04e8e..515048ec72 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -124,6 +124,7 @@ const int nod_agg_dbkey = 64; // dbkey of an aggregate const int nod_invariant = 128; // node is recognized as being invariant const int nod_recurse = 256; // union node is a recursive union const int nod_unique_sort = 512; // sorts using unique key - for distinct and group by +const int nod_window = 1024; // aggregate for window function // Special RecordSelExpr node @@ -571,6 +572,12 @@ const int e_derived_expr_stream_list = 2; const int e_derived_expr_count = 1; const int e_derived_expr_length = 3; +// index (in nod_list) for external procedure blr +const int e_extproc_input_message = 0; +const int e_extproc_output_message = 1; +const int e_extproc_input_assign = 2; +const int e_extproc_output_assign = 3; + // Request resources struct Resource diff --git a/src/jrd/exe_proto.h b/src/jrd/exe_proto.h index 60205e04cf..cf12c9f296 100644 --- a/src/jrd/exe_proto.h +++ b/src/jrd/exe_proto.h @@ -24,6 +24,8 @@ #ifndef JRD_EXE_PROTO_H #define JRD_EXE_PROTO_H +#include "../jrd/cmp_proto.h" + namespace Jrd { class jrd_req; class jrd_nod; @@ -34,6 +36,8 @@ void EXE_assignment(Jrd::thread_db*, Jrd::jrd_nod*); void EXE_assignment(Jrd::thread_db* tdbb, Jrd::jrd_nod* to, dsc* from_desc, bool from_null, Jrd::jrd_nod* missing_node, Jrd::jrd_nod* missing2_node); void EXE_execute_db_triggers(Jrd::thread_db*, Jrd::jrd_tra*, enum Jrd::jrd_req::req_ta); +void EXE_execute_ddl_triggers(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, + bool preTriggers, int action); Jrd::jrd_nod* EXE_looper(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_nod* in_node); Jrd::jrd_req* EXE_find_request(Jrd::thread_db*, Jrd::jrd_req*, bool); void EXE_receive(Jrd::thread_db*, Jrd::jrd_req*, USHORT, USHORT, UCHAR*, bool = false); @@ -44,5 +48,84 @@ void EXE_unwind(Jrd::thread_db*, Jrd::jrd_req*); void EXE_seek(Jrd::thread_db*, Jrd::jrd_req*, USHORT, ULONG); #endif +namespace Jrd +{ + // ASF: To make this class MT-safe in SS for v3, it should be AutoCacheRequest::release job to + // inform CMP that the request is available for subsequent usage. + class AutoCacheRequest + { + public: + AutoCacheRequest(thread_db* tdbb, USHORT aId, USHORT aWhich) + : id(aId), + which(aWhich), + request(CMP_find_request(tdbb, id, which)) + { + } + + ~AutoCacheRequest() + { + release(); + } + + public: + void reset(thread_db* tdbb, USHORT aId, USHORT aWhich) + { + release(); + + id = aId; + which = aWhich; + request = CMP_find_request(tdbb, id, which); + } + + public: + AutoCacheRequest& operator =(jrd_req* newRequest) + { + fb_assert(!request); + request = newRequest; + cacheRequest(); + return *this; + } + + operator jrd_req*() + { + return request; + } + + bool operator !() const + { + return !request; + } + + private: + inline void release() + { + if (request) + { + EXE_unwind(JRD_get_thread_data(), request); + request = NULL; + } + } + + inline void cacheRequest() + { + Database* dbb = JRD_get_thread_data()->getDatabase(); + + if (which == IRQ_REQUESTS) + REQUEST(id) = request; + else if (which == DYN_REQUESTS) + DYN_REQUEST(id) = request; + else + { + fb_assert(false); + } + } + + private: + USHORT id; + USHORT which; + jrd_req* request; + }; +} + #endif // JRD_EXE_PROTO_H diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 315240cef4..f269ec6052 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -63,7 +63,7 @@ static RegisterInternalProvider reg; // InternalProvider -void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Attachment* att) +void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Jrd::Attachment* att) { if (m_connections.getCount() == 0) return; @@ -119,7 +119,7 @@ void InternalConnection::attach(thread_db* tdbb, const Firebird::string& dbName, Database* dbb = tdbb->getDatabase(); fb_assert(dbName.isEmpty() || dbName == dbb->dbb_database_name.c_str()); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if ((user.isEmpty() || user == attachment->att_user->usr_user_name) && (role.isEmpty() || role == attachment->att_user->usr_sql_role_name)) { @@ -136,7 +136,7 @@ void InternalConnection::attach(thread_db* tdbb, const Firebird::string& dbName, { EngineCallbackGuard guard(tdbb, *this); - jrd8_attach_database(status, m_dbName.c_str(), &m_attachment, + jrd8_attach_database(status, 0, m_dbName.c_str(), &m_attachment, m_dpb.getBufferLength(), m_dpb.getBuffer()); } if (status[1]) { @@ -161,7 +161,7 @@ void InternalConnection::doDetach(thread_db* tdbb) ISC_STATUS_ARRAY status = {0}; { // scope - Attachment* att = m_attachment; + Jrd::Attachment* att = m_attachment; m_attachment = NULL; EngineCallbackGuard guard(tdbb, *this); @@ -241,10 +241,10 @@ void InternalTransaction::doStart(ISC_STATUS* status, thread_db* tdbb, ClumpletW } else { - Attachment* att = m_IntConnection.getJrdAtt(); + Jrd::Attachment* att = m_IntConnection.getJrdAtt(); EngineCallbackGuard guard(tdbb, *this); - jrd8_start_transaction(status, &m_transaction, 1, &att, + jrd8_start_transaction(status, 0, &m_transaction, 1, &att, //// FIXME: public_handle tpb.getBufferLength(), tpb.getBuffer()); } } @@ -322,7 +322,7 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) m_inBlr.clear(); m_outBlr.clear(); - Attachment* att = m_intConnection.getJrdAtt(); + Jrd::Attachment* att = m_intConnection.getJrdAtt(); jrd_tra* tran = getIntTransaction()->getJrdTran(); ISC_STATUS_ARRAY status = {0}; @@ -340,13 +340,31 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql) { EngineCallbackGuard guard(tdbb, *this); - jrd_req* const save_caller = tran->tra_callback_caller; - tran->tra_callback_caller = m_callerPrivileges ? tdbb->getRequest() : NULL; + CallerName save_caller_name(tran->tra_caller_name); + + if (m_callerPrivileges) + { + jrd_req* request = tdbb->getRequest(); + CallerName callerName; + + if (request && request->req_trg_name.hasData()) + tran->tra_caller_name = CallerName(obj_trigger, request->req_trg_name); + else if (request && request->req_procedure && + request->req_procedure->prc_name.identifier.hasData()) + { + if (request->req_procedure->prc_name.qualifier.isEmpty()) + tran->tra_caller_name = CallerName(obj_procedure, request->req_procedure->prc_name.identifier); + else + tran->tra_caller_name = CallerName(obj_package_header, request->req_procedure->prc_name.qualifier); + } + else + tran->tra_caller_name = CallerName(); + } jrd8_prepare(status, &tran, &m_request, sql.length(), sql.c_str(), m_connection.getSqlDialect(), 0, NULL, 0, NULL); - tran->tra_callback_caller = save_caller; + tran->tra_caller_name = save_caller_name; } if (status[1]) { raise(status, tdbb, "jrd8_prepare", &sql); @@ -527,7 +545,7 @@ void InternalBlob::open(thread_db* tdbb, Transaction& tran, const dsc& desc, con fb_assert(!m_blob); fb_assert(sizeof(m_blob_id) == desc.dsc_length); - Attachment* att = m_connection.getJrdAtt(); + Jrd::Attachment* att = m_connection.getJrdAtt(); jrd_tra* transaction = ((InternalTransaction&) tran).getJrdTran(); memcpy(&m_blob_id, desc.dsc_address, sizeof(m_blob_id)); @@ -552,7 +570,7 @@ void InternalBlob::create(thread_db* tdbb, Transaction& tran, dsc& desc, const U fb_assert(!m_blob); fb_assert(sizeof(m_blob_id) == desc.dsc_length); - Attachment* att = m_connection.getJrdAtt(); + Jrd::Attachment* att = m_connection.getJrdAtt(); jrd_tra* transaction = ((InternalTransaction&) tran).getJrdTran(); m_blob_id.clear(); diff --git a/src/jrd/fields.h b/src/jrd/fields.h index d11bbd06f0..e16d82129a 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -64,13 +64,13 @@ FIELD(fld_trg_name , nam_trg_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) FIELD(fld_gnr_name , nam_gnr_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) FIELD(fld_fun_name , nam_fun_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) - FIELD(fld_ext_name , nam_ext_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , 0 , 0 , NULL) + FIELD(fld_ext_name , nam_ext_name , dtype_text , 255 , 0 , 0 , NULL) FIELD(fld_typ_name , nam_typ_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) FIELD(fld_dimensions , nam_dimensions , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) FIELD(fld_runtime , nam_runtime , dtype_blob , BLOB_SIZE , isc_blob_summary , 0 , NULL) FIELD(fld_trg_seq , nam_trg_seq , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) FIELD(fld_gnr_type , nam_gnr_type , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) - FIELD(fld_trg_type , nam_trg_type , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) + FIELD(fld_trg_type , nam_trg_type , dtype_int64 , sizeof(SINT64) , 0 , 0 , NULL) FIELD(fld_obj_type , nam_obj_type , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) FIELD(fld_mechanism , nam_mechanism , dtype_short , sizeof(SSHORT) , 0 , 0 , NULL) FIELD(fld_f_descr , nam_desc , dtype_blob , BLOB_SIZE , isc_blob_format , 0 , NULL) @@ -154,3 +154,7 @@ FIELD(fld_ctx_var_name , nam_ctx_var_name , dtype_varying , 80 , 0 , 0 , NULL) FIELD(fld_ctx_var_value , nam_ctx_var_value , dtype_varying , 255 , 0 , 0 , NULL) + + FIELD(fld_engine_name , nam_engine_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) + + FIELD(fld_pkg_name , nam_pkg_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata, 0 , NULL) diff --git a/src/jrd/flu.cpp b/src/jrd/flu.cpp index 721157e990..f773d3610d 100644 --- a/src/jrd/flu.cpp +++ b/src/jrd/flu.cpp @@ -167,12 +167,6 @@ namespace Jrd FPTR_INT Module::lookup(const char* module, const char* name, DatabaseModules& interest) { - FPTR_INT function = FUNCTIONS_entrypoint(module, name); - if (function) - { - return function; - } - // Try to find loadable module Module m = lookupModule(module, true); if (! m) @@ -196,12 +190,6 @@ namespace Jrd FPTR_INT Module::lookup(const TEXT* module, const TEXT* name) { - FPTR_INT function = FUNCTIONS_entrypoint(module, name); - if (function) - { - return function; - } - // Try to find loadable module Module m = lookupModule(module, false); if (! m) diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index 195b79494c..95e766128a 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -60,6 +60,7 @@ #include "../jrd/thread_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/blb.h" +#include "../jrd/ExtEngineManager.h" #include "../common/classes/auto.h" #include "../common/utils_proto.h" #include "../common/classes/FpeControl.h" @@ -185,7 +186,7 @@ bool IbUtil::free(void* ptr) return true; thread_db* tdbb = JRD_get_thread_data(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); size_t pos; if (attachment->att_udf_pointers.find(ptr, pos)) @@ -333,21 +334,12 @@ void FUN_evaluate(thread_db* tdbb, UserFunction* function, jrd_nod* node, impure } #endif - UDF_ARG args[MAX_UDF_ARGUMENTS + 1]; - HalfStaticArray temp; - SET_TDBB(tdbb); - // Start by constructing argument list - UCHAR* temp_ptr = temp.getBuffer(function->fun_temp_length + FB_DOUBLE_ALIGN); - MOVE_CLEAR(temp_ptr, temp.getCount()); - temp_ptr = (UCHAR*) FB_ALIGN((U_IPTR) temp_ptr, FB_DOUBLE_ALIGN); + fun_repeat* return_ptr = function->fun_rpt + function->fun_return_arg; + value->vlu_desc = return_ptr->fun_desc; + value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc; - MOVE_CLEAR(args, sizeof(args)); - UDF_ARG* arg_ptr = args; - //Stack blob_stack; - //blb* blob_created = 0; - OwnedBlobStack blob_stack(tdbb); UCharStack array_stack; jrd_req* request = tdbb->getRequest(); @@ -356,13 +348,7 @@ void FUN_evaluate(thread_db* tdbb, UserFunction* function, jrd_nod* node, impure // because EVL_expr() resets it every time it's called. Kept it for now. const bool null_flag = ((request->req_flags & req_null) == req_null); - fun_repeat* return_ptr = function->fun_rpt + function->fun_return_arg; - jrd_nod** ptr = node->nod_arg; - - value->vlu_desc = return_ptr->fun_desc; - value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc; - -// Trap any potential errors + // Trap any potential errors try { @@ -387,6 +373,28 @@ void FUN_evaluate(thread_db* tdbb, UserFunction* function, jrd_nod* node, impure value->vlu_desc.dsc_address = string->str_data; } + if (function->fun_external) + { + function->fun_external->execute(tdbb, node, value); + return; + } + + UDF_ARG args[MAX_UDF_ARGUMENTS + 1]; + HalfStaticArray temp; + + // Start by constructing argument list + UCHAR* temp_ptr = temp.getBuffer(function->fun_temp_length + FB_DOUBLE_ALIGN); + MOVE_CLEAR(temp_ptr, temp.getCount()); + temp_ptr = (UCHAR *) FB_ALIGN((U_IPTR) temp_ptr, FB_DOUBLE_ALIGN); + + MOVE_CLEAR(args, sizeof(args)); + UDF_ARG* arg_ptr = args; + //Stack blob_stack; + //blb* blob_created = 0; + OwnedBlobStack blob_stack(tdbb); + + jrd_nod** ptr = node->nod_arg; + // We'll use to this trick to give the UDF a way to signal // "I sent a null blob" when not using descriptors. udf_blob* return_blob_struct = 0; @@ -724,12 +732,14 @@ void FUN_evaluate(thread_db* tdbb, UserFunction* function, jrd_nod* node, impure if (isinf(value->vlu_misc.vlu_double)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_udf_fp_overflow) << Arg::Str(function->fun_name)); + Arg::Gds(isc_udf_fp_overflow) << + Arg::Str(function->fun_name.toString())); } else if (isnan(value->vlu_misc.vlu_double)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_udf_fp_nan) << Arg::Str(function->fun_name)); + Arg::Gds(isc_udf_fp_nan) << + Arg::Str(function->fun_name.toString())); } } request->req_flags &= ~req_null; @@ -758,7 +768,7 @@ void FUN_evaluate(thread_db* tdbb, UserFunction* function, jrd_nod* node, impure } -UserFunction* FUN_lookup_function(thread_db* tdbb, const MetaName& name) //, bool ShowAccessError) +UserFunction* FUN_lookup_function(thread_db* tdbb, const QualifiedName& name) { /************************************** * @@ -785,7 +795,8 @@ UserFunction* FUN_lookup_function(thread_db* tdbb, const MetaName& name) //, boo FOR(REQUEST_HANDLE request_fun) X IN RDB$FUNCTIONS - WITH X.RDB$FUNCTION_NAME EQ name.c_str() + WITH X.RDB$FUNCTION_NAME EQ name.identifier.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(name.qualifier.c_str(), '') if (!REQUEST(irq_l_functions)) REQUEST(irq_l_functions) = request_fun; @@ -798,7 +809,8 @@ UserFunction* FUN_lookup_function(thread_db* tdbb, const MetaName& name) //, boo FOR(REQUEST_HANDLE request_arg) Y IN RDB$FUNCTION_ARGUMENTS - WITH Y.RDB$FUNCTION_NAME EQ X.RDB$FUNCTION_NAME + WITH Y.RDB$FUNCTION_NAME EQ X.RDB$FUNCTION_NAME AND + Y.RDB$PACKAGE_NAME EQUIV NULLIF(name.qualifier.c_str(), '') SORTED BY Y.RDB$ARGUMENT_POSITION if (!REQUEST(irq_l_args)) @@ -827,7 +839,8 @@ UserFunction* FUN_lookup_function(thread_db* tdbb, const MetaName& name) //, boo END_FOR; function = FB_NEW_RPT(*dbb->dbb_permanent, count + 1) UserFunction(*dbb->dbb_permanent); - function->fun_name = X.RDB$FUNCTION_NAME; + function->fun_name = QualifiedName(X.RDB$FUNCTION_NAME, + (X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)); function->fun_count = count; function->fun_args = args; function->fun_return_arg = X.RDB$RETURN_ARGUMENT; @@ -838,11 +851,42 @@ UserFunction* FUN_lookup_function(thread_db* tdbb, const MetaName& name) //, boo // Prepare the exception message to be used in case this function ever // causes an exception. This is done at this time to save us from preparing // (thus allocating) this message every time the function is called. - function->fun_exception_message.printf(EXCEPTION_MESSAGE, name.c_str(), + function->fun_exception_message.printf(EXCEPTION_MESSAGE, name.toString().c_str(), X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME); - function->fun_entrypoint = - Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules); + if (!X.RDB$ENGINE_NAME.NULL) // ODS_12_0 + { + HalfStaticArray body; + + if (!X.RDB$FUNCTION_SOURCE.NULL) + { + blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &X.RDB$FUNCTION_SOURCE); + ULONG len = BLB_get_data(tdbb, blob, + (UCHAR*) body.getBuffer(blob->blb_length + 1), blob->blb_length + 1); + body.begin()[MIN(blob->blb_length, len)] = '\0'; + } + else + body.getBuffer(1)[0] = '\0'; + + function->fun_entrypoint = NULL; + function->fun_external = dbb->dbb_extManager.makeFunction( + tdbb, function, X.RDB$ENGINE_NAME, + (X.RDB$ENTRYPOINT.NULL ? "" : X.RDB$ENTRYPOINT), body.begin()); + } + else + { + function->fun_external = NULL; + function->fun_entrypoint = + Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules); + + // Could not find a function with given MODULE, ENTRYPOINT. + // Try the list of internally implemented functions. + if (!function->fun_entrypoint) + { + function->fun_entrypoint = + BUILTIN_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT); + } + } // Could not find a function with given MODULE, ENTRYPOINT. // Try the list of internally implemented functions. @@ -897,7 +941,7 @@ UserFunction* FUN_resolve(thread_db* tdbb, CompilerScratch* csb, UserFunction* f for (; function; function = function->fun_homonym) { - if (function->fun_entrypoint && function->fun_args == args->nod_count) + if ((function->fun_entrypoint || function->fun_external) && function->fun_args == args->nod_count) { int score = 0; jrd_nod** ptr; diff --git a/src/jrd/fun_proto.h b/src/jrd/fun_proto.h index 5f554171bc..0eb0e49f48 100644 --- a/src/jrd/fun_proto.h +++ b/src/jrd/fun_proto.h @@ -40,7 +40,7 @@ public: void FUN_evaluate(Jrd::thread_db*, Jrd::UserFunction*, Jrd::jrd_nod*, Jrd::impure_value*); -Jrd::UserFunction* FUN_lookup_function(Jrd::thread_db*, const Firebird::MetaName&); //, bool ShowAccessError); +Jrd::UserFunction* FUN_lookup_function(Jrd::thread_db*, const Firebird::QualifiedName&); Jrd::UserFunction* FUN_resolve(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::UserFunction*, Jrd::jrd_nod*); #endif // JRD_FUN_PROTO_H diff --git a/src/jrd/functions.cpp b/src/jrd/functions.cpp deleted file mode 100644 index 12e7ca1578..0000000000 --- a/src/jrd/functions.cpp +++ /dev/null @@ -1,473 +0,0 @@ -/* - * PROGRAM: InterBase Access Method - * MODULE: functions.cpp - * DESCRIPTION: External entrypoint definitions - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#include "firebird.h" -#include "../jrd/common.h" -#include -#include -#include "../jrd/jrd.h" // For MAXPATHLEN Bug #126614 -#include "../jrd/license.h" -#include "../jrd/tra.h" -#include "../jrd/dsc_proto.h" -#include "../jrd/fun_proto.h" -#include "../jrd/pag_proto.h" -#include "../jrd/thread_proto.h" -#include "../jrd/trace/TraceManager.h" -#include "../jrd/trace/TraceObjects.h" - -using namespace Jrd; -using namespace Firebird; - -struct FN -{ - const char* fn_module; - const char* fn_entrypoint; - FPTR_INT fn_function; -}; - - -static int test(const long*, char*); -static DSC* ni(DSC*, DSC*); -static SLONG* byteLen(const dsc*); -static SLONG set_context(const vary* ns_vary, const vary* name_vary, const vary* value_vary); -static vary* get_context(const vary* ns_vary, const vary* name_vary); - -// Context variable function names. Do not forget to change functions.h too if changing -static const char - RDB_GET_CONTEXT[] = "RDB$GET_CONTEXT", - RDB_SET_CONTEXT[] = "RDB$SET_CONTEXT"; - -// Namespace names -static const char - SYSTEM_NAMESPACE[] = "SYSTEM", - USER_SESSION_NAMESPACE[] = "USER_SESSION", - USER_TRANSACTION_NAMESPACE[] = "USER_TRANSACTION"; - -// System variables names -static const char - ENGINE_VERSION[] = "ENGINE_VERSION", - NETWORK_PROTOCOL_NAME[] = "NETWORK_PROTOCOL", - CLIENT_ADDRESS_NAME[] = "CLIENT_ADDRESS", - DATABASE_NAME[] = "DB_NAME", - ISOLATION_LEVEL_NAME[] = "ISOLATION_LEVEL", - TRANSACTION_ID_NAME[] = "TRANSACTION_ID", - SESSION_ID_NAME[] = "SESSION_ID", - CURRENT_USER_NAME[] = "CURRENT_USER", - CURRENT_ROLE_NAME[] = "CURRENT_ROLE"; - -// Isolation values modes -static const char - READ_COMMITTED_VALUE[] = "READ COMMITTED", - CONSISTENCY_VALUE[] = "CONSISTENCY", - SNAPSHOT_VALUE[] = "SNAPSHOT"; - - -#define FUNCTION(ROUTINE, FUNCTION_NAME, MODULE_NAME, ENTRYPOINT, RET_ARG) \ - {MODULE_NAME, ENTRYPOINT, (FPTR_INT) ROUTINE}, -#define END_FUNCTION -#define FUNCTION_ARGUMENT(MECHANISM, TYPE, SCALE, LENGTH, SUB_TYPE, CHARSET, PRECISION, CHAR_LENGTH) - -static const FN isc_functions[] = -{ -#ifndef DECLARE_EXAMPLE_IUDF_AUTOMATICALLY - {"test_module", "test_function", (FPTR_INT) test}, // see functions.h -#endif - {"test_module", "ni", (FPTR_INT) ni}, - {"test_module", "ns", (FPTR_INT) ni}, - {"test_module", "nn", (FPTR_INT) ni}, -#ifndef DECLARE_EXAMPLE_IUDF_AUTOMATICALLY - {"test_module", "byte_len", (FPTR_INT) byteLen}, // see functions.h -#endif - -#include "../jrd/functions.h" - - {0, 0, 0} -}; - - -FPTR_INT FUNCTIONS_entrypoint(const char* module, const char* entrypoint) -{ -/************************************** - * - * F U N C T I O N S _ e n t r y p o i n t - * - ************************************** - * - * Functional description - * Lookup function in hardcoded table. The module and - * entrypoint names are null terminated, but may contain - * insignificant trailing blanks. - * - **************************************/ - char temp[MAXPATHLEN + 128]; /* Bug #126614 Fix */ - - char* p = temp; - - while (*module && *module != ' ') - *p++ = *module++; - - *p++ = 0; - const char* const ep = p; - - while (*entrypoint && *entrypoint != ' ') - *p++ = *entrypoint++; - - *p = 0; - - for (const FN* function = isc_functions; function->fn_module; ++function) - { - if (!strcmp(temp, function->fn_module) && !strcmp(ep, function->fn_entrypoint)) - { - return function->fn_function; - } - } - - return 0; -} - -static vary* make_result_str(const char* str, size_t str_len) -{ - vary *result_vary = (vary*) IbUtil::alloc(str_len + 2); - result_vary->vary_length = str_len; - memcpy(result_vary->vary_string, str, result_vary->vary_length); - return result_vary; -} - -static vary* make_result_str(const Firebird::string& str) -{ - return make_result_str(str.c_str(), str.length()); -} - -vary* get_context(const vary* ns_vary, const vary* name_vary) -{ - thread_db* tdbb = JRD_get_thread_data(); - - Database* dbb; - Attachment* att; - jrd_tra* transaction; - - // See if JRD thread data structure looks sane for occasion - if (!tdbb || - !(dbb = tdbb->getDatabase()) || - !(transaction = tdbb->getTransaction()) || - !(att = tdbb->getAttachment())) - { - fb_assert(false); - return NULL; - } - - Database::SyncGuard dsGuard(dbb); - - // Complain if namespace or variable name is null - if (!ns_vary || !name_vary) { - ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_GET_CONTEXT)); - } - - const Firebird::string ns_str(ns_vary->vary_string, ns_vary->vary_length); - const Firebird::string name_str(name_vary->vary_string, name_vary->vary_length); - - // Handle system variables - if (ns_str == SYSTEM_NAMESPACE) - { - if (name_str == ENGINE_VERSION) - { - Firebird::string version; - version.printf("%s.%s.%s", FB_MAJOR_VER, FB_MINOR_VER, FB_REV_NO); - - return make_result_str(version); - } - - if (name_str == NETWORK_PROTOCOL_NAME) - { - if (att->att_network_protocol.isEmpty()) - return NULL; - - return make_result_str(att->att_network_protocol); - } - - if (name_str == CLIENT_ADDRESS_NAME) - { - if (att->att_remote_address.isEmpty()) - return NULL; - - return make_result_str(att->att_remote_address); - } - - if (name_str == DATABASE_NAME) - { - return make_result_str(dbb->dbb_database_name.ToString()); - } - - if (name_str == CURRENT_USER_NAME) - { - if (!att->att_user || att->att_user->usr_user_name.isEmpty()) - return NULL; - - return make_result_str(att->att_user->usr_user_name); - } - - if (name_str == CURRENT_ROLE_NAME) - { - if (!att->att_user || att->att_user->usr_sql_role_name.isEmpty()) - return NULL; - - return make_result_str(att->att_user->usr_sql_role_name); - } - - if (name_str == SESSION_ID_NAME) - { - Firebird::string session_id; - const SLONG att_id = PAG_attachment_id(tdbb); - session_id.printf("%d", att_id); - return make_result_str(session_id); - } - - if (name_str == TRANSACTION_ID_NAME) - { - Firebird::string transaction_id; - transaction_id.printf("%d", transaction->tra_number); - return make_result_str(transaction_id); - } - - if (name_str == ISOLATION_LEVEL_NAME) - { - const char* isolation; - - if (transaction->tra_flags & TRA_read_committed) - isolation = READ_COMMITTED_VALUE; - else if (transaction->tra_flags & TRA_degree3) - isolation = CONSISTENCY_VALUE; - else - isolation = SNAPSHOT_VALUE; - - return make_result_str(isolation, strlen(isolation)); - } - - // "Context variable %s is not found in namespace %s" - ERR_post(Arg::Gds(isc_ctx_var_not_found) << Arg::Str(name_str) << - Arg::Str(ns_str)); - } - - // Handle user-defined variables - if (ns_str == USER_SESSION_NAMESPACE) - { - Firebird::string result_str; - - if (!att->att_context_vars.get(name_str, result_str)) - return NULL; - - return make_result_str(result_str); - } - - if (ns_str == USER_TRANSACTION_NAMESPACE) - { - Firebird::string result_str; - - if (!transaction->tra_context_vars.get(name_str, result_str)) - return NULL; - - return make_result_str(result_str); - } - - // "Invalid namespace name %s passed to %s" - ERR_post(Arg::Gds(isc_ctx_namespace_invalid) << Arg::Str(ns_str) << Arg::Str(RDB_GET_CONTEXT)); - return NULL; -} - -static SLONG set_context(const vary* ns_vary, const vary* name_vary, const vary* value_vary) -{ - // Complain if namespace or variable name is null - if (!ns_vary || !name_vary) - { - ERR_post(Arg::Gds(isc_ctx_bad_argument) << Arg::Str(RDB_SET_CONTEXT)); - } - - thread_db* tdbb = JRD_get_thread_data(); - - if (!tdbb) - { - // Something is seriously wrong - fb_assert(false); - return 0; - } - - const Firebird::string ns_str(ns_vary->vary_string, ns_vary->vary_length); - const Firebird::string name_str(name_vary->vary_string, name_vary->vary_length); - //Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); - jrd_tra* tra = tdbb->getTransaction(); - - Firebird::StringMap* context_vars = NULL; - - bool result = false; - - if (ns_str == USER_SESSION_NAMESPACE) - { - if (!att) - { - fb_assert(false); - return 0; - } - - context_vars = &att->att_context_vars; - } - else if (ns_str == USER_TRANSACTION_NAMESPACE) - { - if (!tra) - { - fb_assert(false); - return 0; - } - - context_vars = &tra->tra_context_vars; - } - else - { - // "Invalid namespace name %s passed to %s" - ERR_post(Arg::Gds(isc_ctx_namespace_invalid) << Arg::Str(ns_str) << Arg::Str(RDB_SET_CONTEXT)); - } - - if (!value_vary) { - result = context_vars->remove(name_str); - } - else if (context_vars->count() == MAX_CONTEXT_VARS) - { - string* rc = context_vars->get(name_str); - if (rc) - { - rc->assign(value_vary->vary_string, value_vary->vary_length); - result = true; - } - else - ERR_post(Arg::Gds(isc_ctx_too_big)); // "Too many context variables" - } - else - { - result = context_vars->put(name_str, Firebird::string(value_vary->vary_string, value_vary->vary_length)); - } - - if (att->att_trace_manager->needs().event_set_context) - { - TraceConnectionImpl conn(att); - TraceTransactionImpl tran(tra); - - const Firebird::string* value_str = NULL; - if (value_vary) - value_str = context_vars->get(name_str); - - TraceContextVarImpl ctxvar(ns_str.c_str(), name_str.c_str(), - value_str ? value_str->c_str() : NULL); - - att->att_trace_manager->event_set_context(&conn, &tran, &ctxvar); - } - - return (SLONG) result; -} - -static int test(const long* n, char *result) -{ -/************************************** - * - * t e s t - * - ************************************** - * - * Functional description - * Sample extern function. Defined in database by: - * - * QLI: - * define function test module_name "test_module" entry_point "test_function" - * long by reference //by value, CVC: BY VALUE is deprecated for input params - * char [20] by reference return_argument; - * ISQL: - * declare external function test - * int null, // CVC: with NULL signaling - * char(20) returns parameter 2 - * entry_point 'test_function' module_name 'test_module'; - * - **************************************/ - - if (n) - sprintf(result, "%ld is a number", *n); - else - sprintf(result, "is NULL"); - const char* const end = result + 20; - - while (*result) - result++; - - while (result < end) - *result++ = ' '; - - return 0; -} - - -static dsc* ni(dsc* v, dsc* v2) -{ - return v ? v : v2; -} - - -// byteLen: return the length in bytes of a given argument. For NULL, return NULL, too. -// v = input descriptor -// rc = return value, allocated dynamically. To be freed by the engine. -// The declaration through SQL is: -// declare external function sys_byte_len -// int by descriptor -// returns int free_it -// entry_point 'byte_len' module_name 'test_module'; -static SLONG* byteLen(const dsc* v) -{ - if (!v || !v->dsc_address || (v->dsc_flags & DSC_null)) - return 0; - - SLONG& rc = *(SLONG*) IbUtil::alloc(sizeof(SLONG)); - switch (v->dsc_dtype) - { - case dtype_text: - { - const UCHAR* const ini = v->dsc_address; - const UCHAR* end = ini + v->dsc_length; - while (ini < end && *--end == ' ') - ; // empty loop body - rc = end - ini + 1; - break; - } - case dtype_cstring: - { - rc = 0; - for (const UCHAR* p = v->dsc_address; *p; ++p, ++rc) - ; // empty loop body - break; - } - case dtype_varying: - rc = reinterpret_cast(v->dsc_address)->vary_length; - break; - default: - rc = DSC_string_length(v); - break; - } - - return &rc; -} diff --git a/src/jrd/functions.h b/src/jrd/functions.h deleted file mode 100644 index e828d87836..0000000000 --- a/src/jrd/functions.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: functions.h - * DESCRIPTION: Definition of built-in functions - * - * The contents of this file are subject to the Initial - * Developer's Public License Version 1.0 (the "License"); - * you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. - * - * Software distributed under the License is distributed AS IS, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. - * See the License for the specific language governing rights - * and limitations under the License. - * - * The Original Code was created by Nickolay Samofatov - * for the Firebird Open Source RDBMS project. - * - * Copyright (c) 2004 Nickolay Samofatov and all contributors - * signed below. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -// Usage: -// FUNCTION(, "", "", "", ) -// FUNCTION_ARGUMENT(, , , , , , , ) - -// Uncomment this to have the two example functions registered automatically in system tables. -//#define DECLARE_EXAMPLE_IUDF_AUTOMATICALLY -// CVC: Starting demonstration code to register IUDF automatically. -#ifdef DECLARE_EXAMPLE_IUDF_AUTOMATICALLY -FUNCTION(byteLen, "SYS_BYTE_LEN", "test_module", "byte_len", 0) - FUNCTION_ARGUMENT(-FUN_reference, (int) blr_long, 0, 4, 0, 0, 0, 0) - FUNCTION_ARGUMENT(FUN_descriptor, (int) blr_long, 0, 4, 0, 0, 0, 0) -END_FUNCTION - -FUNCTION(test, "TEST", "test_module", "test_function", 2) - FUNCTION_ARGUMENT(FUN_ref_with_null, (int) blr_long, 0, 4, 0, 0, 0, 0) - FUNCTION_ARGUMENT(FUN_reference, (int) blr_text, 0, 20, 0, 0, 0, 20) -#endif -// CVC: Finishing demonstration code to register IUDF automatically. - - -FUNCTION(get_context, "RDB$GET_CONTEXT", "system_module", "get_context", 0) - // Result, variable value - FUNCTION_ARGUMENT(-FUN_reference, blr_varying, 0, 255, 0, 0, 0, 255) - // Namespace - FUNCTION_ARGUMENT(FUN_ref_with_null, blr_varying, 0, 80, 0, 0, 0, 80) - // Variable name - FUNCTION_ARGUMENT(FUN_ref_with_null, blr_varying, 0, 80, 0, 0, 0, 80) -END_FUNCTION - -FUNCTION(set_context, "RDB$SET_CONTEXT", "system_module", "set_context", 0) - // Result - FUNCTION_ARGUMENT(FUN_value, blr_long, 0, 4, 0, 0, 0, 0) - // Namespace - FUNCTION_ARGUMENT(FUN_ref_with_null, blr_varying, 0, 80, 0, 0, 0, 80) - // Variable name - FUNCTION_ARGUMENT(FUN_ref_with_null, blr_varying, 0, 80, 0, 0, 0, 80) - // Variable, value - FUNCTION_ARGUMENT(FUN_ref_with_null, blr_varying, 0, 255, 0, 0, 0, 255) -END_FUNCTION - diff --git a/src/jrd/gds.cpp b/src/jrd/gds.cpp index ed9daf1891..96a9089078 100644 --- a/src/jrd/gds.cpp +++ b/src/jrd/gds.cpp @@ -304,6 +304,8 @@ static const UCHAR union_ops[] = { op_byte, op_byte, op_line, op_union, 0}, map[] = { op_word, op_line, op_map, 0}, function[] = { op_byte, op_literal, op_byte, op_line, op_args, 0}, + function2[] = { op_byte, op_literal, op_pad, op_byte, op_literal, op_pad, op_byte, op_line, + op_args, 0}, gen_id[] = { op_byte, op_literal, op_line, op_verb, 0}, declare[] = { op_word, op_dtype, op_line, 0}, variable[] = { op_word, op_line, 0}, @@ -312,8 +314,12 @@ static const UCHAR join[] = { op_join, op_line, 0}, exec_proc[] = { op_byte, op_literal, op_line, op_indent, op_word, op_line, op_parameters, op_indent, op_word, op_line, op_parameters, 0}, + exec_proc2[] = { op_byte, op_literal, op_pad, op_byte, op_literal, op_line, op_indent, op_word, op_line, + op_parameters, op_indent, op_word, op_line, op_parameters, 0}, procedure[] = { op_byte, op_literal, op_pad, op_byte, op_line, op_indent, op_word, op_line, op_parameters, 0}, + procedure2[] = { op_byte, op_literal, op_pad, op_byte, op_literal, op_pad, op_byte, op_line, op_indent, + op_word, op_line, op_parameters, 0}, pid[] = { op_word, op_pad, op_byte, op_line, op_indent, op_word, op_line, op_parameters, 0}, error_handler[] = { op_word, op_line, op_error_handler, 0}, @@ -3598,7 +3604,8 @@ void gds__trace_printer(void* /*arg*/, SSHORT offset, const TEXT* line) gds__ulstr(p, offset, 4, ' '); p += strlen(p); *p++ = ' '; - strcpy(p, line); + strncpy(p, line, PRETTY_BUFFER_SIZE); + p[PRETTY_BUFFER_SIZE] = '\0'; p += strlen(p); *p++ = '\n'; *p = 0; diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index 4261eec60c..d388ac9949 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -126,7 +126,8 @@ void GRANT_privileges(thread_db* tdbb, const Firebird::string& name, USHORT id, CHECK_AND_MOVE(acl, ACL_version); grant_user(acl, owner, obj_user, - (id == obj_procedure ? SCL_execute | OWNER_PRIVS : OWNER_PRIVS)); + (id == obj_procedure || id == obj_package_header ? + SCL_execute | OWNER_PRIVS : OWNER_PRIVS)); /* Pick up any relation-level privileges */ @@ -331,7 +332,8 @@ static void get_object_info(thread_db* tdbb, owner = s_class = default_class = ""; view = false; - if (obj_type == obj_relation) { + if (obj_type == obj_relation) + { jrd_req* request = CMP_find_request(tdbb, irq_grant1, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) @@ -350,12 +352,34 @@ static void get_object_info(thread_db* tdbb, if (!REQUEST(irq_grant1)) REQUEST(irq_grant1) = request; } - else { + else if (obj_type == obj_package_header) + { + jrd_req* request = CMP_find_request(tdbb, irq_grant10, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE request) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ object_name + + if (!REQUEST(irq_grant10)) + REQUEST(irq_grant10) = request; + + s_class = PKG.RDB$SECURITY_CLASS; + default_class = ""; + owner = PKG.RDB$OWNER_NAME; + view = false; + END_FOR; + + if (!REQUEST(irq_grant10)) + REQUEST(irq_grant10) = request; + } + else + { jrd_req* request = CMP_find_request(tdbb, irq_grant9, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) REL IN RDB$PROCEDURES WITH - REL.RDB$PROCEDURE_NAME EQ object_name + REL.RDB$PROCEDURE_NAME EQ object_name AND + REL.RDB$PACKAGE_NAME MISSING if (!REQUEST(irq_grant9)) REQUEST(irq_grant9) = request; @@ -469,6 +493,10 @@ static void grant_user(Acl& acl, CHECK_AND_MOVE(acl, id_person); break; + case obj_package_header: + CHECK_AND_MOVE(acl, id_package); + break; + case obj_procedure: CHECK_AND_MOVE(acl, id_procedure); break; diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 613b1d01f9..936d96f750 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -335,7 +335,7 @@ void IDX_create_index(thread_db* tdbb, // Unless this is the only attachment or a database restore, worry about // preserving the page working sets of other attachments. - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attachment && (attachment != dbb->dbb_attachments || attachment->att_next)) { if (attachment->att_flags & ATT_gbak_attachment || diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 38a7b6fcd6..fb344480b4 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -30,7 +30,7 @@ /* Indices to be created */ /* Maxinum number of segments in any existing system index */ -const int INI_IDX_MAX_SEGMENTS = 2; +const int INI_IDX_MAX_SEGMENTS = 3; struct ini_idx_t { @@ -95,14 +95,16 @@ static const struct ini_idx_t indices[] = INDEX(8, ODS_8_0, rel_triggers, idx_unique, 1) SEGMENT(f_trg_name, idx_metadata) /* trigger name */ }}, +#if 0 // removed for package support /* define index RDB$INDEX_9 for RDB$FUNCTIONS unique RDB$FUNCTION_NAME; */ INDEX(9, ODS_8_0, rel_funs, idx_unique, 1) - SEGMENT(f_fun_name, idx_metadata) /* function name */ + SEGMENT(f_fun_name, idx_metadata) // function name }}, /* define index RDB$INDEX_10 for RDB$FUNCTION_ARGUMENTS RDB$FUNCTION_NAME; */ INDEX(10, ODS_8_0, rel_args, 0, 1) - SEGMENT(f_arg_fun_name, idx_metadata) /* function name */ + SEGMENT(f_arg_fun_name, idx_metadata) // function name }}, +#endif /* define index RDB$INDEX_11 for RDB$GENERATORS unique RDB$GENERATOR_NAME; */ INDEX(11, ODS_8_0, rel_gens, idx_unique, 1) SEGMENT(f_gen_name, idx_metadata) /* Generator name */ @@ -134,11 +136,13 @@ static const struct ini_idx_t indices[] = SEGMENT(f_flt_input, idx_numeric), /* input subtype */ SEGMENT(f_flt_output, idx_numeric) /* output subtype */ }}, +#if 0 // removed for package support /* define index RDB$INDEX_18 for RDB$PROCEDURE_PARAMETERS unique RDB$PROCEDURE_NAME, RDB$PARAMETER_NAME; */ INDEX(18, ODS_8_0, rel_prc_prms, idx_unique, 2) - SEGMENT(f_prm_procedure, idx_metadata), /* procedure name */ - SEGMENT(f_prm_name, idx_metadata) /* parameter name */ + SEGMENT(f_prm_procedure, idx_metadata), // procedure name + SEGMENT(f_prm_name, idx_metadata) // parameter name }}, +#endif /* Index on rel_files removed 93-Feb-27 by DaveS. * f_file_name is now 255 bytes, which with roundup creates @@ -162,10 +166,12 @@ static const struct ini_idx_t indices[] = SEGMENT(f_coll_name, idx_metadata) // collation name }}, +#if 0 // removed for package support /* define index RDB$INDEX_21 for RDB$PROCEDURES unique RDB$PROCEDURE_NAME; */ INDEX(21, ODS_8_0, rel_procedures, idx_unique, 1) - SEGMENT(f_prc_name, idx_metadata) /* procedure name */ + SEGMENT(f_prc_name, idx_metadata) // procedure name }}, +#endif /* define index RDB$INDEX_22 for RDB$PROCEDURES unique RDB$PROCEDURE_ID; */ INDEX(22, ODS_8_0, rel_procedures, idx_unique, 1) SEGMENT(f_prc_id, idx_numeric) /* procedure id */ @@ -310,9 +316,38 @@ static const struct ini_idx_t indices[] = /* define index RDB$INDEX_46 for RDB$GENERATORS unique RDB$GENERATOR_ID; */ INDEX(46, ODS_11_2, rel_gens, idx_unique, 1) SEGMENT(f_gen_id, idx_numeric) /* generator id */ - }} + }}, /* Last index in ODS 11.2 is RDB$INDEX_46 */ + + // define index RDB$INDEX_47 for RDB$FUNCTIONS unique RDB$FUNCTION_NAME, RDB$PACKAGE_NAME; + INDEX(47, ODS_12_0, rel_funs, idx_unique, 2) + SEGMENT(f_fun_name, idx_metadata), // function name + SEGMENT(f_fun_pkg_name, idx_metadata) // package name + }}, + // define index RDB$INDEX_48 for RDB$FUNCTION_ARGUMENTS RDB$FUNCTION_NAME, RDB$PACKAGE_NAME; + INDEX(48, ODS_12_0, rel_args, 0, 2) + SEGMENT(f_arg_fun_name, idx_metadata), // function name + SEGMENT(f_arg_pkg_name, idx_metadata) // package name + }}, + // define index RDB$INDEX_49 for RDB$PACKAGES unique RDB$PACKAGE_NAME; + INDEX(49, ODS_12_0, rel_packages, idx_unique, 1) + SEGMENT(f_pkg_name, idx_metadata) // package name + }}, + // define index RDB$INDEX_50 for RDB$PROCEDURES unique RDB$PROCEDURE_NAME, RDB$PACKAGE_NAME; + INDEX(50, ODS_12_0, rel_procedures, idx_unique, 2) + SEGMENT(f_prc_name, idx_metadata), // procedure name + SEGMENT(f_prc_pkg_name, idx_metadata) // package name + }}, + // define index RDB$INDEX_51 for RDB$PROCEDURE_PARAMETERS unique RDB$PROCEDURE_NAME, + // RDB$PARAMETER_NAME, RDB$PACKAGE_NAME; + INDEX(51, ODS_12_0, rel_prc_prms, idx_unique, 3) + SEGMENT(f_prm_procedure, idx_metadata), // procedure name + SEGMENT(f_prm_name, idx_metadata), // parameter name + SEGMENT(f_prm_pkg_name, idx_metadata) // package name + }} + + // Last index in ODS 12.0 is RDB$INDEX_51 }; #define SYSTEM_INDEX_COUNT FB_NELEM(indices) diff --git a/src/jrd/inf_pub.h b/src/jrd/inf_pub.h index 68dc7c67ff..5d0ea113e4 100644 --- a/src/jrd/inf_pub.h +++ b/src/jrd/inf_pub.h @@ -309,6 +309,7 @@ enum info_db_provider #define isc_info_rsb_skip 22 #define isc_info_rsb_virt_sequential 23 #define isc_info_rsb_recursive 24 +#define isc_info_rsb_window 25 /**********************/ /* Bitmap expressions */ diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index ed3305e8bb..20058e6729 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -75,7 +75,6 @@ static void modify_relation_field(thread_db*, const int*, const int*, jrd_req**) static void store_generator(thread_db*, const gen*, jrd_req**); static void store_global_field(thread_db*, const gfld*, jrd_req**); static void store_intlnames(thread_db*, Database*); -static void store_functions(thread_db*, Database*); static void store_message(thread_db*, const trigger_msg*, jrd_req**); static void store_relation_field(thread_db*, const int*, const int*, int, jrd_req**, bool); static void store_trigger(thread_db*, const jrd_trg*, jrd_req**); @@ -457,11 +456,6 @@ void INI_format(const TEXT* owner, const TEXT* charset) } CMP_release(tdbb, handle1); -// Store IUDFs declared automatically as system functions. -// CVC: Demonstration code to register IUDF automatically moved to functions.h - - store_functions(tdbb, dbb); - DFW_perform_system_work(tdbb); add_relation_fields(tdbb, 0); @@ -666,7 +660,7 @@ void INI_init2(thread_db* tdbb) { /* If the ODS is less than 10, then remove all fields named * RDB$FIELD_PRECISION and field RDB$CHARACTER_LENGTH from - * relation RDB$FUNCTION_ARGUMENTS , as they were not present + * relation RDB$FUNCTION_ARGUMENTS, as they were not present * in < 10 ODS */ if (fld[RFLD_F_NAME] == nam_f_precision || @@ -734,6 +728,48 @@ void INI_init2(thread_db* tdbb) if (ENCODE_ODS(major_version, minor_original) >= ODS_11_2) n = init2_helper(fld, n, relation); } + else if (fld[RFLD_F_NAME] == nam_engine_name && ( + relfld[RFLD_R_NAME] == nam_funs || + relfld[RFLD_R_NAME] == nam_procedures || + relfld[RFLD_R_NAME] == nam_trgs)) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } + else if (fld[RFLD_F_NAME] == nam_entry && ( + relfld[RFLD_R_NAME] == nam_procedures || + relfld[RFLD_R_NAME] == nam_trgs)) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } + else if (fld[RFLD_F_NAME] == nam_pkg_name && + (relfld[RFLD_R_NAME] == nam_funs || + relfld[RFLD_R_NAME] == nam_args || + relfld[RFLD_R_NAME] == nam_procedures || + relfld[RFLD_R_NAME] == nam_proc_parameters || + relfld[RFLD_R_NAME] == nam_dpds)) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } + else if (fld[RFLD_F_NAME] == nam_fun_source && relfld[RFLD_R_NAME] == nam_funs) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } + else if (fld[RFLD_F_NAME] == nam_private_flag && + (relfld[RFLD_R_NAME] == nam_funs || relfld[RFLD_R_NAME] == nam_procedures)) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } + else if (fld[RFLD_F_NAME] == nam_mon_pkg_name && + relfld[RFLD_R_NAME] == nam_mon_calls) + { + if (ENCODE_ODS(major_version, minor_original) >= ODS_12_0) + n = init2_helper(fld, n, relation); + } else n = init2_helper(fld, n, relation); } @@ -746,12 +782,20 @@ void INI_init2(thread_db* tdbb) { if (n-- > 0) { + // In ODS 12.0, RDB$TRIGGER_TYPE was changed from SHORTINT to BIGINT + if (fld[RFLD_F_NAME] == nam_trg_type && major_version < ODS_12_0) + { + desc->dsc_dtype = dtype_short; + desc->dsc_length = sizeof(SSHORT); + } + format->fmt_length = (USHORT)MET_align(dbb, &(*desc), format->fmt_length); desc->dsc_address = (UCHAR*) (IPTR) format->fmt_length; // In ODS11 length of RDB$MESSAGE was enlarged from 80 to 1023 bytes if ((fld[RFLD_F_NAME] == nam_msg) && (major_version < ODS_VERSION11)) { + // In ODS11 length of RDB$MESSAGES are enlarged from 80 to 1023 bytes desc->dsc_length = 80 + sizeof(USHORT); } @@ -769,6 +813,13 @@ void INI_init2(thread_db* tdbb) { desc->dsc_length = 31; } + else if (fld[RFLD_F_NAME] == nam_entry && + ENCODE_ODS(major_version, minor_original) < ODS_12_0) + { + // In ODS12.0 length of RDB$EXTERNAL_NAME are enlarged from + // 31 to 255 bytes + desc->dsc_length = 31; + } format->fmt_length += desc->dsc_length; } @@ -1373,81 +1424,6 @@ static void store_intlnames(thread_db* tdbb, Database* dbb) } -static void store_function_argument(thread_db* tdbb, Database* dbb, jrd_req*& handle, - const char* function_name, SLONG argument_position, SLONG mechanism, SLONG type, SLONG scale, - SLONG length, SLONG sub_type, SLONG charset, SLONG precision, SLONG char_length) -{ - STORE(REQUEST_HANDLE handle) FA IN RDB$FUNCTION_ARGUMENTS - PAD(function_name, FA.RDB$FUNCTION_NAME); - FA.RDB$FUNCTION_NAME.NULL = FALSE; - FA.RDB$ARGUMENT_POSITION = argument_position; - FA.RDB$MECHANISM = mechanism; - FA.RDB$FIELD_TYPE = type; - FA.RDB$FIELD_SCALE = scale; - FA.RDB$FIELD_LENGTH = length; - FA.RDB$FIELD_SUB_TYPE = sub_type; - FA.RDB$CHARACTER_SET_ID = charset; - FA.RDB$FIELD_PRECISION = precision; - FA.RDB$CHARACTER_LENGTH = char_length; - END_STORE; -} - - -static void store_function(thread_db* tdbb, Database* dbb, jrd_req*& handle, - const char* function_name, const char* module, const char* entrypoint, SLONG argument) -{ - STORE(REQUEST_HANDLE handle) F IN RDB$FUNCTIONS - PAD(function_name, F.RDB$FUNCTION_NAME); - F.RDB$FUNCTION_NAME.NULL = FALSE; - //F.RDB$FUNCTION_TYPE - //F.RDB$QUERY_NAME - //F.RDB$DESCRIPTION - strcpy(F.RDB$MODULE_NAME, module); - F.RDB$MODULE_NAME.NULL = FALSE; - PAD(entrypoint, F.RDB$ENTRYPOINT); - F.RDB$ENTRYPOINT.NULL = FALSE; - F.RDB$RETURN_ARGUMENT = argument; - F.RDB$SYSTEM_FLAG = RDB_system; - F.RDB$SYSTEM_FLAG.NULL = FALSE; - END_STORE; -} - - -static void store_functions(thread_db* tdbb, Database* dbb) -{ -/************************************** - * - * s t o r e _ f u n c t i o n s - * - ************************************** - * - * Functional description - * Store built-in functions and their arguments - * - **************************************/ - SET_TDBB(tdbb); - - jrd_req *fun_handle = NULL, *arg_handle = NULL; - const char *function_name; - SLONG argument_position; - -#define FUNCTION(ROUTINE, FUNCTION_NAME, MODULE_NAME, ENTRYPOINT, RET_ARG) \ - function_name = FUNCTION_NAME; \ - argument_position = RET_ARG ? 1 : 0; \ - store_function(tdbb, dbb, fun_handle, FUNCTION_NAME, MODULE_NAME, ENTRYPOINT, RET_ARG); -#define END_FUNCTION -#define FUNCTION_ARGUMENT(MECHANISM, TYPE, SCALE, LENGTH, SUB_TYPE, CHARSET, PRECISION, CHAR_LENGTH) \ - store_function_argument(tdbb, dbb, arg_handle, function_name, argument_position, \ - MECHANISM, TYPE, SCALE, LENGTH, SUB_TYPE, CHARSET, PRECISION, CHAR_LENGTH); \ - argument_position++; - -#include "../jrd/functions.h" - - CMP_release(tdbb, fun_handle); - CMP_release(tdbb, arg_handle); -} - - static void store_message(thread_db* tdbb, const trigger_msg* message, jrd_req** handle) { /************************************** diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index 2cde8efe33..85de5b6680 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -209,7 +209,7 @@ CharSetContainer* CharSetContainer::lookupCharset(thread_db* tdbb, USHORT ttype) USHORT id = TTYPE_TO_CHARSET(ttype); if (id == CS_dynamic) - id = tdbb->getAttachment()->att_charset; + id = tdbb->getCharSet(); if (id >= dbb->dbb_charsets.getCount()) dbb->dbb_charsets.resize(id + 10); @@ -486,6 +486,63 @@ void Database::destroyIntlObjects() } +void INTL_adjust_text_descriptor(thread_db* tdbb, dsc* desc) +{ +/************************************** + * + * I N T L _ a d j u s t _ t e x t _ d e s c r i p t o r + * + ************************************** + * + * Functional description + * This function receives a text descriptor with + * dsc_length = numberOfCharacters * maxBytesPerChar + * and change dsc_length to number of bytes used by the string. + * + **************************************/ + if (desc->dsc_dtype == dtype_text) + { + SET_TDBB(tdbb); + + USHORT ttype = INTL_TTYPE(desc); + + CharSet* charSet = INTL_charset_lookup(tdbb, ttype); + + if (charSet->isMultiByte()) + { + Firebird::HalfStaticArray buffer; + + if (charSet->getFlags() & CHARSET_LEGACY_SEMANTICS) + { + desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address, TEXT_LEN(desc), + buffer.getBuffer(TEXT_LEN(desc) * charSet->maxBytesPerChar()), 0, + TEXT_LEN(desc)); + + ULONG maxLength = TEXT_LEN(desc) / charSet->maxBytesPerChar(); + ULONG charLength = charSet->length(desc->dsc_length, desc->dsc_address, true); + + while (charLength > maxLength) + { + if (desc->dsc_address[desc->dsc_length - 1] == *charSet->getSpace()) + { + --desc->dsc_length; + --charLength; + } + else + break; + } + } + else + { + desc->dsc_length = charSet->substring(TEXT_LEN(desc), desc->dsc_address, + TEXT_LEN(desc), buffer.getBuffer(TEXT_LEN(desc)), 0, + TEXT_LEN(desc) / charSet->maxBytesPerChar()); + } + } + } +} + + CHARSET_ID INTL_charset(thread_db* tdbb, USHORT ttype) { /************************************** @@ -511,7 +568,7 @@ CHARSET_ID INTL_charset(thread_db* tdbb, USHORT ttype) return (CS_BINARY); case ttype_dynamic: SET_TDBB(tdbb); - return (tdbb->getAttachment()->att_charset); + return (tdbb->getCharSet()); default: return (TTYPE_TO_CHARSET(ttype)); } @@ -694,10 +751,10 @@ CsConvert INTL_convert_lookup(thread_db* tdbb, CHECK_DBB(dbb); if (from_cs == CS_dynamic) - from_cs = tdbb->getAttachment()->att_charset; + from_cs = tdbb->getCharSet(); if (to_cs == CS_dynamic) - to_cs = tdbb->getAttachment()->att_charset; + to_cs = tdbb->getCharSet(); /* Should from_cs == to_cs? be handled better? YYY */ @@ -1045,7 +1102,7 @@ Collation* INTL_texttype_lookup(thread_db* tdbb, SET_TDBB(tdbb); if (parm1 == ttype_dynamic) - parm1 = MAP_CHARSET_TO_TTYPE(tdbb->getAttachment()->att_charset); + parm1 = MAP_CHARSET_TO_TTYPE(tdbb->getCharSet()); CharSetContainer* csc = CharSetContainer::lookupCharset(tdbb, parm1); diff --git a/src/jrd/intl.h b/src/jrd/intl.h index eb146633f5..010f29b96a 100644 --- a/src/jrd/intl.h +++ b/src/jrd/intl.h @@ -148,7 +148,7 @@ inline USHORT INTL_TEXT_TYPE(const dsc& desc) #define MAP_CHARSET_TO_TTYPE(cs) (cs & 0x00FF) #define INTL_RES_TTYPE(desc) (INTL_DYNAMIC_CHARSET(desc) ?\ - MAP_CHARSET_TO_TTYPE(tdbb->getAttachment()->att_charset) :\ + MAP_CHARSET_TO_TTYPE(tdbb->getCharSet()) :\ INTL_GET_TTYPE (desc)) #define INTL_INDEX_TYPE(desc) INTL_TEXT_TO_INDEX (INTL_RES_TTYPE (desc)) diff --git a/src/jrd/intl_proto.h b/src/jrd/intl_proto.h index 633821cc96..e964a8bf34 100644 --- a/src/jrd/intl_proto.h +++ b/src/jrd/intl_proto.h @@ -34,6 +34,7 @@ namespace Jrd { struct dsc; struct SubtypeInfo; +void INTL_adjust_text_descriptor(Jrd::thread_db* tdbb, dsc* desc); CHARSET_ID INTL_charset(Jrd::thread_db*, USHORT); int INTL_compare(Jrd::thread_db*, const dsc*, const dsc*, ErrorFunction); ULONG INTL_convert_bytes(Jrd::thread_db*, CHARSET_ID, UCHAR*, ULONG, CHARSET_ID, diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 74cca4d9ce..d8c8025ac1 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -91,6 +91,7 @@ enum irq_type_t irq_r_params, // scan procedure parameters irq_r_procedure, // scan procedure + irq_pkg_security, // verify security for package irq_p_security, // verify security for procedure irq_c_prc_dpd, // create procedure dependencies for delete @@ -124,8 +125,8 @@ enum irq_type_t irq_m_fields3, // process a modification of RDB$FIELDS for triggers (ODS 11.1) irq_m_fields4, // process a modification of RDB$FIELDS for procedures (TYPE OF COLUMN) irq_m_fields5, // process a modification of RDB$FIELDS for triggers (TYPE OF COLUMN) + irq_m_fields6, // process a modification of RDB$FIELDS for packaged procedures (TYPE OF COLUMN) irq_r_params2, // scan procedure parameters (ODS 11.1) - irq_l_trg_dbg, // lookup trigger debug_info (ODS 11.1) irq_l_colls, // lookup collations irq_l_relfield, // lookup a relation field irq_verify_trusted_role, // ensure trusted role exists @@ -154,6 +155,8 @@ enum irq_type_t irq_default_cs, // DSQL/METD: lookup the default charset irq_rel_ids, // DSQL/METD: check relation/field ids irq_comp_circ_dpd, // check computed circular dependencies + irq_grant10, // process grant option (packages) + irq_l_procedure_pkg_class, // lookup security class of a packaged procedure irq_MAX }; diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index c486625c14..9bc2aeecd1 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -118,6 +118,7 @@ #include "../common/config/config.h" #include "../common/config/dir_list.h" +#include "../jrd/PluginManager.h" #include "../jrd/db_alias.h" #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceObjects.h" @@ -147,6 +148,8 @@ int debug; namespace { + using Jrd::Attachment; + Database* databases = NULL; GlobalPtr databases_mutex; bool engineShuttingDown = false; @@ -156,8 +159,10 @@ namespace public: static void init() { + PluginManager::initialize(); IbUtil::initialize(); IntlManager::initialize(); + ExtEngineManager::initialize(); } static void cleanup() @@ -167,7 +172,7 @@ namespace InitMutex engineStartup; - inline void validateHandle(thread_db* tdbb, Attachment* const attachment) + inline void validateHandle(thread_db* tdbb, Jrd::Attachment* const attachment) { if (!attachment->checkHandle() || !attachment->att_database->checkHandle()) { @@ -225,7 +230,7 @@ namespace AttachmentHolder(thread_db* arg, bool lockAtt) : tdbb(arg) { - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (lockAtt && attachment) { if (engineShuttingDown) @@ -240,7 +245,7 @@ namespace ~AttachmentHolder() { - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attLocked && attachment) attachment->att_mutex.leave(); } @@ -286,7 +291,7 @@ namespace thread_db* tdbb; }; - void validateAccess(const Attachment* attachment) + void validateAccess(const Jrd::Attachment* attachment) { if (!attachment->locksmith()) { @@ -305,87 +310,105 @@ namespace void Jrd::Trigger::compile(thread_db* tdbb) { - if (!request /*&& !compile_in_progress*/) + SET_TDBB(tdbb); + + Database* dbb = tdbb->getDatabase(); + + if (engine.isEmpty() && !extTrigger) { - SET_TDBB(tdbb); - - Database* dbb = tdbb->getDatabase(); - - Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex); - - if (request) + if (!request /*&& !compile_in_progress*/) { - return; - } - - compile_in_progress = true; - // Allocate statement memory pool - MemoryPool* new_pool = dbb->createPool(); - // Trigger request is not compiled yet. Lets do it now - USHORT par_flags = (USHORT) (flags & TRG_ignore_perm) ? csb_ignore_perm : 0; - if (type & 1) - par_flags |= csb_pre_trigger; - else - par_flags |= csb_post_trigger; - - CompilerScratch* csb = NULL; - try { - Jrd::ContextPoolHolder context(tdbb, new_pool); - - csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5); - csb->csb_g_flags |= par_flags; - - if (!dbg_blob_id.isEmpty()) - DBG_parse_debug_info(tdbb, &dbg_blob_id, csb->csb_dbg_info); - - PAR_blr(tdbb, relation, blr.begin(), (ULONG) blr.getCount(), NULL, &csb, &request, - (relation ? true : false), par_flags); - - delete csb; - } - catch (const Exception&) - { - compile_in_progress = false; - delete csb; - csb = NULL; + Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex); if (request) { - CMP_release(tdbb, request); - request = NULL; - } - else { - dbb->deletePool(new_pool); + return; } - throw; + compile_in_progress = true; + // Allocate statement memory pool + MemoryPool* new_pool = dbb->createPool(); + // Trigger request is not compiled yet. Lets do it now + USHORT par_flags = (USHORT) (flags & TRG_ignore_perm) ? csb_ignore_perm : 0; + if (type & 1) + par_flags |= csb_pre_trigger; + else + par_flags |= csb_post_trigger; + + CompilerScratch* csb = NULL; + try { + Jrd::ContextPoolHolder context(tdbb, new_pool); + + csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5); + csb->csb_g_flags |= par_flags; + + if (!dbg_blob_id.isEmpty()) + DBG_parse_debug_info(tdbb, &dbg_blob_id, csb->csb_dbg_info); + + PAR_blr(tdbb, relation, blr.begin(), (ULONG) blr.getCount(), NULL, &csb, &request, + (relation ? true : false), par_flags); + + delete csb; + } + catch (const Exception&) + { + compile_in_progress = false; + delete csb; + csb = NULL; + + if (request) { + CMP_release(tdbb, request); + request = NULL; + } + else { + dbb->deletePool(new_pool); + } + + throw; + } + + request->req_trg_name = name; + + if (sys_trigger) + { + request->req_flags |= req_sys_trigger; + } + if (flags & TRG_ignore_perm) + { + request->req_flags |= req_ignore_perm; + } + + compile_in_progress = false; } - request->req_trg_name = name; - - if (sys_trigger) - { - request->req_flags |= req_sys_trigger; - } - if (flags & TRG_ignore_perm) - { - request->req_flags |= req_ignore_perm; - } - - compile_in_progress = false; + return; } + + // external trigger + + if (extTrigger) + return; + + extTrigger = dbb->dbb_extManager.makeTrigger(tdbb, this, engine, entryPoint, extBody.c_str(), + (relation ? (type & 1 ? + Firebird::ExternalTrigger::TYPE_BEFORE : + Firebird::ExternalTrigger::TYPE_AFTER) : + Firebird::ExternalTrigger::TYPE_DATABASE)); } void Jrd::Trigger::release(thread_db* tdbb) { - if (blr.getCount() == 0 || !request || CMP_clone_is_active(request)) + if (extTrigger) { - return; // FALSE; + delete extTrigger; + extTrigger = NULL; } + if (blr.getCount() == 0 || !request || CMP_clone_is_active(request)) + return; + CMP_release(tdbb, request); request = NULL; - return; // TRUE; } // Option block for database parameter block @@ -516,13 +539,13 @@ static void check_database(thread_db* tdbb); static void check_transaction(thread_db*, jrd_tra*); static void commit(thread_db*, jrd_tra*, const bool); static bool drop_files(const jrd_file*); -static void find_intl_charset(thread_db*, Attachment*, const DatabaseOptions*); +static void find_intl_charset(thread_db*, Jrd::Attachment*, const DatabaseOptions*); static jrd_tra* find_transaction(thread_db*, ISC_STATUS); static void init_database_locks(thread_db*); static ISC_STATUS handle_error(ISC_STATUS*, ISC_STATUS); static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction); static void verify_request_synchronization(jrd_req*& request, SSHORT level); -static unsigned int purge_transactions(thread_db*, Attachment*, const bool, const ULONG); +static unsigned int purge_transactions(thread_db*, Jrd::Attachment*, const bool, const ULONG); namespace { enum VdnResult {VDN_FAIL, VDN_OK, VDN_SECURITY}; } @@ -530,7 +553,7 @@ static VdnResult verifyDatabaseName(const PathName&, ISC_STATUS*, bool); static ISC_STATUS unwindAttach(const Exception& ex, ISC_STATUS* userStatus, thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, Database* dbb); #ifdef WIN_NT static void ExtractDriveLetter(const TEXT*, ULONG*); @@ -538,12 +561,12 @@ static void ExtractDriveLetter(const TEXT*, ULONG*); static Database* init(thread_db*, const PathName&, bool); static void prepare(thread_db*, jrd_tra*, USHORT, const UCHAR*); -static void release_attachment(thread_db*, Attachment*); -static void detachLocksFromAttachment(Attachment*); +static void release_attachment(thread_db*, Jrd::Attachment*); +static void detachLocksFromAttachment(Jrd::Attachment*); static void rollback(thread_db*, jrd_tra*, const bool); static void shutdown_database(Database*, const bool); static void strip_quotes(string&); -static void purge_attachment(thread_db*, Attachment*, const bool); +static void purge_attachment(thread_db*, Jrd::Attachment*, const bool); static void getUserInfo(UserId&, const DatabaseOptions&); static bool shutdown_dbb(thread_db*, Database*); @@ -560,8 +583,8 @@ static void cancel_attachments() if ( !(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use | DBB_security_db)) ) { Database::SyncGuard dsGuard(dbb); - Attachment* lockedAtt = NULL; - Attachment* att = dbb->dbb_attachments; + Jrd::Attachment* lockedAtt = NULL; + Jrd::Attachment* att = dbb->dbb_attachments; while (att) { @@ -755,8 +778,9 @@ void trace_failed_attach(TraceManager* traceManager, const char* filename, ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, + FB_API_HANDLE public_handle, const TEXT* filename, - Attachment** handle, + Jrd::Attachment** handle, SSHORT dpb_length, const UCHAR* dpb) { @@ -873,7 +897,7 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, // Initialize special error handling - Attachment* attachment = NULL; + Jrd::Attachment* attachment = NULL; bool initing_security = false; @@ -924,7 +948,7 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, } } - attachment = Attachment::create(dbb); + attachment = Jrd::Attachment::create(dbb, public_handle); tdbb->setAttachment(attachment); attachment->att_filename = is_alias ? file_name : expanded_name; attachment->att_network_protocol = options.dpb_network_protocol; @@ -937,7 +961,7 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, dbb->dbb_flags &= ~DBB_being_opened; dbb->dbb_sys_trans->tra_attachment = attachment; - attachment->att_charset = options.dpb_interp; + attachment->att_client_charset = attachment->att_charset = options.dpb_interp; if (options.dpb_no_garbage) attachment->att_flags |= ATT_no_cleanup; @@ -1364,6 +1388,8 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, // Recover database after crash during backup difference file merge dbb->dbb_backup_manager->endBackup(tdbb, true); // true = do recovery + *handle = attachment; + if (attachment->att_trace_manager->needs().event_attach) { TraceConnectionImpl conn(attachment); @@ -1384,11 +1410,15 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_COMMIT); MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_ROLLBACK); + // load DDL triggers + MET_load_ddl_triggers(tdbb); + const trig_vec* trig_connect = dbb->dbb_triggers[DB_TRIGGER_CONNECT]; if (trig_connect && !trig_connect->isEmpty()) { // Start a transaction to execute ON CONNECT triggers. // Ensure this transaction can't trigger auto-sweep. + //// TODO: register the transaction in y-valve - for external engines attachment->att_flags |= ATT_no_cleanup; transaction = TRA_start(tdbb, 0, NULL); attachment->att_flags = save_flags; @@ -1402,6 +1432,7 @@ ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, } catch (const Exception&) { + *handle = NULL; attachment->att_flags = save_flags; if (!(dbb->dbb_flags & DBB_bugcheck) && transaction) TRA_rollback(tdbb, transaction, false, false); @@ -1502,7 +1533,7 @@ ISC_STATUS GDS_CANCEL_BLOB(ISC_STATUS* user_status, blb** blob_handle) } -ISC_STATUS GDS_CANCEL_EVENTS(ISC_STATUS* user_status, Attachment** handle, SLONG* id) +ISC_STATUS GDS_CANCEL_EVENTS(ISC_STATUS* user_status, Jrd::Attachment** handle, SLONG* id) { /************************************** * @@ -1538,7 +1569,7 @@ ISC_STATUS GDS_CANCEL_EVENTS(ISC_STATUS* user_status, Attachment** handle, SLONG } -ISC_STATUS FB_CANCEL_OPERATION(ISC_STATUS* user_status, Attachment** handle, USHORT option) +ISC_STATUS FB_CANCEL_OPERATION(ISC_STATUS* user_status, Jrd::Attachment** handle, USHORT option) { /************************************** * @@ -1554,7 +1585,7 @@ ISC_STATUS FB_CANCEL_OPERATION(ISC_STATUS* user_status, Attachment** handle, USH { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *handle; + Jrd::Attachment* const attachment = *handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb, false); @@ -1691,7 +1722,7 @@ ISC_STATUS GDS_COMMIT_RETAINING(ISC_STATUS* user_status, jrd_tra** tra_handle) ISC_STATUS GDS_COMPILE(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_req** req_handle, SSHORT blr_length, const SCHAR* blr) @@ -1709,7 +1740,7 @@ ISC_STATUS GDS_COMPILE(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); check_database(tdbb); @@ -1743,7 +1774,7 @@ ISC_STATUS GDS_COMPILE(ISC_STATUS* user_status, ISC_STATUS GDS_CREATE_BLOB2(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, blb** blob_handle, bid* blob_id, @@ -1788,8 +1819,9 @@ ISC_STATUS GDS_CREATE_BLOB2(ISC_STATUS* user_status, ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, + FB_API_HANDLE public_handle, const TEXT* filename, - Attachment** handle, + Jrd::Attachment** handle, USHORT dpb_length, const UCHAR* dpb) { @@ -1904,7 +1936,7 @@ ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, dbb->dbb_flags |= (DBB_being_opened | options.dpb_flags); - Attachment* attachment = NULL; + Jrd::Attachment* attachment = NULL; bool initing_security = false; @@ -1929,7 +1961,7 @@ ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, dbb->dbb_encrypt_key = options.dpb_key; } - attachment = Attachment::create(dbb); + attachment = Jrd::Attachment::create(dbb, public_handle); tdbb->setAttachment(attachment); attachment->att_filename = is_alias ? file_name : expanded_name; attachment->att_network_protocol = options.dpb_network_protocol; @@ -1971,7 +2003,7 @@ ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, break; } - attachment->att_charset = options.dpb_interp; + attachment->att_client_charset = attachment->att_charset = options.dpb_interp; if (!options.dpb_page_size) { options.dpb_page_size = DEFAULT_PAGE_SIZE; @@ -1998,7 +2030,8 @@ ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, { if (options.dpb_overwrite) { - if (GDS_ATTACH_DATABASE(user_status, filename, handle, dpb_length, dpb) == isc_adm_task_denied) + if (GDS_ATTACH_DATABASE(user_status, public_handle, filename, handle, + dpb_length, dpb) == isc_adm_task_denied) { throw; } @@ -2172,7 +2205,7 @@ ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, ISC_STATUS GDS_DATABASE_INFO(ISC_STATUS* user_status, - Attachment** handle, + Jrd::Attachment** handle, SSHORT item_length, const SCHAR* items, SSHORT buffer_length, @@ -2192,7 +2225,7 @@ ISC_STATUS GDS_DATABASE_INFO(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *handle; + Jrd::Attachment* const attachment = *handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); check_database(tdbb); @@ -2210,8 +2243,9 @@ ISC_STATUS GDS_DATABASE_INFO(ISC_STATUS* user_status, } +//// TODO: make this function return unsupported (deprecated) feature error ISC_STATUS GDS_DDL(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, USHORT ddl_length, const SCHAR* ddl) @@ -2229,7 +2263,7 @@ ISC_STATUS GDS_DDL(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); validateHandle(tdbb, *tra_handle); DatabaseContextHolder dbbHolder(tdbb); @@ -2240,7 +2274,8 @@ ISC_STATUS GDS_DDL(ISC_STATUS* user_status, TraceDynExecute trace(tdbb, ddl_length, (UCHAR*) ddl); try { - JRD_ddl(tdbb, /*attachment,*/ transaction, ddl_length, reinterpret_cast(ddl)); + JRD_ddl(tdbb, /*attachment,*/ transaction, ddl_length, + reinterpret_cast(ddl), ""); trace.finish(res_successful); } @@ -2260,7 +2295,7 @@ ISC_STATUS GDS_DDL(ISC_STATUS* user_status, } -ISC_STATUS GDS_DETACH(ISC_STATUS* user_status, Attachment** handle) +ISC_STATUS GDS_DETACH(ISC_STATUS* user_status, Jrd::Attachment** handle) { /************************************** * @@ -2279,12 +2314,18 @@ ISC_STATUS GDS_DETACH(ISC_STATUS* user_status, Attachment** handle) { // scope MutexLockGuard guard(databases_mutex); - Attachment* const attachment = *handle; + Jrd::Attachment* const attachment = *handle; validateHandle(tdbb, attachment); { // holder scope DatabaseContextHolder dbbHolder(tdbb); + if (attachment->att_in_use) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Attachment in use")); + } + Database* dbb = tdbb->getDatabase(); // if this is the last attachment, mark dbb as not in use @@ -2321,7 +2362,7 @@ ISC_STATUS GDS_DETACH(ISC_STATUS* user_status, Attachment** handle) } -ISC_STATUS GDS_DROP_DATABASE(ISC_STATUS* user_status, Attachment** handle) +ISC_STATUS GDS_DROP_DATABASE(ISC_STATUS* user_status, Jrd::Attachment** handle) { /************************************** * @@ -2339,10 +2380,16 @@ ISC_STATUS GDS_DROP_DATABASE(ISC_STATUS* user_status, Attachment** handle) MutexLockGuard guard(databases_mutex); - Attachment* const attachment = *handle; + Jrd::Attachment* const attachment = *handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); + if (attachment->att_in_use) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Attachment in use")); + } + Database* const dbb = tdbb->getDatabase(); const PathName& file_name = attachment->att_filename; @@ -2380,6 +2427,8 @@ ISC_STATUS GDS_DROP_DATABASE(ISC_STATUS* user_status, Attachment** handle) Arg::Gds(isc_obj_in_use) << Arg::Str("DATABASE")); } + dbb->dbb_extManager.closeAttachment(tdbb, attachment); + // Forced release of all transactions purge_transactions(tdbb, attachment, true, attachment->att_flags); @@ -2485,7 +2534,7 @@ ISC_STATUS GDS_GET_SEGMENT(ISC_STATUS* user_status, ISC_STATUS GDS_GET_SLICE(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, ISC_QUAD* array_id, USHORT /*sdl_length*/, @@ -2538,7 +2587,7 @@ ISC_STATUS GDS_GET_SLICE(ISC_STATUS* user_status, ISC_STATUS GDS_OPEN_BLOB2(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, blb** blob_handle, bid* blob_id, @@ -2651,7 +2700,7 @@ ISC_STATUS GDS_PUT_SEGMENT(ISC_STATUS* user_status, ISC_STATUS GDS_PUT_SLICE(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, ISC_QUAD* array_id, USHORT /*sdl_length*/, @@ -2695,7 +2744,7 @@ ISC_STATUS GDS_PUT_SLICE(ISC_STATUS* user_status, ISC_STATUS GDS_QUE_EVENTS(ISC_STATUS* user_status, - Attachment** handle, + Jrd::Attachment** handle, SLONG* id, SSHORT length, const UCHAR* items, @@ -2716,7 +2765,7 @@ ISC_STATUS GDS_QUE_EVENTS(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *handle; + Jrd::Attachment* const attachment = *handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); check_database(tdbb); @@ -2793,7 +2842,7 @@ ISC_STATUS GDS_RECEIVE(ISC_STATUS* user_status, ISC_STATUS GDS_RECONNECT(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, SSHORT length, const UCHAR* id) @@ -2817,7 +2866,7 @@ ISC_STATUS GDS_RECONNECT(ISC_STATUS* user_status, ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); check_database(tdbb); @@ -3405,7 +3454,8 @@ int GDS_SHUTDOWN(unsigned int timeout) } -ISC_STATUS GDS_START_MULTIPLE(ISC_STATUS* user_status, jrd_tra** tra_handle, USHORT count, TEB* vector) +ISC_STATUS GDS_START_MULTIPLE(ISC_STATUS* user_status, FB_API_HANDLE public_handle, + jrd_tra** tra_handle, USHORT count, TEB* vector) { /************************************** * @@ -3421,7 +3471,7 @@ ISC_STATUS GDS_START_MULTIPLE(ISC_STATUS* user_status, jrd_tra** tra_handle, USH { ThreadContextHolder tdbb(user_status); - JRD_start_multiple(tdbb, tra_handle, count, vector); + JRD_start_multiple(tdbb, tra_handle, count, vector, public_handle); } catch (const Exception& ex) { @@ -3432,7 +3482,8 @@ ISC_STATUS GDS_START_MULTIPLE(ISC_STATUS* user_status, jrd_tra** tra_handle, USH } -ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, jrd_tra** tra_handle, SSHORT count, ...) +ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, FB_API_HANDLE public_handle, + jrd_tra** tra_handle, SSHORT count, ...) { /************************************** * @@ -3460,7 +3511,7 @@ ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, jrd_tra** tra_handle, for (TEB* teb_iter = tebs.begin(); teb_iter < tebs.end(); teb_iter++) { - teb_iter->teb_database = va_arg(ptr, Attachment**); + teb_iter->teb_database = va_arg(ptr, Jrd::Attachment**); teb_iter->teb_tpb_length = va_arg(ptr, int); teb_iter->teb_tpb = va_arg(ptr, UCHAR*); } @@ -3469,7 +3520,7 @@ ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, jrd_tra** tra_handle, ThreadContextHolder tdbb(user_status); - JRD_start_multiple(tdbb, tra_handle, count, tebs.begin()); + JRD_start_multiple(tdbb, tra_handle, count, tebs.begin(), public_handle); } catch (const Exception& ex) { @@ -3481,7 +3532,7 @@ ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, jrd_tra** tra_handle, ISC_STATUS GDS_TRANSACT_REQUEST(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, USHORT blr_length, const SCHAR* blr, @@ -3504,7 +3555,7 @@ ISC_STATUS GDS_TRANSACT_REQUEST(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); validateHandle(tdbb, *tra_handle); DatabaseContextHolder dbbHolder(tdbb); @@ -3685,7 +3736,8 @@ ISC_STATUS GDS_UNWIND(ISC_STATUS* user_status, jrd_req** req_handle, SSHORT leve } -ISC_STATUS GDS_DSQL_ALLOCATE(ISC_STATUS* user_status, Attachment** db_handle, dsql_req** stmt_handle) +ISC_STATUS GDS_DSQL_ALLOCATE(ISC_STATUS* user_status, Jrd::Attachment** db_handle, + dsql_req** stmt_handle) { try { @@ -3696,7 +3748,7 @@ ISC_STATUS GDS_DSQL_ALLOCATE(ISC_STATUS* user_status, Attachment** db_handle, ds ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb); check_database(tdbb); @@ -3749,7 +3801,7 @@ ISC_STATUS GDS_DSQL_EXECUTE(ISC_STATUS* user_status, ISC_STATUS GDS_DSQL_EXECUTE_IMMEDIATE(ISC_STATUS* user_status, - Attachment** db_handle, + Jrd::Attachment** db_handle, jrd_tra** tra_handle, USHORT length, const TEXT* string, USHORT dialect, USHORT in_blr_length, const SCHAR* in_blr, @@ -3761,7 +3813,7 @@ ISC_STATUS GDS_DSQL_EXECUTE_IMMEDIATE(ISC_STATUS* user_status, { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); if (*tra_handle) { @@ -4040,7 +4092,7 @@ bool JRD_reschedule(thread_db* tdbb, SLONG quantum, bool punt) { // If database has been shutdown then get out - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); jrd_tra* const transaction = tdbb->getTransaction(); jrd_req* const request = tdbb->getRequest(); @@ -4176,9 +4228,9 @@ static void check_database(thread_db* tdbb) SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); - const Attachment* attach = dbb->dbb_attachments; + const Jrd::Attachment* attach = dbb->dbb_attachments; while (attach && attach != attachment) attach = attach->att_next; @@ -4262,8 +4314,13 @@ static void commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_f { prepare(tdbb, transaction, 0, NULL); } + else if (transaction->tra_in_use) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Transaction in use")); + } - const Attachment* const attachment = tdbb->getAttachment(); + const Jrd::Attachment* const attachment = tdbb->getAttachment(); if (!(attachment->att_flags & ATT_no_db_triggers) && !(transaction->tra_flags & TRA_prepared)) { @@ -4332,7 +4389,7 @@ static jrd_tra* find_transaction(thread_db* tdbb, ISC_STATUS error_code) **************************************/ SET_TDBB(tdbb); - const Attachment* const attachment = tdbb->getAttachment(); + const Jrd::Attachment* const attachment = tdbb->getAttachment(); for (jrd_tra* transaction = tdbb->getTransaction(); transaction; transaction = transaction->tra_sibling) @@ -4348,7 +4405,7 @@ static jrd_tra* find_transaction(thread_db* tdbb, ISC_STATUS error_code) } -static void find_intl_charset(thread_db* tdbb, Attachment* attachment, const DatabaseOptions* options) +static void find_intl_charset(thread_db* tdbb, Jrd::Attachment* attachment, const DatabaseOptions* options) { /************************************** * @@ -4367,7 +4424,7 @@ static void find_intl_charset(thread_db* tdbb, Attachment* attachment, const Dat if (options->dpb_lc_ctype.isEmpty()) { // No declaration of character set, act like 3.x Interbase - attachment->att_charset = DEFAULT_ATTACHMENT_CHARSET; + attachment->att_client_charset = attachment->att_charset = DEFAULT_ATTACHMENT_CHARSET; return; } @@ -4378,7 +4435,7 @@ static void find_intl_charset(thread_db* tdbb, Attachment* attachment, const Dat if (MET_get_char_coll_subtype(tdbb, &id, lc_ctype, options->dpb_lc_ctype.length()) && INTL_defined_type(tdbb, id & 0xFF) && ((id & 0xFF) != CS_BINARY)) { - attachment->att_charset = id & 0xFF; + attachment->att_client_charset = attachment->att_charset = id & 0xFF; } else { @@ -4887,6 +4944,7 @@ static Database* init(thread_db* tdbb, #ifdef ISC_DATABASE_ENCRYPTION // Lookup some external "hooks" + /*** ASF: old PluginManager PluginManager::Plugin crypt_lib = PluginManager::enginePluginManager().findPlugin(CRYPT_IMAGE); if (crypt_lib) { @@ -4895,6 +4953,7 @@ static Database* init(thread_db* tdbb, dbb->dbb_encrypt = (Database::crypt_routine) crypt_lib.lookupSymbol(encrypt_entrypoint); dbb->dbb_decrypt = (Database::crypt_routine) crypt_lib.lookupSymbol(decrypt_entrypoint); } + ***/ #endif return dbb; @@ -4994,6 +5053,12 @@ static void prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const **************************************/ SET_TDBB(tdbb); + if (transaction->tra_in_use) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Transaction in use")); + } + if (!(transaction->tra_flags & TRA_prepared)) { // run ON TRANSACTION COMMIT triggers @@ -5010,7 +5075,7 @@ static void prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const } -static void release_attachment(thread_db* tdbb, Attachment* attachment) +static void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment) { /************************************** * @@ -5029,6 +5094,8 @@ static void release_attachment(thread_db* tdbb, Attachment* attachment) if (!attachment) return; + dbb->dbb_extManager.closeAttachment(tdbb, attachment); + #ifdef SUPERSERVER if (dbb->dbb_relations) { @@ -5095,7 +5162,7 @@ static void release_attachment(thread_db* tdbb, Attachment* attachment) // remove the attachment block from the dbb linked list - for (Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next) + for (Jrd::Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next) { if (*ptr == attachment) { @@ -5114,13 +5181,13 @@ static void release_attachment(thread_db* tdbb, Attachment* attachment) delete attachment->att_user; - Attachment::destroy(attachment); // string were re-saved in the beginning of this function, - // keep that in sync please + Jrd::Attachment::destroy(attachment); // string were re-saved in the beginning of this function, + // keep that in sync please tdbb->setAttachment(NULL); } -static void detachLocksFromAttachment(Attachment* attachment) +static void detachLocksFromAttachment(Jrd::Attachment* attachment) { /************************************** * @@ -5147,7 +5214,7 @@ static void detachLocksFromAttachment(Attachment* attachment) } -bool Attachment::backupStateWriteLock(thread_db* tdbb, SSHORT wait) +bool Jrd::Attachment::backupStateWriteLock(thread_db* tdbb, SSHORT wait) { if (att_backup_state_counter++) return true; @@ -5159,13 +5226,13 @@ bool Attachment::backupStateWriteLock(thread_db* tdbb, SSHORT wait) return false; } -void Attachment::backupStateWriteUnLock(thread_db* tdbb) +void Jrd::Attachment::backupStateWriteUnLock(thread_db* tdbb) { if (--att_backup_state_counter == 0) att_database->dbb_backup_manager->unlockStateWrite(tdbb); } -bool Attachment::backupStateReadLock(thread_db* tdbb, SSHORT wait) +bool Jrd::Attachment::backupStateReadLock(thread_db* tdbb, SSHORT wait) { if (att_backup_state_counter++) return true; @@ -5177,16 +5244,17 @@ bool Attachment::backupStateReadLock(thread_db* tdbb, SSHORT wait) return false; } -void Attachment::backupStateReadUnLock(thread_db* tdbb) +void Jrd::Attachment::backupStateReadUnLock(thread_db* tdbb) { if (--att_backup_state_counter == 0) att_database->dbb_backup_manager->unlockStateRead(tdbb); } -Attachment::Attachment(MemoryPool* pool, Database* dbb) +Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, FB_API_HANDLE publicHandle) : att_pool(pool), att_memory_stats(&dbb->dbb_memory_stats), att_database(dbb), + att_public_handle(publicHandle), att_lock_owner_id(Database::getLockOwnerId()), att_backup_state_counter(0), att_stats(*pool), @@ -5194,6 +5262,7 @@ Attachment::Attachment(MemoryPool* pool, Database* dbb) att_filename(*pool), att_timestamp(TimeStamp::getCurrentTimeStamp()), att_context_vars(*pool), + ddlTriggersContext(*pool), att_network_protocol(*pool), att_remote_address(*pool), att_remote_process(*pool), @@ -5206,7 +5275,7 @@ Attachment::Attachment(MemoryPool* pool, Database* dbb) } -Attachment::~Attachment() +Jrd::Attachment::~Attachment() { delete att_trace_manager; @@ -5221,13 +5290,13 @@ Attachment::~Attachment() } -PreparedStatement* Attachment::prepareStatement(thread_db* tdbb, MemoryPool& pool, +PreparedStatement* Jrd::Attachment::prepareStatement(thread_db* tdbb, MemoryPool& pool, jrd_tra* transaction, const string& text) { return FB_NEW(pool) PreparedStatement(tdbb, pool, this, transaction, text); } -void Attachment::cancelExternalConnection(thread_db* tdbb) +void Jrd::Attachment::cancelExternalConnection(thread_db* tdbb) { if (att_ext_connection) { att_ext_connection->cancelExecution(tdbb); @@ -5247,6 +5316,12 @@ static void rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining * Abort a transaction. * **************************************/ + if (transaction->tra_in_use) + { + //// TODO: localize + status_exception::raise(Arg::Gds(isc_random) << Arg::Str("Transaction in use")); + } + ISC_STATUS_ARRAY user_status = {0}; ISC_STATUS_ARRAY local_status = {0}; ISC_STATUS* const orig_status = tdbb->tdbb_status_vector; @@ -5265,7 +5340,7 @@ static void rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining check_database(tdbb); const Database* const dbb = tdbb->getDatabase(); - const Attachment* const attachment = tdbb->getAttachment(); + const Jrd::Attachment* const attachment = tdbb->getAttachment(); if (!(attachment->att_flags & ATT_no_db_triggers)) { @@ -5464,9 +5539,9 @@ static bool shutdown_dbb(thread_db* tdbb, Database* dbb) if (!(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use | DBB_security_db)) && !((dbb->dbb_ast_flags & DBB_shutdown) && (dbb->dbb_ast_flags & DBB_shutdown_locks))) { - Attachment* att_next; + Jrd::Attachment* att_next; - for (Attachment* attach = dbb->dbb_attachments; attach; attach = att_next) + for (Jrd::Attachment* attach = dbb->dbb_attachments; attach; attach = att_next) { att_next = attach->att_next; tdbb->setAttachment(attach); @@ -5570,7 +5645,8 @@ UCHAR* JRD_num_attachments(UCHAR* const buf, USHORT buf_len, JRD_info_tag flag, dbFiles.add(dbb->dbb_filename); total += sizeof(USHORT) + dbb->dbb_filename.length(); - for (const Attachment* attach = dbb->dbb_attachments; attach; attach = attach->att_next) + for (const Jrd::Attachment* attach = dbb->dbb_attachments; attach; + attach = attach->att_next) { num_att++; @@ -5698,7 +5774,7 @@ static void ExtractDriveLetter(const TEXT* file_name, ULONG* drive_mask) static unsigned int purge_transactions(thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, const bool force_flag, const ULONG att_flags) { @@ -5759,7 +5835,7 @@ static unsigned int purge_transactions(thread_db* tdbb, } -static void purge_attachment(thread_db* tdbb, Attachment* attachment, const bool force_flag) +static void purge_attachment(thread_db* tdbb, Jrd::Attachment* attachment, const bool force_flag) { /************************************** * @@ -5877,6 +5953,8 @@ static void purge_attachment(thread_db* tdbb, Attachment* attachment, const bool shutdown_database(dbb, true); } } + + tdbb->setAttachment(NULL); } @@ -6083,7 +6161,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options) static ISC_STATUS unwindAttach(const Exception& ex, ISC_STATUS* userStatus, thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, Database* dbb) { ex.stuff_exception(userStatus); @@ -6188,6 +6266,14 @@ void thread_db::setRequest(jrd_req* val) reqStat = val ? &val->req_stats : RuntimeStatistics::getDummy(); } +SSHORT thread_db::getCharSet() +{ + if (request && request->req_charset != CS_dynamic) + return request->req_charset; + else + return attachment->att_charset; +} + void JRD_autocommit_ddl(thread_db* tdbb, jrd_tra* transaction) { @@ -6236,7 +6322,7 @@ void JRD_autocommit_ddl(thread_db* tdbb, jrd_tra* transaction) void JRD_ddl(thread_db* tdbb, /*Jrd::Attachment* attachment,*/ jrd_tra* transaction, - USHORT ddl_length, const UCHAR* ddl) + USHORT ddl_length, const UCHAR* ddl, const string& sqlText) { /************************************** * @@ -6248,7 +6334,7 @@ void JRD_ddl(thread_db* tdbb, /*Jrd::Attachment* attachment,*/ jrd_tra* transact * **************************************/ - DYN_ddl(/*attachment,*/ transaction, ddl_length, ddl); + DYN_ddl(/*attachment,*/ transaction, ddl_length, ddl, sqlText); JRD_autocommit_ddl(tdbb, transaction); } @@ -6436,7 +6522,8 @@ void JRD_start_and_send(thread_db* tdbb, jrd_req* request, jrd_tra* transaction, } -void JRD_start_multiple(thread_db* tdbb, jrd_tra** tra_handle, USHORT count, TEB* vector) +void JRD_start_multiple(thread_db* tdbb, jrd_tra** tra_handle, USHORT count, TEB* vector, + FB_API_HANDLE public_handle) { /************************************** * @@ -6468,7 +6555,7 @@ void JRD_start_multiple(thread_db* tdbb, jrd_tra** tra_handle, USHORT count, TEB for (TEB* v = vector; v < vector + count; v++) { - Attachment* attachment = *v->teb_database; + Jrd::Attachment* attachment = *v->teb_database; AutoPtr dbbHolder; if (attachment != tdbb->getAttachment()) @@ -6484,18 +6571,22 @@ void JRD_start_multiple(thread_db* tdbb, jrd_tra** tra_handle, USHORT count, TEB } transaction = TRA_start(tdbb, v->teb_tpb_length, v->teb_tpb); + transaction->tra_public_handle = public_handle; transaction->tra_sibling = prior; prior = transaction; + // Will this work for multiple databases transaction? - I suppose it will. + *tra_handle = transaction; + // run ON TRANSACTION START triggers EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_start); } - - *tra_handle = transaction; } catch (const Exception&) { + *tra_handle = NULL; + if (prior) { ThreadStatusGuard temp_status(tdbb); @@ -6539,7 +6630,7 @@ void JRD_start_transaction(thread_db* tdbb, jrd_tra** transaction, SSHORT count, for (TEB* teb_iter = tebs.begin(); teb_iter < tebs.end(); teb_iter++) { - teb_iter->teb_database = va_arg(ptr, Attachment**); + teb_iter->teb_database = va_arg(ptr, Jrd::Attachment**); teb_iter->teb_tpb_length = va_arg(ptr, int); teb_iter->teb_tpb = va_arg(ptr, UCHAR*); } @@ -6572,7 +6663,7 @@ void JRD_unwind_request(thread_db* tdbb, jrd_req* request, SSHORT level) void JRD_compile(thread_db* tdbb, - Attachment* attachment, + Jrd::Attachment* attachment, jrd_req** req_handle, ULONG blr_length, const UCHAR* blr, @@ -6652,7 +6743,7 @@ bool JRD_verify_database_access(const PathName& name) } -ISC_STATUS GDS_PING(ISC_STATUS* user_status, Attachment** db_handle) +ISC_STATUS GDS_PING(ISC_STATUS* user_status, Jrd::Attachment** db_handle) { /************************************** * @@ -6669,7 +6760,7 @@ ISC_STATUS GDS_PING(ISC_STATUS* user_status, Attachment** db_handle) { ThreadContextHolder tdbb(user_status); - Attachment* const attachment = *db_handle; + Jrd::Attachment* const attachment = *db_handle; validateHandle(tdbb, attachment); DatabaseContextHolder dbbHolder(tdbb, false); check_database(tdbb); @@ -6721,7 +6812,7 @@ void JRD_shutdown_attachments(const Database* dbb) MemoryPool& pool = *getDefaultMemoryPool(); PingQueue* const queue = FB_NEW(pool) PingQueue(pool); - for (const Attachment* attachment = dbb->dbb_attachments; + for (const Jrd::Attachment* attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) { if (attachment->att_flags & ATT_shutdown) diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 04da2f074c..d90806400e 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -51,6 +51,8 @@ #include "../jrd/os/guid.h" #include "../jrd/sbm.h" #include "../jrd/scl.h" + +#include "../jrd/ExtEngineManager.h" #ifdef DEV_BUILD #define DEBUG if (debug) DBG_supervisor(debug); @@ -147,15 +149,25 @@ public: jrd_req* request; // Compiled request. Gets filled on first invocation bool compile_in_progress; bool sys_trigger; - UCHAR type; // Trigger type + FB_UINT64 type; // Trigger type USHORT flags; // Flags as they are in RDB$TRIGGERS table jrd_rel* relation; // Trigger parent relation Firebird::MetaName name; // Trigger name + Firebird::MetaName engine; // External engine name + Firebird::string entryPoint; // External trigger entrypoint + Firebird::string extBody; // External trigger body + ExtEngineManager::Trigger* extTrigger; // External trigger + void compile(thread_db*); // Ensure that trigger is compiled void release(thread_db*); // Try to free trigger request explicit Trigger(MemoryPool& p) - : blr(p), name(p) + : blr(p), + name(p), + engine(p), + entryPoint(p), + extBody(p), + extTrigger(NULL) { dbg_blob_id.clear(); } @@ -227,19 +239,34 @@ typedef Firebird::GenericMap > > DSqlCache; +struct DdlTriggerContext +{ + DdlTriggerContext() + : ddlEvent(*getDefaultMemoryPool()), + objectName(*getDefaultMemoryPool()), + sqlText(*getDefaultMemoryPool()) + { + } + + Firebird::string ddlEvent; + Firebird::MetaName objectName; + Firebird::string sqlText; +}; + + // // the attachment block; one is created for each attachment to a database // class Attachment : public pool_alloc, public Firebird::PublicHandle { public: - static Attachment* create(Database* dbb) + static Attachment* create(Database* dbb, FB_API_HANDLE publicHandle) { MemoryPool* const pool = dbb->createPool(); try { - Attachment* const attachment = FB_NEW(*pool) Attachment(pool, dbb); + Attachment* const attachment = FB_NEW(*pool) Attachment(pool, dbb, publicHandle); pool->setStatsGroup(attachment->att_memory_stats); return attachment; } @@ -292,6 +319,7 @@ public: Firebird::MemoryStats att_memory_stats; Database* att_database; // Parent database block + FB_API_HANDLE att_public_handle; // Public handle Attachment* att_next; // Next attachment to database UserId* att_user; // User identification jrd_tra* att_transactions; // Transactions belonging to attachment @@ -309,7 +337,8 @@ public: vcl* att_counts[DBB_max_count]; RuntimeStatistics att_stats; ULONG att_flags; // Flags describing the state of the attachment - SSHORT att_charset; // user's charset specified in dpb + SSHORT att_client_charset; // user's charset specified in dpb + SSHORT att_charset; // current (client or external) attachment charset Lock* att_long_locks; // outstanding two phased locks vec* att_compatibility_table; // hash table of compatible locks vcl* att_val_errors; @@ -317,6 +346,7 @@ public: Firebird::PathName att_filename; // alias used to attach the database const Firebird::TimeStamp att_timestamp; // Connection date and time Firebird::StringMap att_context_vars; // Context variables for the connection + Firebird::Stack ddlTriggersContext; // Context variables for DDL trigger event Firebird::string att_network_protocol; // Network protocol used by client for connection Firebird::string att_remote_address; // Protocol-specific addess of remote client SLONG att_remote_pid; // Process id of remote client @@ -329,6 +359,7 @@ public: Firebird::SortedArray att_udf_pointers; dsql_dbb* att_dsql_instance; Firebird::Mutex att_mutex; // attachment mutex + bool att_in_use; // attachment in use (can't be detached or dropped) EDS::Connection* att_ext_connection; // external connection executed by this attachment TraceManager* att_trace_manager; // Trace API manager @@ -356,7 +387,7 @@ public: } private: - Attachment(MemoryPool* pool, Database* dbb); + Attachment(MemoryPool* pool, Database* dbb, FB_API_HANDLE publicHandle); ~Attachment(); }; @@ -423,8 +454,9 @@ public: // (it will usually be 0) Lock* prc_existence_lock; // existence lock, if any Firebird::MetaName prc_security_name; // security class name for procedure - Firebird::MetaName prc_name; // ascic name + Firebird::QualifiedName prc_name; // name USHORT prc_alter_count; // No. of times the procedure was altered + ExtEngineManager::Procedure* prc_external; public: explicit jrd_prc(MemoryPool& p) @@ -459,11 +491,17 @@ public: USHORT prm_number; dsc prm_desc; jrd_nod* prm_default_value; + bool prm_nullable; + prm_mech_t prm_mechanism; Firebird::MetaName prm_name; // asciiz name -//public: + Firebird::MetaName prm_field_source; + +public: explicit Parameter(MemoryPool& p) - : prm_name(p) - { } + : prm_name(p), + prm_field_source(p) + { + } }; // Index block to cache index information @@ -750,6 +788,8 @@ public: void setRequest(jrd_req* val); + SSHORT getCharSet(); + void bumpStats(const RuntimeStatistics::StatType index) { reqStat->bumpValue(index); diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index 2e8716f90e..9070eed909 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -42,7 +42,7 @@ namespace Jrd { extern "C" { -ISC_STATUS jrd8_attach_database(ISC_STATUS*, const TEXT*, Jrd::Attachment**, SSHORT, const UCHAR*); +ISC_STATUS jrd8_attach_database(ISC_STATUS*, FB_API_HANDLE, const TEXT*, Jrd::Attachment**, SSHORT, const UCHAR*); ISC_STATUS jrd8_blob_info(ISC_STATUS*, Jrd::blb**, SSHORT, const SCHAR*, SSHORT, SCHAR*); ISC_STATUS jrd8_cancel_blob(ISC_STATUS*, Jrd::blb **); ISC_STATUS jrd8_cancel_events(ISC_STATUS*, Jrd::Attachment**, SLONG *); @@ -55,7 +55,7 @@ ISC_STATUS jrd8_commit_retaining(ISC_STATUS*, Jrd::jrd_tra **); ISC_STATUS jrd8_compile_request(ISC_STATUS*, Jrd::Attachment**, Jrd::jrd_req**, SSHORT, const SCHAR*); ISC_STATUS jrd8_create_blob2(ISC_STATUS*, Jrd::Attachment**, Jrd::jrd_tra**, Jrd::blb**, Jrd::bid*, USHORT, const UCHAR*); -ISC_STATUS jrd8_create_database(ISC_STATUS*, const TEXT*, Jrd::Attachment**, USHORT, const UCHAR*); +ISC_STATUS jrd8_create_database(ISC_STATUS*, FB_API_HANDLE, const TEXT*, Jrd::Attachment**, USHORT, const UCHAR*); ISC_STATUS jrd8_database_info(ISC_STATUS*, Jrd::Attachment**, SSHORT, const SCHAR*, SSHORT, SCHAR*); ISC_STATUS jrd8_ddl(ISC_STATUS*, Jrd::Attachment**, Jrd::jrd_tra**, USHORT, const SCHAR*); @@ -90,9 +90,9 @@ ISC_STATUS jrd8_service_query(ISC_STATUS*, Jrd::Service**, ULONG*, ISC_STATUS jrd8_service_start(ISC_STATUS*, Jrd::Service**, ULONG*, USHORT, const SCHAR*); ISC_STATUS jrd8_start_and_send(ISC_STATUS*, Jrd::jrd_req**, Jrd::jrd_tra **, USHORT, USHORT, SCHAR *, SSHORT); -ISC_STATUS jrd8_start_request(ISC_STATUS*, Jrd::jrd_req**, Jrd::jrd_tra **, SSHORT); -ISC_STATUS jrd8_start_multiple(ISC_STATUS*, Jrd::jrd_tra **, USHORT, Jrd::teb *); -ISC_STATUS jrd8_start_transaction(ISC_STATUS*, Jrd::jrd_tra **, SSHORT, ...); +ISC_STATUS jrd8_start_request(ISC_STATUS*, Jrd::jrd_req**, Jrd::jrd_tra**, SSHORT); +ISC_STATUS jrd8_start_multiple(ISC_STATUS*, FB_API_HANDLE, Jrd::jrd_tra**, USHORT, Jrd::teb*); +ISC_STATUS jrd8_start_transaction(ISC_STATUS*, FB_API_HANDLE, Jrd::jrd_tra**, SSHORT, ...); ISC_STATUS jrd8_transaction_info(ISC_STATUS*, Jrd::jrd_tra**, SSHORT, const SCHAR*, SSHORT, SCHAR*); ISC_STATUS jrd8_transact_request(ISC_STATUS*, Jrd::Attachment**, Jrd::jrd_tra**, USHORT, const SCHAR*, USHORT, const SCHAR*, USHORT, SCHAR*); @@ -141,7 +141,7 @@ void JRD_print_procedure_info(Jrd::thread_db*, const char*); void JRD_autocommit_ddl(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction); void JRD_ddl(Jrd::thread_db* tdbb, /*Jrd::Attachment* attachment,*/ Jrd::jrd_tra* transaction, - USHORT ddl_length, const UCHAR* ddl); + USHORT ddl_length, const UCHAR* ddl, const Firebird::string& sqlText); void JRD_receive(Jrd::thread_db* tdbb, Jrd::jrd_req* request, USHORT msg_type, USHORT msg_length, UCHAR* msg, SSHORT level #ifdef SCROLLABLE_CURSORS @@ -158,7 +158,8 @@ void JRD_rollback_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction); void JRD_rollback_retaining(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction); void JRD_start_and_send(Jrd::thread_db* tdbb, Jrd::jrd_req* request, Jrd::jrd_tra* transaction, USHORT msg_type, USHORT msg_length, UCHAR* msg, SSHORT level); -void JRD_start_multiple(Jrd::thread_db* tdbb, Jrd::jrd_tra** tra_handle, USHORT count, Jrd::teb* vector); +void JRD_start_multiple(Jrd::thread_db* tdbb, Jrd::jrd_tra** tra_handle, USHORT count, + Jrd::teb* vector, FB_API_HANDLE public_handle = 0); void JRD_start_transaction(Jrd::thread_db* tdbb, Jrd::jrd_tra** transaction, SSHORT count, ...); void JRD_unwind_request(Jrd::thread_db* tdbb, Jrd::jrd_req* request, SSHORT level); void JRD_compile(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, Jrd::jrd_req** req_handle, diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index fc6d2eb34d..838cdb9a62 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -70,7 +70,7 @@ static void internal_dequeue(thread_db*, Lock*); static USHORT internal_downgrade(thread_db*, Lock*); static bool internal_enqueue(thread_db*, Lock*, USHORT, SSHORT, bool); -static void set_lock_attachment(Lock*, Attachment*); +static void set_lock_attachment(Lock*, Jrd::Attachment*); // globals and macros @@ -256,7 +256,7 @@ bool LCK_convert(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait) Database* dbb = lock->lck_dbb; - Attachment* const old_attachment = lock->lck_attachment; + Jrd::Attachment* const old_attachment = lock->lck_attachment; set_lock_attachment(lock, tdbb->getAttachment()); const bool result = CONVERT(tdbb, lock, level, wait); @@ -915,7 +915,7 @@ static void hash_allocate(Lock* lock) Database* dbb = lock->lck_dbb; - Attachment* attachment = lock->lck_attachment; + Jrd::Attachment* attachment = lock->lck_attachment; if (attachment) { attachment->att_compatibility_table = @@ -942,7 +942,7 @@ static Lock* hash_get_lock(Lock* lock, USHORT* hash_slot, Lock*** prior) **************************************/ fb_assert(LCK_CHECK_LOCK(lock)); - Attachment* const att = lock->lck_attachment; + Jrd::Attachment* const att = lock->lck_attachment; if (!att) return NULL; @@ -1003,7 +1003,7 @@ static void hash_insert_lock(Lock* lock) **************************************/ fb_assert(LCK_CHECK_LOCK(lock)); - Attachment* const att = lock->lck_attachment; + Jrd::Attachment* const att = lock->lck_attachment; if (!att) return; @@ -1378,7 +1378,7 @@ static bool internal_enqueue(thread_db* tdbb, } -static void set_lock_attachment(Lock* lock, Attachment* attachment) +static void set_lock_attachment(Lock* lock, Jrd::Attachment* attachment) { if (lock->lck_attachment == attachment) return; diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 91b47ac7d7..7c7c21c223 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -89,7 +89,8 @@ #include "../jrd/thread_proto.h" #include "../common/utils_proto.h" -#include "../../src/jrd/DebugInterface.h" +#include "../jrd/PreparedStatement.h" +#include "../jrd/DebugInterface.h" #include "../common/classes/MsgPrint.h" @@ -106,21 +107,23 @@ using namespace Jrd; using namespace Firebird; static int blocking_ast_dsql_cache(void* ast_object); -static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const Firebird::MetaName& name); +static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const QualifiedName& name); static int blocking_ast_procedure(void*); static int blocking_ast_relation(void*); static int partners_ast_relation(void*); -static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, UCHAR, bool, USHORT); +static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, FB_UINT64, bool, + USHORT, const Firebird::MetaName&, const Firebird::string&, const bid*); static bool get_type(thread_db*, USHORT*, const UCHAR*, const TEXT*); static void lookup_view_contexts(thread_db*, jrd_rel*); static void make_relation_scope_name(const TEXT*, const USHORT, Firebird::string& str); static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name = Firebird::MetaName()); static jrd_nod* parse_param_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*); -static jrd_nod* parse_procedure_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*); +static jrd_nod* parse_procedure_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*, bool); static void par_messages(thread_db*, const UCHAR* const, USHORT, jrd_prc*, CompilerScratch*); static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*); static void save_trigger_data(thread_db*, trig_vec**, jrd_rel*, jrd_req*, blb*, bid*, - const TEXT*, UCHAR, bool, USHORT); + const TEXT*, FB_UINT64, bool, USHORT, const Firebird::MetaName&, const Firebird::string&, + const bid*); static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*, const Firebird::MetaName&, int, jrd_tra*); static bool verify_TRG_ignore_perm(thread_db*, const Firebird::MetaName&); @@ -455,7 +458,7 @@ void MET_verify_cache(thread_db* tdbb) { char buffer[1024], *buf = buffer; buf += sprintf(buf, "Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n", - procedure->prc_id, procedure->prc_name.c_str(), procedure->prc_use_count, procedure->prc_int_use_count); + procedure->prc_id, procedure->prc_name.toString().c_str(), procedure->prc_use_count, procedure->prc_int_use_count); vec::const_iterator ptr2 = procedures->begin(); for (const vec::const_iterator end2 = procedures->end(); ptr2 < end2; ++ptr2) @@ -473,7 +476,7 @@ void MET_verify_cache(thread_db* tdbb) if (resource.rsc_type != Resource::rsc_procedure) break; if (resource.rsc_prc == procedure) { - buf += sprintf(buf, "%d:%s\n", prc->prc_id, prc->prc_name.c_str()); + buf += sprintf(buf, "%d:%s\n", prc->prc_id, prc->prc_name.toString().c_str()); } } } @@ -577,7 +580,8 @@ void MET_clear_cache(thread_db* tdbb) { CMP_release(tdbb, procedure->prc_request); procedure->prc_request = NULL; - LCK_release(tdbb, procedure->prc_existence_lock); + if (procedure->prc_existence_lock) //// TODO: verify why this IF is necessary now + LCK_release(tdbb, procedure->prc_existence_lock); procedure->prc_existence_lock = NULL; procedure->prc_flags |= PRC_obsolete; } @@ -831,7 +835,8 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND - DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME + DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME AND + PRC.RDB$PACKAGE_NAME MISSING if (!REQUEST(irq_m_fields2)) REQUEST(irq_m_fields2) = request; @@ -852,6 +857,36 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc if (!REQUEST(irq_m_fields2)) REQUEST(irq_m_fields2) = request; + request = CMP_find_request(tdbb, irq_m_fields6, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES CROSS + PRC IN RDB$PROCEDURES + WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND + DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND + (DEP.RDB$DEPENDENT_TYPE EQ obj_package_header OR + DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND + DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PACKAGE_NAME + + if (!REQUEST(irq_m_fields6)) + REQUEST(irq_m_fields6) = request; + + Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME); + + dsc desc; + desc.dsc_dtype = dtype_text; + INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE); + desc.dsc_length = proc_name.length(); + desc.dsc_address = (UCHAR*) proc_name.c_str(); + + DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_procedure, &desc, + PRC.RDB$PROCEDURE_ID, PRC.RDB$PACKAGE_NAME); + DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr); + END_FOR; + + if (!REQUEST(irq_m_fields6)) + REQUEST(irq_m_fields6) = request; + request = CMP_find_request(tdbb, irq_m_fields3, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) @@ -896,7 +931,8 @@ DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc DEP.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND DEP.RDB$DEPENDED_ON_TYPE EQ obj_relation AND DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND - DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME + DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME AND + PRC.RDB$PACKAGE_NAME MISSING if (!REQUEST(irq_m_fields4)) REQUEST(irq_m_fields4) = request; @@ -1060,9 +1096,9 @@ void MET_delete_shadow(thread_db* tdbb, USHORT shadow_number) } -bool MET_dsql_cache_use(thread_db* tdbb, int type, const Firebird::MetaName& name) +bool MET_dsql_cache_use(thread_db* tdbb, int type, const Firebird::MetaName& name, const Firebird::MetaName& package) { - DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, name); + DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(package, name)); const bool obsolete = item->obsolete; @@ -1079,9 +1115,10 @@ bool MET_dsql_cache_use(thread_db* tdbb, int type, const Firebird::MetaName& nam } -void MET_dsql_cache_release(thread_db* tdbb, int type, const Firebird::MetaName& name) +void MET_dsql_cache_release(thread_db* tdbb, int type, const Firebird::MetaName& name, + const Firebird::MetaName& package) { - DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, name); + DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(package, name)); // release lock LCK_release(tdbb, item->lock); @@ -1578,6 +1615,43 @@ void MET_load_db_triggers(thread_db* tdbb, int type) } +// Load DDL triggers from RDB$TRIGGERS. +void MET_load_ddl_triggers(thread_db* tdbb) +{ + SET_TDBB(tdbb); + + Database* dbb = tdbb->getDatabase(); + CHECK_DBB(dbb); + + if ((tdbb->getAttachment()->att_flags & ATT_no_db_triggers) || + tdbb->getDatabase()->dbb_ddl_triggers != NULL) + { + return; + } + + tdbb->getDatabase()->dbb_ddl_triggers = FB_NEW(*tdbb->getDatabase()->dbb_permanent) + trig_vec(*tdbb->getDatabase()->dbb_permanent); + + jrd_req* trigger_request = NULL; + + FOR(REQUEST_HANDLE trigger_request) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME MISSING AND + TRG.RDB$TRIGGER_INACTIVE EQ 0 + SORTED BY TRG.RDB$TRIGGER_SEQUENCE + { + if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) + { + MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME, + &tdbb->getDatabase()->dbb_ddl_triggers); + } + } + END_FOR; + + CMP_release(tdbb, trigger_request); +} + + void MET_load_trigger(thread_db* tdbb, jrd_rel* relation, const Firebird::MetaName& trigger_name, @@ -1611,27 +1685,6 @@ void MET_load_trigger(thread_db* tdbb, return; } - bid debug_blob_id; - debug_blob_id.clear(); - if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1) - { - jrd_req* debug_info_req = CMP_find_request(tdbb, irq_l_trg_dbg, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE debug_info_req) - TRG IN RDB$TRIGGERS - WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str() - - if (!REQUEST(irq_l_trg_dbg)) - REQUEST(irq_l_trg_dbg) = debug_info_req; - - if (!TRG.RDB$DEBUG_INFO.NULL) - debug_blob_id = TRG.RDB$DEBUG_INFO; - END_FOR; - - if (!REQUEST(irq_l_trg_dbg)) - REQUEST(irq_l_trg_dbg) = debug_info_req; - } - // Scan RDB$TRIGGERS next jrd_req* trigger_request = CMP_find_request(tdbb, irq_s_triggers, IRQ_REQUESTS); @@ -1656,9 +1709,31 @@ void MET_load_trigger(thread_db* tdbb, trig_flags &= ~TRG_ignore_perm; } + bid debug_blob_id; + debug_blob_id.clear(); + + bid extBodyId; + extBodyId.clear(); + + if (!TRG.RDB$DEBUG_INFO.NULL) // ODS_11_1 + debug_blob_id = TRG.RDB$DEBUG_INFO; + + Firebird::MetaName engine; + Firebird::string entryPoint; + + if (!TRG.RDB$ENGINE_NAME.NULL) // ODS_12_0 + { + engine = TRG.RDB$ENGINE_NAME; + extBodyId = TRG.RDB$TRIGGER_SOURCE; + } + + if (!TRG.RDB$ENTRYPOINT.NULL) // ODS_12_0 + entryPoint = TRG.RDB$ENTRYPOINT; + if (TRG.RDB$RELATION_NAME.NULL) { - if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) + if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB || + (TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) { // this is a database trigger get_trigger(tdbb, @@ -1667,9 +1742,12 @@ void MET_load_trigger(thread_db* tdbb, &debug_blob_id, triggers, TRG.RDB$TRIGGER_NAME, - (UCHAR) (TRG.RDB$TRIGGER_TYPE & ~TRIGGER_TYPE_DB), + TRG.RDB$TRIGGER_TYPE & ~TRIGGER_TYPE_MASK, (bool) TRG.RDB$SYSTEM_FLAG, - trig_flags); + trig_flags, + engine, + entryPoint, + &extBodyId); } } else @@ -1686,7 +1764,10 @@ void MET_load_trigger(thread_db* tdbb, TRG.RDB$TRIGGER_NAME, (UCHAR) trigger_action, (bool) TRG.RDB$SYSTEM_FLAG, - trig_flags); + trig_flags, + engine, + entryPoint, + &extBodyId); } } END_FOR; @@ -2420,7 +2501,7 @@ bool MET_lookup_partner(thread_db* tdbb, } -jrd_prc* MET_lookup_procedure(thread_db* tdbb, const Firebird::MetaName& name, bool noscan) +jrd_prc* MET_lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool noscan) { /************************************** * @@ -2471,7 +2552,9 @@ jrd_prc* MET_lookup_procedure(thread_db* tdbb, const Firebird::MetaName& name, b jrd_req* request = CMP_find_request(tdbb, irq_l_procedure, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) - P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ name.c_str() + P IN RDB$PROCEDURES + WITH P.RDB$PROCEDURE_NAME EQ name.identifier.c_str() AND + P.RDB$PACKAGE_NAME EQUIV NULLIF(name.qualifier.c_str(), '') if (!REQUEST(irq_l_procedure)) REQUEST(irq_l_procedure) = request; @@ -2819,7 +2902,7 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) if (!REQUEST (irq_s_triggers2)) REQUEST (irq_s_triggers2) = trigger_request; - const UCHAR type = (UCHAR) TRG.RDB$TRIGGER_TYPE; + const FB_UINT64 type = TRG.RDB$TRIGGER_TYPE; const USHORT trig_flags = TRG.RDB$FLAGS; const TEXT* name = TRG.RDB$TRIGGER_NAME; @@ -2878,7 +2961,8 @@ void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation) request->req_flags |= req_ignore_perm; } - save_trigger_data(tdbb, ptr, relation, request, NULL, NULL, NULL, type, true, 0); + save_trigger_data(tdbb, ptr, relation, request, NULL, NULL, NULL, type, true, 0, "", "", + NULL); } END_FOR; @@ -3049,15 +3133,31 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) REQUEST(irq_r_procedure) = request; } - if (procedure->prc_name.length() == 0) + if (procedure->prc_name.toString().length() == 0) { - procedure->prc_name = P.RDB$PROCEDURE_NAME; + procedure->prc_name = QualifiedName(P.RDB$PROCEDURE_NAME, + (P.RDB$PACKAGE_NAME.NULL ? "" : P.RDB$PACKAGE_NAME)); } procedure->prc_id = P.RDB$PROCEDURE_ID; + if (!P.RDB$SECURITY_CLASS.NULL) { procedure->prc_security_name = P.RDB$SECURITY_CLASS; } + else if (!P.RDB$PACKAGE_NAME.NULL) + { + AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ P.RDB$PACKAGE_NAME + { + if (!PKG.RDB$SECURITY_CLASS.NULL) + procedure->prc_security_name = PKG.RDB$SECURITY_CLASS; + } + END_FOR + } + if (P.RDB$SYSTEM_FLAG.NULL || !P.RDB$SYSTEM_FLAG) { procedure->prc_flags &= ~PRC_system; @@ -3084,10 +3184,14 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) jrd_req* request2 = CMP_find_request(tdbb, irq_r_params, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request2) - PA IN RDB$PROCEDURE_PARAMETERS CROSS - F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = PA.RDB$FIELD_SOURCE - AND PA.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME + MetaName packageName(P.RDB$PACKAGE_NAME.NULL ? NULL : P.RDB$PACKAGE_NAME); + + FOR (REQUEST_HANDLE request2) + PA IN RDB$PROCEDURE_PARAMETERS + CROSS F IN RDB$FIELDS + WITH F.RDB$FIELD_NAME = PA.RDB$FIELD_SOURCE AND + PA.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME AND + PA.RDB$PACKAGE_NAME EQUIV NULLIF(MetaName(packageName).c_str(), '') if (!REQUEST(irq_r_params)) { @@ -3103,10 +3207,11 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) { jrd_req* request3 = CMP_find_request(tdbb, irq_r_params2, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request3) - PA2 IN RDB$PROCEDURE_PARAMETERS WITH - PA2.RDB$PROCEDURE_NAME EQ PA.RDB$PROCEDURE_NAME AND - PA2.RDB$PARAMETER_NAME EQ PA.RDB$PARAMETER_NAME + FOR (REQUEST_HANDLE request3) + PA2 IN RDB$PROCEDURE_PARAMETERS + WITH PA2.RDB$PROCEDURE_NAME EQ PA.RDB$PROCEDURE_NAME AND + PA2.RDB$PARAMETER_NAME EQ PA.RDB$PARAMETER_NAME AND + PA2.RDB$PACKAGE_NAME EQUIV NULLIF(MetaName(packageName).c_str(), '') if (!REQUEST(irq_r_params2)) REQUEST(irq_r_params2) = request3; @@ -3133,6 +3238,12 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) parameter->prm_number = PA.RDB$PARAMETER_NUMBER; (*paramVector)[parameter->prm_number] = parameter; parameter->prm_name = PA.RDB$PARAMETER_NAME; + parameter->prm_nullable = PA.RDB$NULL_FLAG.NULL || PA.RDB$NULL_FLAG == 0; // ODS_11_1 + parameter->prm_mechanism = PA.RDB$PARAMETER_MECHANISM.NULL ? // ODS_11_1 + prm_mech_normal : (prm_mech_t) PA.RDB$PARAMETER_MECHANISM; + + if (!PA.RDB$FIELD_SOURCE.NULL) + parameter->prm_field_source = PA.RDB$FIELD_SOURCE; DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE, F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH, @@ -3192,6 +3303,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) } prc_t prc_type = prc_legacy; + bool external = !P.RDB$ENGINE_NAME.NULL; // ODS_12_0 MemoryPool* csb_pool = dbb->createPool(); @@ -3229,7 +3341,8 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) try { - parse_procedure_blr(tdbb, procedure, &P.RDB$PROCEDURE_BLR, csb); + parse_procedure_blr(tdbb, procedure, + (P.RDB$PROCEDURE_BLR.NULL ? NULL : &P.RDB$PROCEDURE_BLR), csb, external); } catch (const Firebird::Exception&) { @@ -3243,7 +3356,7 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) dbb->deletePool(csb_pool); } - ERR_post(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(procedure->prc_name)); + ERR_post(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(procedure->prc_name.toString())); } procedure->prc_request->req_procedure = procedure; @@ -3269,6 +3382,25 @@ jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags) delete csb; } + if (external) + { + HalfStaticArray body; + + if (!P.RDB$PROCEDURE_SOURCE.NULL) + { + blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &P.RDB$PROCEDURE_SOURCE); + ULONG len = BLB_get_data(tdbb, blob, + (UCHAR*) body.getBuffer(blob->blb_length + 1), blob->blb_length + 1); + body.begin()[MIN(blob->blb_length, len)] = '\0'; + } + else + body.getBuffer(1)[0] = '\0'; + + procedure->prc_external = dbb->dbb_extManager.makeProcedure( + tdbb, procedure, P.RDB$ENGINE_NAME, + (P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin()); // ODS_12_0 + } + END_FOR; if (!REQUEST(irq_r_procedure)) { @@ -3578,7 +3710,7 @@ void MET_remove_procedure(thread_db* tdbb, int id, jrd_prc* procedure) { // Fully clear procedure block. Some pieces of code check for empty // procedure name and ID, this is why we do it. - procedure->prc_name = ""; + procedure->prc_name = QualifiedName(); procedure->prc_security_name = ""; procedure->prc_defaults = 0; procedure->prc_id = 0; @@ -4166,13 +4298,19 @@ static int blocking_ast_dsql_cache(void* ast_object) } -static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const Firebird::MetaName& name) +static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const QualifiedName& name) { Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); Firebird::string key((char*) &type, sizeof(type)); - key.append(name.c_str()); + int len = name.identifier.length(); + key.append((char*) &len, sizeof(len)); + key.append(name.identifier.c_str()); + + len = name.qualifier.length(); + key.append((char*) &len, sizeof(len)); + key.append(name.qualifier.c_str()); DSqlCacheItem* item = attachment->att_dsql_cache.put(key); if (item) @@ -4316,8 +4454,10 @@ static int partners_ast_relation(void* ast_object) static void get_trigger(thread_db* tdbb, jrd_rel* relation, bid* blob_id, bid* debug_blob_id, trig_vec** ptr, - const TEXT* name, UCHAR type, - bool sys_trigger, USHORT flags) + const TEXT* name, FB_UINT64 type, + bool sys_trigger, USHORT flags, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const bid* body) { /************************************** * @@ -4331,13 +4471,16 @@ static void get_trigger(thread_db* tdbb, jrd_rel* relation, **************************************/ SET_TDBB(tdbb); - if (blob_id->isEmpty()) + if (blob_id->isEmpty() && (engine.isEmpty() || entryPoint.isEmpty())) return; - Database* dbb = tdbb->getDatabase(); - blb* blrBlob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id); + blb* blrBlob = NULL; + + if (!blob_id->isEmpty()) + blrBlob = BLB_open(tdbb, tdbb->getDatabase()->dbb_sys_trans, blob_id); + save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debug_blob_id, - name, type, sys_trigger, flags); + name, type, sys_trigger, flags, engine, entryPoint, body); } @@ -4505,10 +4648,69 @@ static jrd_nod* parse_param_blr(thread_db* tdbb, return node; } + +// Generate BLR message for external procedures +static void gen_ext_message(Firebird::UCharBuffer& blr, UCHAR message, + const vec* parameters) +{ + size_t count = (parameters ? parameters->count() : 0); + + blr.add(blr_message); // e_extproc_input_message, e_extproc_output_message + blr.add(message); + blr.add(count * 2 + message); // parameters (with nulls) + blr.add((count * 2 + message) >> 8); + + dsc shortDesc; + shortDesc.makeShort(0); + + if (!parameters) + return; + + for (Parameter* const* i = parameters->begin(); i != parameters->end(); ++i) + { + const Parameter* parameter = *i; + + if (!parameter->prm_nullable) + blr.add(blr_not_nullable); + + if (parameter->prm_mechanism == prm_mech_type_of) + PreparedStatement::generateBlr(¶meter->prm_desc, blr); + else + { + fb_assert(parameter->prm_mechanism == prm_mech_normal); + + bool isText = parameter->prm_desc.isText() || + (parameter->prm_desc.isBlob() && + parameter->prm_desc.getBlobSubType() == isc_blob_text); + + if (isText) + blr.add(blr_domain_name2); + else + blr.add(blr_domain_name); + + blr.add(blr_domain_full); + + blr.add(parameter->prm_field_source.length()); + blr.push(reinterpret_cast(parameter->prm_field_source.c_str()), + parameter->prm_field_source.length()); + + if (isText) + { + blr.add(parameter->prm_desc.getTextType()); + blr.add(parameter->prm_desc.getTextType() >> 8); + } + } + + PreparedStatement::generateBlr(&shortDesc, blr); // null flag + } +} + + static jrd_nod* parse_procedure_blr(thread_db* tdbb, jrd_prc* procedure, bid* blob_id, - CompilerScratch* csb) + CompilerScratch* csb, + bool external) { /************************************** * @@ -4523,15 +4725,70 @@ static jrd_nod* parse_procedure_blr(thread_db* tdbb, SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id); - const SLONG length = blob->blb_length + 10; - Firebird::HalfStaticArray tmp; - UCHAR* temp = tmp.getBuffer(length); - const ULONG real_len = BLB_get_data(tdbb, blob, temp, length); - fb_assert(real_len >= length - 10 && real_len <= ULONG(MAX_USHORT)); - par_messages(tdbb, temp, (USHORT) real_len, procedure, csb); + Firebird::UCharBuffer tmp; - return PAR_blr(tdbb, NULL, temp, real_len, NULL, &csb, + if (external || !blob_id) + { + tmp.add(blr_version5); + tmp.add(blr_begin); + + // ASF: Generate input messages even when number of parameters is 0 + // because we'll use hardcoded message number (0 and 1) in execution. + gen_ext_message(tmp, 0, procedure->prc_input_fields); // input message + gen_ext_message(tmp, 1, procedure->prc_output_fields); // output message + + dsc shortDesc; + shortDesc.makeShort(0); + PreparedStatement::generateBlr(&shortDesc, tmp); // end flag + + tmp.add(blr_begin); // e_extproc_input_assign + + for (USHORT i = 0; i < procedure->prc_inputs; ++i) + { + tmp.add(blr_assignment); + tmp.add(blr_parameter2); + tmp.add(0); // input message + tmp.add((i * 2)); + tmp.add((i * 2) >> 8); + tmp.add((i * 2) + 1); // null indicator + tmp.add(((i * 2) + 1) >> 8); + tmp.add(blr_null); + } + + tmp.add(blr_end); + + tmp.add(blr_begin); // e_extproc_output_assign + + for (USHORT i = 0; i < procedure->prc_outputs; ++i) + { + tmp.add(blr_assignment); + tmp.add(blr_parameter2); + tmp.add(1); // output message + tmp.add((i * 2)); + tmp.add((i * 2) >> 8); + tmp.add((i * 2) + 1); // null indicator + tmp.add(((i * 2) + 1) >> 8); + tmp.add(blr_null); + } + + tmp.add(blr_end); + + tmp.add(blr_end); + tmp.add(blr_eoc); + } + else + { + blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id); + const SLONG length = blob->blb_length + 10; + UCHAR* temp = tmp.getBuffer(length); + const ULONG realLen = BLB_get_data(tdbb, blob, temp, length); + fb_assert(realLen >= length - 10 && realLen <= ULONG(MAX_USHORT)); + tmp.resize(realLen); + } + + par_messages(tdbb, tmp.begin(), (ULONG) tmp.getCount(), procedure, csb); + + return PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &procedure->prc_request, false, 0); } @@ -4710,6 +4967,8 @@ void MET_release_triggers( thread_db* tdbb, trig_vec** vector_ptr) { CMP_release(tdbb, r); } + + delete (*vector)[i].extTrigger; } delete vector; @@ -4769,9 +5028,13 @@ static bool resolve_charset_and_collation(thread_db* tdbb, if (charset == NULL) charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME; + if (dbb->dbb_charset_ids.get((const TEXT*) charset, *id)) + return true; + USHORT charset_id = 0; if (get_type(tdbb, &charset_id, charset, "RDB$CHARACTER_SET_NAME")) { + dbb->dbb_charset_ids.put((const TEXT*) charset, charset_id); *id = charset_id; return true; } @@ -4783,7 +5046,8 @@ static bool resolve_charset_and_collation(thread_db* tdbb, FIRST 1 CS IN RDB$CHARACTER_SETS WITH CS.RDB$CHARACTER_SET_NAME EQ charset - found = true; + found = true; + dbb->dbb_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID); *id = CS.RDB$CHARACTER_SET_ID; END_FOR; @@ -4816,7 +5080,8 @@ static bool resolve_charset_and_collation(thread_db* tdbb, AND COL.RDB$COLLATION_NAME EQ collation AND AL1.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID - found = true; + found = true; + dbb->dbb_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID); *id = CS.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8); END_FOR; @@ -4828,8 +5093,10 @@ static bool resolve_charset_and_collation(thread_db* tdbb, static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation, jrd_req* request, blb* blrBlob, bid* dbgBlobID, - const TEXT* name, UCHAR type, - bool sys_trigger, USHORT flags) + const TEXT* name, FB_UINT64 type, + bool sys_trigger, USHORT flags, + const Firebird::MetaName& engine, const Firebird::string& entryPoint, + const bid* body) { /************************************** * @@ -4843,7 +5110,8 @@ static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation **************************************/ trig_vec* vector = *ptr; - if (!vector) { + if (!vector) + { vector = FB_NEW(*tdbb->getDatabase()->dbb_permanent) trig_vec(*tdbb->getDatabase()->dbb_permanent); *ptr = vector; @@ -4856,17 +5124,32 @@ static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation UCHAR* ptr2 = t.blr.getBuffer(length); t.blr.resize(BLB_get_data(tdbb, blrBlob, ptr2, length)); } + if (dbgBlobID) t.dbg_blob_id = *dbgBlobID; - if (name) { + + if (name) t.name = name; + + if (body) + { + blb* bodyBlob = BLB_open(tdbb, tdbb->getDatabase()->dbb_sys_trans, body); + + HalfStaticArray temp; + ULONG length = BLB_get_data(tdbb, bodyBlob, (UCHAR*) temp.getBuffer(bodyBlob->blb_length), + bodyBlob->blb_length); + + t.extBody = string(temp.begin(), length); } + t.type = type; t.flags = flags; t.compile_in_progress = false; t.sys_trigger = sys_trigger; t.request = request; t.relation = relation; + t.engine = engine; + t.entryPoint = entryPoint; } @@ -4932,6 +5215,7 @@ static void store_dependencies(thread_db* tdbb, jrd_rel* relation = NULL; jrd_prc* procedure = NULL; const Firebird::MetaName* dpdo_name = 0; + MetaName packageName; SubtypeInfo info; switch (dpdo_type) @@ -4972,7 +5256,8 @@ static void store_dependencies(thread_db* tdbb, break; case obj_procedure: procedure = (jrd_prc*) node->nod_arg[e_dep_object]; - dpdo_name = &procedure->prc_name; + dpdo_name = &procedure->prc_name.identifier; + packageName = procedure->prc_name.qualifier; break; case obj_collation: { @@ -5002,7 +5287,8 @@ static void store_dependencies(thread_db* tdbb, case obj_udf: { UserFunction* udf = (UserFunction*) node->nod_arg[e_dep_object]; - dpdo_name = &udf->fun_name; + dpdo_name = &udf->fun_name.identifier; + packageName = udf->fun_name.qualifier; } break; case obj_index: @@ -5078,7 +5364,8 @@ static void store_dependencies(thread_db* tdbb, X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND X.RDB$DEPENDED_ON_TYPE = dpdo_type AND X.RDB$FIELD_NAME MISSING AND - X.RDB$DEPENDENT_TYPE = dependency_type + X.RDB$DEPENDENT_TYPE = dependency_type AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '') if (!REQUEST(irq_c_deps)) REQUEST(irq_c_deps) = request; @@ -5101,14 +5388,23 @@ static void store_dependencies(thread_db* tdbb, strcpy(DEP.RDB$DEPENDENT_NAME, object_name.c_str()); DEP.RDB$DEPENDED_ON_TYPE = dpdo_type; strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name->c_str()); + if (field_name.length() > 0) { DEP.RDB$FIELD_NAME.NULL = FALSE; strcpy(DEP.RDB$FIELD_NAME, field_name.c_str()); } - else { + else DEP.RDB$FIELD_NAME.NULL = TRUE; + + if (packageName.hasData()) + { + DEP.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(DEP.RDB$PACKAGE_NAME, packageName.c_str()); } + else + DEP.RDB$PACKAGE_NAME.NULL = TRUE; + DEP.RDB$DEPENDENT_TYPE = dependency_type; END_STORE; diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 1350a469af..55c4b80cd8 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -67,8 +67,8 @@ Jrd::DeferredWork* MET_change_fields(Jrd::thread_db*, Jrd::jrd_tra*, const dsc*) Jrd::Format* MET_current(Jrd::thread_db*, Jrd::jrd_rel*); void MET_delete_dependencies(Jrd::thread_db*, const Firebird::MetaName&, int, Jrd::jrd_tra*); void MET_delete_shadow(Jrd::thread_db*, USHORT); -bool MET_dsql_cache_use(Jrd::thread_db* tdbb, int type, const Firebird::MetaName& name); -void MET_dsql_cache_release(Jrd::thread_db* tdbb, int type, const Firebird::MetaName& name); +bool MET_dsql_cache_use(Jrd::thread_db* tdbb, int type, const Firebird::MetaName& name, const Firebird::MetaName& package = ""); +void MET_dsql_cache_release(Jrd::thread_db* tdbb, int type, const Firebird::MetaName& name, const Firebird::MetaName& package = ""); void MET_error(const TEXT*, ...); Jrd::Format* MET_format(Jrd::thread_db*, Jrd::jrd_rel*, USHORT); bool MET_get_char_coll_subtype(Jrd::thread_db*, USHORT*, const UCHAR*, USHORT); @@ -80,6 +80,7 @@ Jrd::jrd_nod* MET_get_dependencies(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR*, Jrd::jrd_fld* MET_get_field(Jrd::jrd_rel*, USHORT); void MET_get_shadow_files(Jrd::thread_db*, bool); void MET_load_db_triggers(Jrd::thread_db*, int); +void MET_load_ddl_triggers(Jrd::thread_db* tdbb); void MET_load_trigger(Jrd::thread_db*, Jrd::jrd_rel*, const Firebird::MetaName&, Jrd::trig_vec**); void MET_lookup_cnstrt_for_index(Jrd::thread_db*, Firebird::MetaName& constraint, const Firebird::MetaName& index_name); void MET_lookup_cnstrt_for_trigger(Jrd::thread_db*, Firebird::MetaName&, Firebird::MetaName&, const Firebird::MetaName&); @@ -92,7 +93,7 @@ void MET_lookup_generator_id(Jrd::thread_db*, SLONG, Firebird::MetaName&); void MET_lookup_index(Jrd::thread_db*, Firebird::MetaName&, const Firebird::MetaName&, USHORT); SLONG MET_lookup_index_name(Jrd::thread_db*, const Firebird::MetaName&, SLONG*, SSHORT*); bool MET_lookup_partner(Jrd::thread_db*, Jrd::jrd_rel*, struct Jrd::index_desc*, const TEXT*); -Jrd::jrd_prc* MET_lookup_procedure(Jrd::thread_db*, const Firebird::MetaName&, bool); +Jrd::jrd_prc* MET_lookup_procedure(Jrd::thread_db*, const Firebird::QualifiedName&, bool); Jrd::jrd_prc* MET_lookup_procedure_id(Jrd::thread_db*, SSHORT, bool, bool, USHORT); Jrd::jrd_rel* MET_lookup_relation(Jrd::thread_db*, const Firebird::MetaName&); Jrd::jrd_rel* MET_lookup_relation_id(Jrd::thread_db*, SLONG, bool); diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index f185be4a12..62aea3aa61 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -437,6 +437,16 @@ int MOV_make_string2(Jrd::thread_db* tdbb, } +Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, bool limit) +{ + Jrd::MoveBuffer buffer; + UCHAR* ptr; + int len = MOV_make_string2(tdbb, desc, ttype, &ptr, buffer, limit); + + return string((const char*) ptr, len); +} + + void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to) { /************************************** diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index 5eb946fdfe..e51b389eb0 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -48,6 +48,8 @@ GDS_TIME MOV_get_sql_time(const dsc*); GDS_TIMESTAMP MOV_get_timestamp(const dsc*); int MOV_make_string(const dsc*, USHORT, const char**, vary*, USHORT); int MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, bool = true); +Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, + bool limit = true); void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*); #endif // JRD_MOV_PROTO_H diff --git a/src/jrd/names.h b/src/jrd/names.h index 9f25c7c953..562560d1f1 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -240,6 +240,15 @@ NAME("RDB$DEBUG_INFO", nam_debug_info) NAME("RDB$PARAMETER_MECHANISM", nam_prm_mechanism) NAME("RDB$SOURCE_INFO", nam_src_info) +NAME("RDB$ENGINE_NAME", nam_engine_name) + +NAME("RDB$PACKAGES", nam_packages) +NAME("RDB$PACKAGE_NAME", nam_pkg_name) +NAME("RDB$PACKAGE_HEADER_SOURCE", nam_pkg_header_source) +NAME("RDB$PACKAGE_BODY_SOURCE", nam_pkg_body_source) +NAME("RDB$PRIVATE_FLAG", nam_private_flag) +NAME("RDB$FUNCTION_SOURCE", nam_fun_source) + NAME("MON$ATTACHMENTS", nam_mon_attachments) NAME("MON$ATTACHMENT_ID", nam_mon_att_id) NAME("MON$ATTACHMENT_NAME", nam_mon_att_name) @@ -316,3 +325,4 @@ NAME("MON$TRANSACTION_ID", nam_mon_tra_id) NAME("MON$USER", nam_mon_user) NAME("MON$VARIABLE_NAME", nam_mon_var_name) NAME("MON$VARIABLE_VALUE", nam_mon_var_value) +NAME("MON$PACKAGE_NAME", nam_mon_pkg_name) diff --git a/src/jrd/nod.h b/src/jrd/nod.h index b1a49f84ec..9eb89ed8f1 100644 --- a/src/jrd/nod.h +++ b/src/jrd/nod.h @@ -201,3 +201,4 @@ NODE(nod_asn_list, asn_list, "") NODE(nod_class_node_jrd, class_node_jrd, "class_node_jrd") NODE(nod_stmt_expr, stmt_expr, "stmt_expr") NODE(nod_derived_expr, derived_expr, "derived_expr") + NODE(nod_continue_loop, continue_loop, "") diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 843ca5589b..f9f6d9d808 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -45,9 +45,11 @@ const int obj_generator = 14; const int obj_udf = 15; const int obj_blob_filter = 16; const int obj_collation = 17; +const int obj_package_header = 18; +const int obj_package_body = 19; // keep this last ! -const int obj_type_MAX = 18; +const int obj_type_MAX = 20; #endif /* JRD_OBJ_H */ diff --git a/src/jrd/ods.h b/src/jrd/ods.h index 1a2d6f90e9..178e14dfb5 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -66,6 +66,7 @@ const USHORT ODS_VERSION9 = 9; // btree leaf pages are always propagated up const USHORT ODS_VERSION10 = 10; // V6.0 features. SQL delimited idetifier, // SQLDATE, and 64-bit exact numeric type const USHORT ODS_VERSION11 = 11; // Firebird 2.x features +const USHORT ODS_VERSION12 = 12; // Firebird 3.x features // ODS minor version -- minor versions ARE compatible, but may be // increasingly functional. Add new minor versions, but leave previous @@ -111,8 +112,13 @@ const USHORT ODS_CURRENT11_1 = 1; // Firebird 2.1 features const USHORT ODS_CURRENT11_2 = 2; // Firebird 2.5 features const USHORT ODS_CURRENT11 = 2; +// Minor versions for ODS 12 + +const USHORT ODS_CURRENT12_0 = 0; /* Firebird 3.0 features */ +const USHORT ODS_CURRENT12 = 0; + // useful ODS macros. These are currently used to flag the version of the -// system triggers and system indices in ini.epp +// system triggers and system indices in ini.e inline USHORT ENCODE_ODS(USHORT major, USHORT minor) { @@ -128,6 +134,7 @@ const USHORT ODS_10_1 = ENCODE_ODS(ODS_VERSION10, 1); const USHORT ODS_11_0 = ENCODE_ODS(ODS_VERSION11, 0); const USHORT ODS_11_1 = ENCODE_ODS(ODS_VERSION11, 1); const USHORT ODS_11_2 = ENCODE_ODS(ODS_VERSION11, 2); +const USHORT ODS_12_0 = ENCODE_ODS(ODS_VERSION12, 0); const USHORT ODS_FIREBIRD_FLAG = 0x8000; @@ -146,16 +153,16 @@ inline USHORT DECODE_ODS_MINOR(USHORT ods_version) // Set current ODS major and minor version -const USHORT ODS_VERSION = ODS_VERSION11; // Current ODS major version -- always +const USHORT ODS_VERSION = ODS_VERSION12; // Current ODS major version -- always // the highest. -const USHORT ODS_RELEASED = ODS_CURRENT11_0; // The lowest stable minor version +const USHORT ODS_RELEASED = ODS_CURRENT12_0; // The lowest stable minor version // number for this ODS_VERSION! -const USHORT ODS_CURRENT = ODS_CURRENT11; // The highest defined minor version +const USHORT ODS_CURRENT = ODS_CURRENT12; // The highest defined minor version // number for this ODS_VERSION! -const USHORT ODS_CURRENT_VERSION = ODS_11_2; // Current ODS version in use which includes +const USHORT ODS_CURRENT_VERSION = ODS_12_0; // Current ODS version in use which includes // both major and minor ODS versions! diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index ecea7b3368..8e1ebb431b 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -78,6 +78,7 @@ #include "../jrd/dbg_proto.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/VirtualTable.h" +#include "../jrd/WindowRsb.h" #include "../common/classes/array.h" #include "../common/classes/objects_array.h" @@ -159,7 +160,6 @@ static jrd_nod* make_missing(thread_db*, OptimizerBlk*, jrd_rel*, jrd_nod*, USHO static jrd_nod* make_starts(thread_db*, OptimizerBlk*, jrd_rel*, jrd_nod*, USHORT, index_desc*); static bool map_equal(const jrd_nod*, const jrd_nod*, const jrd_nod*); static void mark_indices(CompilerScratch::csb_repeat*, SSHORT); -static void mark_rsb_recursive(RecordSource*); static int match_index(thread_db*, OptimizerBlk*, SSHORT, jrd_nod*, const index_desc*); static bool match_indices(thread_db*, OptimizerBlk*, SSHORT, jrd_nod*, const index_desc*); static bool node_equality(const jrd_nod*, const jrd_nod*); @@ -445,6 +445,9 @@ RecordSource* OPT_compile(thread_db* tdbb, fb_assert(local_streams[0] < MAX_STREAMS && local_streams[0] < MAX_UCHAR); local_streams[++local_streams[0]] = (UCHAR)(IPTR) node->nod_arg[e_agg_stream]; + + if (node->nod_flags & nod_window) + rsb = WindowRsb::create(tdbb, opt, rsb); } break; @@ -2393,7 +2396,7 @@ static bool dump_index(const jrd_nod* node, UCHAR** buffer_ptr, SLONG* buffer_le MoveBuffer nameBuffer; const char* namePtr = index_name.c_str(); - const CHARSET_ID charset = tdbb->getAttachment()->att_charset; + const CHARSET_ID charset = tdbb->getCharSet(); if (charset != CS_METADATA && charset != CS_NONE) { namePtr = (const char*) nameBuffer.getBuffer(DataTypeUtil(tdbb).convertLength( @@ -2471,7 +2474,7 @@ static bool dump_rsb(const jrd_req* request, if (name) { - const CHARSET_ID charset = tdbb->getAttachment()->att_charset; + const CHARSET_ID charset = tdbb->getCharSet(); if (charset != CS_METADATA && charset != CS_NONE) { nameBuffer.getBuffer(DataTypeUtil(tdbb).convertLength(length, CS_METADATA, charset)); @@ -2558,8 +2561,8 @@ static bool dump_rsb(const jrd_req* request, if (request->req_procedure || procedure->prc_request->req_fors.getCount() == 0) { - const Firebird::MetaName& n = procedure->prc_name; - const CHARSET_ID charset = tdbb->getAttachment()->att_charset; + const Firebird::string n = procedure->prc_name.toString(); + const CHARSET_ID charset = tdbb->getCharSet(); if (charset != CS_METADATA && charset != CS_NONE) { nameBuffer.getBuffer(DataTypeUtil(tdbb).convertLength(n.length(), CS_METADATA, @@ -2647,8 +2650,8 @@ static bool dump_rsb(const jrd_req* request, *buffer++ = isc_info_rsb_left_cross; break; - case rsb_virt_sequential: - *buffer++ = isc_info_rsb_virt_sequential; + case rsb_record_stream: + buffer += rsb->rsb_record_stream->dump(buffer, *buffer_length); break; default: @@ -3668,7 +3671,7 @@ static void find_rsbs(RecordSource* rsb, StreamStack* stream_list, RsbStack* rsb case rsb_navigate: case rsb_ext_sequential: case rsb_ext_indexed: - case rsb_virt_sequential: + case rsb_record_stream: // No need to go any farther down with these. stream_list->push(rsb->rsb_stream); return; @@ -3736,7 +3739,7 @@ static void find_used_streams(const RecordSource* rsb, UCHAR* streams) case rsb_sequential: case rsb_union: case rsb_recursive_union: - case rsb_virt_sequential: + case rsb_record_stream: stream = rsb->rsb_stream; found = true; break; @@ -4974,7 +4977,7 @@ static RecordSource* gen_retrieval(thread_db* tdbb, else if (relation->isVirtual()) { // Virtual - rsb = VirtualTable::optimize(tdbb, opt, stream); + rsb = VirtualTable::create(tdbb, opt, stream); } else if (opt->opt_conjuncts.getCount() || (sort_ptr && *sort_ptr) //|| (project_ptr && *project_ptr) @@ -6005,7 +6008,7 @@ static RecordSource* gen_union(thread_db* tdbb, *rsb_ptr++ = (RecordSource*)(IPTR) (csb->csb_impure - rsb->rsb_impure); *rsb_ptr = (RecordSource*) (IPTR) union_node->nod_arg[e_uni_map_stream]; - mark_rsb_recursive(rsb); + OPT_mark_rsb_recursive(rsb); } return rsb; @@ -7033,11 +7036,11 @@ static void mark_indices(CompilerScratch::csb_repeat* csb_tail, SSHORT relation_ } -static void mark_rsb_recursive(RecordSource* rsb) +void OPT_mark_rsb_recursive(RecordSource* rsb) { /************************************** * - * m a r k _ r s b _ r e c u r s i v e + * O P T _ m a r k _ r s b _ r e c u r s i v e * ************************************** * @@ -7058,10 +7061,13 @@ static void mark_rsb_recursive(RecordSource* rsb) case rsb_ext_sequential: case rsb_ext_indexed: case rsb_ext_dbkey: - case rsb_virt_sequential: case rsb_procedure: return; + case rsb_record_stream: + rsb->rsb_record_stream->markRecursive(); + return; + case rsb_first: case rsb_skip: case rsb_boolean: @@ -7075,13 +7081,13 @@ static void mark_rsb_recursive(RecordSource* rsb) RecordSource** ptr = rsb->rsb_arg; const RecordSource* const* const end = ptr + rsb->rsb_count; for (; ptr < end; ptr++) - mark_rsb_recursive(*ptr); + OPT_mark_rsb_recursive(*ptr); } return; case rsb_left_cross: - mark_rsb_recursive(rsb->rsb_arg[RSB_LEFT_outer]); - mark_rsb_recursive(rsb->rsb_arg[RSB_LEFT_inner]); + OPT_mark_rsb_recursive(rsb->rsb_arg[RSB_LEFT_outer]); + OPT_mark_rsb_recursive(rsb->rsb_arg[RSB_LEFT_inner]); return; case rsb_merge: @@ -7090,7 +7096,7 @@ static void mark_rsb_recursive(RecordSource* rsb) const RecordSource* const* const end = ptr + rsb->rsb_count * 2; for (; ptr < end; ptr += 2) - mark_rsb_recursive(*ptr); + OPT_mark_rsb_recursive(*ptr); } return; @@ -7100,13 +7106,13 @@ static void mark_rsb_recursive(RecordSource* rsb) const RecordSource* const* end = ptr + rsb->rsb_count; for (; ptr < end; ptr += 2) - mark_rsb_recursive(*ptr); + OPT_mark_rsb_recursive(*ptr); } return; case rsb_recursive_union: - mark_rsb_recursive(rsb->rsb_arg[0]); - mark_rsb_recursive(rsb->rsb_arg[2]); + OPT_mark_rsb_recursive(rsb->rsb_arg[0]); + OPT_mark_rsb_recursive(rsb->rsb_arg[2]); return; default: diff --git a/src/jrd/opt_proto.h b/src/jrd/opt_proto.h index e2b8270ef2..2e3ac0d12b 100644 --- a/src/jrd/opt_proto.h +++ b/src/jrd/opt_proto.h @@ -49,6 +49,7 @@ Jrd::jrd_nod* OPT_make_dbkey(Jrd::OptimizerBlk*, Jrd::jrd_nod*, USHORT); Jrd::jrd_nod* OPT_make_index(Jrd::thread_db*, Jrd::OptimizerBlk*, Jrd::jrd_rel*, Jrd::index_desc*); +void OPT_mark_rsb_recursive(Jrd::RecordSource* rsb); int OPT_match_index(Jrd::OptimizerBlk*, USHORT, Jrd::index_desc*); // End only exported for VMS diff --git a/src/jrd/os/darwin/mod_loader.cpp b/src/jrd/os/darwin/mod_loader.cpp index 1e172384d7..402990da71 100644 --- a/src/jrd/os/darwin/mod_loader.cpp +++ b/src/jrd/os/darwin/mod_loader.cpp @@ -80,8 +80,9 @@ ModuleLoader::Module* ModuleLoader::loadModule(const Firebird::PathName& modPath NSObjectFileImage image; /* Create an object file image from the given path */ - const NSObjectFileImageReturnCode retVal = - NSCreateObjectFileImageFromFile(modPath.c_str(), &image); + const NSObjectFileImageReturnCode retVal = modPath.hasData() ? + NSCreateObjectFileImageFromFile(modPath.c_str(), &image) : NSObjectFileImageInappropriateFile; + switch (retVal) { case NSObjectFileImageSuccess: diff --git a/src/jrd/os/path_utils.h b/src/jrd/os/path_utils.h index 1ef6320ac0..d0b5c6e2d6 100644 --- a/src/jrd/os/path_utils.h +++ b/src/jrd/os/path_utils.h @@ -48,6 +48,9 @@ public: /// String used to point to parent directory static const char* up_dir_link; + /// The directory list separator for the platform. + static const char dir_list_sep; + /** An abstract base class for iterating through the contents of a directory. Instances of this class are created using the newDirItr method of the PathUtils class. Each platform implementation is expected to diff --git a/src/jrd/os/posix/mod_loader.cpp b/src/jrd/os/posix/mod_loader.cpp index 13cd1ea53b..964c4aa8e2 100644 --- a/src/jrd/os/posix/mod_loader.cpp +++ b/src/jrd/os/posix/mod_loader.cpp @@ -65,6 +65,9 @@ bool ModuleLoader::isLoadableModule(const Firebird::PathName& module) void ModuleLoader::doctorModuleExtention(Firebird::PathName& name) { + if (name.isEmpty()) + return; + Firebird::PathName::size_type pos = name.rfind(".so"); if (pos != Firebird::PathName::npos && pos == name.length() - 3) return; // No doctoring necessary @@ -73,7 +76,7 @@ void ModuleLoader::doctorModuleExtention(Firebird::PathName& name) ModuleLoader::Module *ModuleLoader::loadModule(const Firebird::PathName& modPath) { - void* module = dlopen(modPath.c_str(), RTLD_LAZY); + void* module = dlopen(modPath.nullStr(), RTLD_LAZY); if (module == NULL) return 0; diff --git a/src/jrd/os/posix/path_utils.cpp b/src/jrd/os/posix/path_utils.cpp index 02a91e1ee9..0086e17895 100644 --- a/src/jrd/os/posix/path_utils.cpp +++ b/src/jrd/os/posix/path_utils.cpp @@ -36,6 +36,7 @@ const char PathUtils::dir_sep = '/'; const char* PathUtils::up_dir_link = ".."; +const char PathUtils::dir_list_sep = ':'; class PosixDirItr : public PathUtils::dir_iterator { diff --git a/src/jrd/os/win32/path_utils.cpp b/src/jrd/os/win32/path_utils.cpp index 6e22f15704..42ddb0136e 100644 --- a/src/jrd/os/win32/path_utils.cpp +++ b/src/jrd/os/win32/path_utils.cpp @@ -7,6 +7,7 @@ const char PathUtils::dir_sep = '\\'; const char* PathUtils::up_dir_link = ".."; +const char PathUtils::dir_list_sep = ';'; class Win32DirItr : public PathUtils::dir_iterator { diff --git a/src/jrd/pag.cpp b/src/jrd/pag.cpp index 359c147603..79810ee8b8 100644 --- a/src/jrd/pag.cpp +++ b/src/jrd/pag.cpp @@ -971,7 +971,7 @@ SLONG PAG_attachment_id(thread_db* tdbb) SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); WIN window(DB_PAGE_SPACE, -1); // If we've been here before just return the id @@ -1257,7 +1257,7 @@ void PAG_header(thread_db* tdbb, bool info) SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); fb_assert(attachment); WIN window(HEADER_PAGE_NUMBER); @@ -1385,7 +1385,7 @@ void PAG_header_init(thread_db* tdbb) SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); fb_assert(attachment); // Allocate a spare buffer which is large enough, @@ -2012,7 +2012,7 @@ void PAG_sweep_interval(thread_db* tdbb, SLONG interval) static int blocking_ast_attachment(void* ast_object) { - Attachment* const attachment = static_cast(ast_object); + Jrd::Attachment* const attachment = static_cast(ast_object); try { @@ -2452,7 +2452,7 @@ USHORT PageManager::getTempPageSpaceID(thread_db* tdbb) #else SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* att = tdbb->getAttachment(); + Jrd::Attachment* att = tdbb->getAttachment(); if (!att->att_temp_pg_lock) { Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock(); diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 135b9bd58e..0e40744727 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -90,13 +90,14 @@ static void par_dependency(thread_db*, CompilerScratch*, SSHORT, SSHORT, const F static jrd_nod* par_exec_proc(thread_db*, CompilerScratch*, SSHORT); static jrd_nod* par_fetch(thread_db*, CompilerScratch*, jrd_nod*); static jrd_nod* par_field(thread_db*, CompilerScratch*, SSHORT); -static jrd_nod* par_function(thread_db*, CompilerScratch*); +static jrd_nod* par_function(thread_db*, CompilerScratch*, SSHORT); static jrd_nod* par_literal(thread_db*, CompilerScratch*); static jrd_nod* par_map(thread_db*, CompilerScratch*, USHORT); static jrd_nod* par_message(thread_db*, CompilerScratch*); static jrd_nod* par_modify(thread_db*, CompilerScratch*, SSHORT); static USHORT par_name(CompilerScratch*, Firebird::MetaName&); static size_t par_name(CompilerScratch* csb, Firebird::string& name); +static jrd_nod* par_partition_by(thread_db*, CompilerScratch*); static jrd_nod* par_plan(thread_db*, CompilerScratch*); static jrd_nod* par_procedure(thread_db*, CompilerScratch*, SSHORT); static void par_procedure_parms(thread_db*, CompilerScratch*, jrd_prc*, jrd_nod**, jrd_nod**, bool); @@ -1149,19 +1150,24 @@ static jrd_nod* par_exec_proc(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_ jrd_prc* procedure = NULL; { - Firebird::MetaName name; + QualifiedName name; - if (blr_operator == blr_exec_pid) { + if (blr_operator == blr_exec_pid) + { const USHORT pid = csb->csb_blr_reader.getWord(); if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) - name.printf("id %d", pid); + name.identifier.printf("id %d", pid); } - else { - par_name(csb, name); + else + { + if (blr_operator == blr_exec_proc2) + par_name(csb, name.qualifier); + par_name(csb, name.identifier); procedure = MET_lookup_procedure(tdbb, name, false); } + if (!procedure) - error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name)); + error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); } jrd_nod* node = PAR_make_node(tdbb, e_esp_length); @@ -1313,10 +1319,14 @@ static jrd_nod* par_field(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_oper procedure = NULL; } - if (procedure) { + if (procedure) + { par_name(csb, name); if ((id = find_proc_field(procedure, name)) == -1) - error(csb, Arg::Gds(isc_fldnotdef2) << Arg::Str(name) << Arg::Str(procedure->prc_name)); + { + error(csb, Arg::Gds(isc_fldnotdef2) << Arg::Str(name) << + Arg::Str(procedure->prc_name.toString())); + } } else { @@ -1406,7 +1416,7 @@ static jrd_nod* par_field(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_oper } -static jrd_nod* par_function(thread_db* tdbb, CompilerScratch* csb) +static jrd_nod* par_function(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_operator) { /************************************** * @@ -1420,8 +1430,24 @@ static jrd_nod* par_function(thread_db* tdbb, CompilerScratch* csb) **************************************/ SET_TDBB(tdbb); - Firebird::MetaName name; - const USHORT count = par_name(csb, name); + const UCHAR* savePos = csb->csb_blr_reader.getPos(); + + QualifiedName name; + USHORT count = 0; + + if (blr_operator == blr_function2) + count = par_name(csb, name.qualifier); + + count += par_name(csb, name.identifier); + + if (blr_operator == blr_function && + (name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT")) + { + csb->csb_blr_reader.setPos(savePos); + jrd_nod* node = par_sys_function(tdbb, csb); + node->nod_type = nod_sys_function; + return node; + } // Isn't it strange that gbak presence means nothing to this function now? UserFunction* function = @@ -1436,29 +1462,33 @@ static jrd_nod* par_function(thread_db* tdbb, CompilerScratch* csb) } csb->csb_blr_reader.seekBackward(count); - error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name)); + error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString())); } UserFunction* homonyms; for (homonyms = function; homonyms; homonyms = homonyms->fun_homonym) { - if (homonyms->fun_entrypoint) + if (homonyms->fun_entrypoint || homonyms->fun_external) break; } if (!homonyms) + { if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment) { - warning(Arg::Warning(isc_funnotdef) << Arg::Str(name) << + warning(Arg::Warning(isc_funnotdef) << Arg::Str(name.toString()) << Arg::Warning(isc_modnotfound)); } - else { + else + { csb->csb_blr_reader.seekBackward(count); - error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name) << + error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) << Arg::Gds(isc_modnotfound)); } + } jrd_nod* node = PAR_make_node(tdbb, e_fun_length); node->nod_count = 1; + node->nod_type = nod_function; node->nod_arg[e_fun_function] = (jrd_nod*) function; node->nod_arg[e_fun_args] = par_args(tdbb, csb, VALUE); @@ -1787,6 +1817,29 @@ static size_t par_name(CompilerScratch* csb, Firebird::string& name) } +static jrd_nod* par_partition_by(thread_db* tdbb, CompilerScratch* csb) +{ +/************************************** + * + * p a r _ p a r t i t i o n _ b y + * + ************************************** + * + * Functional description + * Parse PARTITION BY subclauses of window functions. + * + **************************************/ + SET_TDBB(tdbb); + + SSHORT count = (unsigned int) csb->csb_blr_reader.getByte(); + + if (count != 0) // Support for OVER (PARTITION BY ) + status_exception::raise(Arg::Gds(isc_wish_list)); + + return NULL; +} + + static jrd_nod* par_plan(thread_db* tdbb, CompilerScratch* csb) { /************************************** @@ -2011,24 +2064,30 @@ static jrd_nod* par_procedure(thread_db* tdbb, CompilerScratch* csb, SSHORT blr_ SET_TDBB(tdbb); { - Firebird::MetaName name; + QualifiedName name; - if (blr_operator == blr_procedure) { - par_name(csb, name); + if (blr_operator == blr_procedure || blr_operator == blr_procedure2) + { + if (blr_operator == blr_procedure2) + par_name(csb, name.qualifier); + + par_name(csb, name.identifier); procedure = MET_lookup_procedure(tdbb, name, false); } - else { + else + { const SSHORT pid = csb->csb_blr_reader.getWord(); if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) - name.printf("id %d", pid); + name.identifier.printf("id %d", pid); } + if (!procedure) - error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name)); + error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); } if (procedure->prc_type == prc_executable) { - error(csb, Arg::Gds(isc_illegal_prc_type) << Arg::Str(procedure->prc_name)); + error(csb, Arg::Gds(isc_illegal_prc_type) << Arg::Str(procedure->prc_name.toString())); } jrd_nod* node = PAR_make_node(tdbb, e_prc_length); @@ -2080,7 +2139,7 @@ static void par_procedure_parms(thread_db* tdbb, if (!(tdbb->tdbb_flags & TDBB_prc_being_dropped)) { error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) << - Arg::Str(procedure->prc_name)); + Arg::Str(procedure->prc_name.toString())); } else mismatch = true; @@ -2173,7 +2232,7 @@ static void par_procedure_parms(thread_db* tdbb, else if ((input_flag ? procedure->prc_inputs : procedure->prc_outputs) && !mismatch) { error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) << - Arg::Str(procedure->prc_name)); + Arg::Str(procedure->prc_name.toString())); } } @@ -2969,17 +3028,21 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected) break; case blr_exec_proc: + case blr_exec_proc2: case blr_exec_pid: node = par_exec_proc(tdbb, csb, blr_operator); break; case blr_pid: case blr_procedure: + case blr_procedure2: node = par_procedure(tdbb, csb, blr_operator); break; case blr_function: - node = par_function(tdbb, csb); + case blr_function2: + node = par_function(tdbb, csb, blr_operator); + set_type = false; break; case blr_index: @@ -3063,6 +3126,10 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected) node = par_union(tdbb, csb, true); break; + case blr_window: + node->nod_flags = nod_window; + // fall into + case blr_aggregate: node->nod_arg[e_agg_stream] = (jrd_nod*) (IPTR) par_context(csb, 0); fb_assert((int) (IPTR)node->nod_arg[e_agg_stream] <= MAX_STREAMS); @@ -3075,6 +3142,9 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected) node = par_sort(tdbb, csb, false); return node->nod_count ? node : NULL; + case blr_partition_by: + return par_partition_by(tdbb, csb); + case blr_field: case blr_fid: node = par_field(tdbb, csb, blr_operator); @@ -3319,10 +3389,10 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected) break; case blr_leave: - node->nod_arg[0] = (jrd_nod*) (IPTR) csb->csb_blr_reader.getByte(); + case blr_continue_loop: + node->nod_arg[0] = (jrd_nod*)(IPTR) csb->csb_blr_reader.getByte(); break; - case blr_maximum: case blr_minimum: case blr_count: diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 08cb97cfcf..a168f5b654 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -170,6 +170,8 @@ RELATION(nam_trgs, rel_triggers, ODS_8_0, rel_persistent) FIELD(f_trg_flags, nam_flags, fld_flag, 1, 0, 0, 0) FIELD(f_trg_valid_blr, nam_valid_blr, fld_flag, 0, 0, 0, 0) FIELD(f_trg_debug_info, nam_debug_info, fld_debug_info, 0, 0, 0, 0) + FIELD(f_trg_engine_name, nam_engine_name, fld_engine_name, 0, 0, 0, 0) + FIELD(f_trg_entry, nam_entry, fld_ext_name, 0, 0, 0, 0) END_RELATION RELATION(nam_dpds, rel_dpds, ODS_8_0, rel_persistent) FIELD(f_dpd_name, nam_dpd_name, fld_gnr_name, 1, 0, 0, 0) @@ -177,6 +179,7 @@ RELATION(nam_dpds, rel_dpds, ODS_8_0, rel_persistent) FIELD(f_dpd_f_name, nam_f_name, fld_f_name, 1, 0, 0, 0) FIELD(f_dpd_type, nam_dpd_type, fld_obj_type, 1, 0, 0, 0) FIELD(f_dpd_o_type, nam_dpd_o_type, fld_obj_type, 1, 0, 0, 0) + FIELD(f_dpd_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) END_RELATION RELATION(nam_funs, rel_funs, ODS_8_0, rel_persistent) FIELD(f_fun_name, nam_fun_name, fld_fun_name, 1, 0, 0, 0) @@ -187,6 +190,10 @@ RELATION(nam_funs, rel_funs, ODS_8_0, rel_persistent) FIELD(f_fun_entry, nam_entry, fld_ext_name, 1, 0, 0, 0) FIELD(f_fun_ret_arg, nam_ret_arg, fld_f_position, 1, 0, 0, 0) FIELD(f_fun_sys_flag, nam_sys_flag, fld_flag, 0, 0, 0, 0) + FIELD(f_fun_engine_name, nam_engine_name, fld_engine_name, 0, 0, 0, 0) + FIELD(f_fun_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) + FIELD(f_fun_private_flag, nam_private_flag, fld_flag, 0, 0, 0, 0) + FIELD(f_fun_source, nam_fun_source, fld_source, 0, 0, 0, 0) END_RELATION RELATION(nam_args, rel_args, ODS_8_0, rel_persistent) FIELD(f_arg_fun_name, nam_fun_name, fld_fun_name, 1, 0, 0, 0) @@ -199,6 +206,7 @@ RELATION(nam_args, rel_args, ODS_8_0, rel_persistent) FIELD(f_arg_charset_id, nam_charset_id, fld_charset_id, 1, 0, 0, 0) FIELD(f_arg_precision, nam_f_precision, fld_f_precision, 1, 0, 0, 0) FIELD(f_arg_char_length, nam_char_length, fld_f_length, 1, 0, 0, 0) + FIELD(f_arg_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) END_RELATION RELATION(nam_filters, rel_filters, ODS_8_0, rel_persistent) FIELD(f_flt_name, nam_fun_name, fld_fun_name, 1, 0, 0, 0) @@ -284,6 +292,10 @@ RELATION(nam_procedures, rel_procedures, ODS_8_0, rel_persistent) FIELD(f_prc_type, nam_prc_type, fld_prc_type, 0, 0, 0, 0) FIELD(f_prc_valid_blr, nam_valid_blr, fld_flag, 0, 0, 0, 0) FIELD(f_prc_debug_info, nam_debug_info, fld_debug_info, 0, 0, 0, 0) + FIELD(f_prc_engine_name, nam_engine_name, fld_engine_name, 0, 0, 0, 0) + FIELD(f_prc_entry, nam_entry, fld_ext_name, 0, 0, 0, 0) + FIELD(f_prc_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) + FIELD(f_prc_private_flag, nam_private_flag, fld_flag, 0, 0, 0, 0) END_RELATION RELATION(nam_proc_parameters, rel_prc_prms, ODS_8_0, rel_persistent) FIELD(f_prm_name, nam_prm_name, fld_prm_name, 1, 0, 0, 0) @@ -300,6 +312,7 @@ RELATION(nam_proc_parameters, rel_prc_prms, ODS_8_0, rel_persistent) FIELD(f_prm_mech, nam_prm_mechanism, fld_mechanism, 1, 0, 0, 0) FIELD(f_prm_fname, nam_f_name, fld_f_name, 1, 0, 0, 0) FIELD(f_prm_rname, nam_r_name, fld_r_name, 1, 0, 0, 0) + FIELD(f_prm_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) END_RELATION RELATION(nam_charsets, rel_charsets, ODS_8_0, rel_persistent) FIELD(f_cs_cs_name, nam_charset_name, fld_charset_name, 1, 0, 0, 0) @@ -418,6 +431,7 @@ RELATION(nam_mon_calls, rel_mon_calls, ODS_11_1, rel_virtual) FIELD(f_mon_call_src_line, nam_mon_src_line, fld_src_info, 0, 0, 0, 0) FIELD(f_mon_call_src_column, nam_mon_src_column, fld_src_info, 0, 0, 0, 0) FIELD(f_mon_call_stat_id, nam_mon_stat_id, fld_stat_id, 0, 0, 0, 0) + FIELD(f_mon_call_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, 0, 0, 0) END_RELATION RELATION(nam_mon_io_stats, rel_mon_io_stats, ODS_11_1, rel_virtual) FIELD(f_mon_io_stat_id, nam_mon_stat_id, fld_stat_id, 0, 0, 0, 0) @@ -453,3 +467,15 @@ RELATION(nam_mon_mem_usage, rel_mon_mem_usage, ODS_11_2, rel_virtual) FIELD(f_mon_mem_max_used, nam_mon_max_used, fld_counter, 0, 0, 0, 0) FIELD(f_mon_mem_max_alloc, nam_mon_max_alloc, fld_counter, 0, 0, 0, 0) END_RELATION + +// Continue persistent tables + +RELATION(nam_packages, rel_packages, ODS_12_0, rel_persistent) + FIELD(f_pkg_name, nam_pkg_name, fld_pkg_name, 0, 0, 0, 0) + FIELD(f_pkg_header_source, nam_pkg_header_source, fld_source, 0, 0, 0, 0) + FIELD(f_pkg_body_source, nam_pkg_body_source, fld_source, 0, 0, 0, 0) + FIELD(f_pkg_class, nam_class, fld_class, 0, 0, 0, 0) + FIELD(f_pkg_owner, nam_owner, fld_user, 0, 0, 0, 0) + FIELD(f_pkg_sys_flag, nam_sys_flag, fld_flag, 0, 0, 0, 0) + FIELD(f_pkg_desc, nam_description, fld_description, 0, 0, 0, 0) +END_RELATION diff --git a/src/jrd/req.h b/src/jrd/req.h index 162694a901..17160dda80 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -33,6 +33,7 @@ #include "../jrd/exe.h" #include "../jrd/sort.h" #include "../jrd/RecordNumber.h" +#include "../jrd/ExtEngineManager.h" #include "../common/classes/stack.h" #include "../common/classes/timestamp.h" @@ -201,7 +202,7 @@ public: req_blobs(pool), req_external(*pool), req_access(*pool), req_resources(*pool), req_trg_name(*pool), req_stats(*pool), req_base_stats(*pool), req_fors(*pool), req_exec_sta(*pool), req_ext_stmt(NULL), req_invariants(*pool), - req_blr(*pool), req_domain_validation(NULL), + req_charset(CS_dynamic), req_blr(*pool), req_domain_validation(NULL), req_map_field_info(*pool), req_map_item_info(*pool), req_auto_trans(*pool), req_sorts(*pool) {} @@ -265,6 +266,7 @@ public: ULONG req_flags; // misc request flags Savepoint* req_proc_sav_point; // procedure savepoint list Firebird::TimeStamp req_timestamp; // Start time of request + SSHORT req_charset; // Client charset for this request Firebird::RefStrPtr req_sql_text; // SQL text Firebird::Array req_blr; // BLR for non-SQL query @@ -281,10 +283,14 @@ public: MapFieldInfo req_map_field_info; // Map field name to field info MapItemInfo req_map_item_info; // Map item to item info Firebird::Stack req_auto_trans; // Autonomous transactions + ExtEngineManager::ResultSet* resultSet; // external procedure result set + ValuesImpl* inputParams; // external procedure input values + ValuesImpl* outputParams; // external procedure output values SortOwner req_sorts; enum req_ta { - // order should be maintained because the numbers are stored in BLR + // Order should be maintained because the numbers are stored in BLR + // and should be in sync with ExternalTrigger::Action. req_trigger_insert = 1, req_trigger_update = 2, req_trigger_delete = 3, @@ -292,7 +298,8 @@ public: req_trigger_disconnect = 5, req_trigger_trans_start = 6, req_trigger_trans_commit = 7, - req_trigger_trans_rollback = 8 + req_trigger_trans_rollback = 8, + req_trigger_ddl = 9 } req_trigger_action; // action that caused trigger to fire enum req_s { @@ -352,6 +359,7 @@ const ULONG req_ignore_perm = 0x40000L; // ignore permissions checks const ULONG req_fetch_required = 0x80000L; // need to fetch next record const ULONG req_error_handler = 0x100000L; // looper is called to handle error const ULONG req_blr_version4 = 0x200000L; // Request is of blr_version4 +const ULONG req_continue_loop = 0x800000L; // PSQL continue statement // Mask for flags preserved in a clone of a request const ULONG REQ_FLAGS_CLONE_MASK = (req_sys_trigger | req_internal | req_ignore_perm | req_blr_version4); diff --git a/src/jrd/rse.cpp b/src/jrd/rse.cpp index 571a60617a..fc245eac09 100644 --- a/src/jrd/rse.cpp +++ b/src/jrd/rse.cpp @@ -68,6 +68,7 @@ #include "../jrd/sort_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/VirtualTable.h" +#include "../jrd/opt_proto.h" #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceJrdHelpers.h" @@ -108,7 +109,6 @@ static bool fetch_left(thread_db*, RecordSource*, IRSB); #endif static UCHAR* get_merge_data(thread_db*, merge_file*, SLONG); static bool get_procedure(thread_db*, RecordSource*, irsb_procedure*, record_param*); -static bool get_record(thread_db*, RecordSource*, RecordSource*, rse_get_mode); static bool get_union(thread_db*, RecordSource*, IRSB); static void invalidate_child_rpbs(thread_db*, RecordSource*); static void join_to_nulls(thread_db*, StreamStack*); @@ -261,8 +261,8 @@ void RSE_close(thread_db* tdbb, RecordSource* rsb) EXT_close(rsb); return; - case rsb_virt_sequential: - VirtualTable::close(tdbb, rsb); + case rsb_record_stream: + rsb->rsb_record_stream->close(tdbb); return; default: @@ -281,7 +281,7 @@ bool RSE_get_record(thread_db* tdbb, RecordSource* rsb, rse_get_mode mode) ************************************** * * Functional description - * External entrypoint for get_record(). + * External entrypoint for RSE_internal_get_record(). * Handle the "stickiness" of directionality. * Check whether we need to count records * retrieved at the top level of the rsb tree. @@ -312,7 +312,7 @@ bool RSE_get_record(thread_db* tdbb, RecordSource* rsb, rse_get_mode mode) request->req_flags &= ~req_count_records; bool result; - while ( (result = get_record(tdbb, rsb, NULL, mode)) ) + while ( (result = RSE_internal_get_record(tdbb, rsb, NULL, mode)) ) { if (rsb->rsb_flags & rsb_writelock) { @@ -432,7 +432,7 @@ void RSE_open(thread_db* tdbb, RecordSource* rsb) effect of large sequential scans on the page working sets of other attachments. */ - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (attachment && (attachment != dbb->dbb_attachments || attachment->att_next)) { /* If the relation has more data pages than the number of @@ -550,8 +550,8 @@ void RSE_open(thread_db* tdbb, RecordSource* rsb) EXT_open(tdbb, rsb); return; - case rsb_virt_sequential: - VirtualTable::open(tdbb, rsb); + case rsb_record_stream: + rsb->rsb_record_stream->open(tdbb); return; case rsb_left_cross: @@ -634,6 +634,7 @@ static void close_procedure(thread_db* tdbb, RecordSource* rsb) jrd_req* request = tdbb->getRequest(); irsb_procedure* impure = (irsb_procedure*) ((UCHAR *) request + rsb->rsb_impure); jrd_req* proc_request = impure->irsb_req_handle; + if (proc_request) { EXE_unwind(tdbb, proc_request); proc_request->req_flags &= ~req_in_use; @@ -752,7 +753,7 @@ static bool fetch_record(thread_db* tdbb, RecordSource* rsb, USHORT n RecordSource* sub_rsb = rsb->rsb_arg[n]; - if (get_record(tdbb, sub_rsb, NULL + if (RSE_internal_get_record(tdbb, sub_rsb, NULL #ifdef SCROLLABLE_CURSORS , mode #else @@ -780,7 +781,7 @@ static bool fetch_record(thread_db* tdbb, RecordSource* rsb, USHORT n } RSE_open(tdbb, sub_rsb); - if (get_record(tdbb, sub_rsb, NULL + if (RSE_internal_get_record(tdbb, sub_rsb, NULL #ifdef SCROLLABLE_CURSORS , mode #else @@ -830,7 +831,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure, rse_get_ if (impure->irsb_flags & irsb_mustread) { - if (!get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, mode)) { if (mode == RSE_get_backward) return false; @@ -865,7 +866,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure, rse_get_ /* fetch records from the inner stream until exhausted */ - while (get_record(tdbb, rsb->rsb_arg[RSB_LEFT_inner], NULL, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_inner], NULL, mode)) { if (!rsb->rsb_arg[RSB_LEFT_inner_boolean] || EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[RSB_LEFT_inner_boolean])) @@ -902,7 +903,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure, rse_get_ find the records that haven't been. */ bool found; do { - if (!get_record(tdbb, full, NULL, mode)) { + if (!RSE_internal_get_record(tdbb, full, NULL, mode)) { if (mode == RSE_get_forward) return false; @@ -910,7 +911,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure, rse_get_ } RSE_open(tdbb, rsb->rsb_arg[RSB_LEFT_outer]); - while (found = get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, mode)) + while (found = RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, mode)) { if ( (!rsb->rsb_arg[RSB_LEFT_boolean] || @@ -926,7 +927,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure, rse_get_ RSE_close(tdbb, rsb->rsb_arg[RSB_LEFT_outer]); } while (found); } - else if (!get_record(tdbb, full, NULL, mode)) + else if (!RSE_internal_get_record(tdbb, full, NULL, mode)) { if (mode == RSE_get_forward) return false; @@ -980,7 +981,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure) { if (impure->irsb_flags & irsb_mustread) { - if (!get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, RSE_get_forward)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, RSE_get_forward)) { if (rsb->rsb_left_inner_streams->isEmpty()) return false; @@ -1006,7 +1007,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure) RSE_open(tdbb, rsb->rsb_arg[RSB_LEFT_inner]); } - while (get_record(tdbb, rsb->rsb_arg[RSB_LEFT_inner], NULL, RSE_get_forward)) + while (RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_inner], NULL, RSE_get_forward)) { if (!rsb->rsb_arg[RSB_LEFT_inner_boolean] || EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[RSB_LEFT_inner_boolean])) @@ -1040,10 +1041,10 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure) find the records that haven't been. */ bool found; do { - if (!get_record(tdbb, full, NULL, RSE_get_forward)) + if (!RSE_internal_get_record(tdbb, full, NULL, RSE_get_forward)) return false; RSE_open(tdbb, rsb->rsb_arg[RSB_LEFT_outer]); - while ( (found = get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, RSE_get_forward)) ) + while ( (found = RSE_internal_get_record(tdbb, rsb->rsb_arg[RSB_LEFT_outer], NULL, RSE_get_forward)) ) { if ((!rsb->rsb_arg[RSB_LEFT_boolean] || EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[RSB_LEFT_boolean])) && @@ -1058,7 +1059,7 @@ static bool fetch_left(thread_db* tdbb, RecordSource* rsb, IRSB impure) RSE_close(tdbb, rsb->rsb_arg[RSB_LEFT_outer]); } while (found); } - else if (!get_record(tdbb, full, NULL, RSE_get_forward)) + else if (!RSE_internal_get_record(tdbb, full, NULL, RSE_get_forward)) return false; join_to_nulls(tdbb, rsb->rsb_left_inner_streams); @@ -1725,6 +1726,24 @@ static bool get_procedure(thread_db* tdbb, TraceProcFetch trace(tdbb, proc_request); try { EXE_receive(tdbb, proc_request, 1, oml, om); + + dsc desc = msg_format->fmt_desc[msg_format->fmt_count - 1]; + desc.dsc_address = (UCHAR*) (om + (IPTR) desc.dsc_address); + USHORT eos; + dsc eos_desc; + eos_desc.dsc_dtype = dtype_short; + eos_desc.dsc_scale = 0; + eos_desc.dsc_length = sizeof(SSHORT); + eos_desc.dsc_sub_type = 0; + eos_desc.dsc_flags = 0; + eos_desc.dsc_address = (UCHAR*) &eos; + MOV_move(tdbb, &desc, &eos_desc); + + if (!eos) + { + trace.fetch(true, res_successful); + return false; + } } catch (const Firebird::Exception&) { @@ -1733,23 +1752,6 @@ static bool get_procedure(thread_db* tdbb, throw; } - dsc desc = msg_format->fmt_desc[msg_format->fmt_count - 1]; - desc.dsc_address = (UCHAR*) (om + (IPTR) desc.dsc_address); - USHORT eos; - dsc eos_desc; - eos_desc.dsc_dtype = dtype_short; - eos_desc.dsc_scale = 0; - eos_desc.dsc_length = sizeof(SSHORT); - eos_desc.dsc_sub_type = 0; - eos_desc.dsc_flags = 0; - eos_desc.dsc_address = (UCHAR *) & eos; - MOV_move(tdbb, &desc, &eos_desc); - if (!eos) - { - trace.fetch(true, res_successful); - return false; - } - for (int i = 0; i < rec_format->fmt_count; i++) { proc_assignment(tdbb, @@ -1766,14 +1768,14 @@ static bool get_procedure(thread_db* tdbb, } -static bool get_record(thread_db* tdbb, - RecordSource* rsb, - RecordSource* parent_rsb, - rse_get_mode mode) +bool RSE_internal_get_record(thread_db* tdbb, + RecordSource* rsb, + RecordSource* parent_rsb, + rse_get_mode mode) { /************************************** * - * g e t _ r e c o r d + * R S E _ i n t e r n a l _ g e t _ r e c o r d * ************************************** * @@ -1960,7 +1962,7 @@ static bool get_record(thread_db* tdbb, any_null = false; any_true = false; - while (get_record(tdbb, rsb->rsb_next, rsb, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, rsb, mode)) { if (EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[0])) { @@ -2022,7 +2024,7 @@ static bool get_record(thread_db* tdbb, ANY is true */ result = false; - while (get_record(tdbb, rsb->rsb_next, rsb, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, rsb, mode)) { if (EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[0])) { result = true; @@ -2051,7 +2053,7 @@ static bool get_record(thread_db* tdbb, NOT ALL is true */ any_false = false; - while (get_record(tdbb, rsb->rsb_next, rsb, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, rsb, mode)) { request->req_flags &= ~req_null; @@ -2100,7 +2102,7 @@ static bool get_record(thread_db* tdbb, ALL is true */ any_false = false; - while (get_record(tdbb, rsb->rsb_next, rsb, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, rsb, mode)) { request->req_flags &= ~req_null; @@ -2142,7 +2144,7 @@ static bool get_record(thread_db* tdbb, bool flag = false; result = false; - while (get_record(tdbb, rsb->rsb_next, rsb, mode)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, rsb, mode)) { if (EVL_boolean(tdbb, (jrd_nod*) rsb->rsb_arg[0])) { result = true; @@ -2187,7 +2189,7 @@ static bool get_record(thread_db* tdbb, return false; } ((irsb_first_n*) impure)->irsb_count--; - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; @@ -2198,13 +2200,13 @@ static bool get_record(thread_db* tdbb, invalidate_child_rpbs(tdbb, rsb); return false; } - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; case RSE_get_backward: ((irsb_first_n*) impure)->irsb_count++; - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; #endif @@ -2227,12 +2229,12 @@ static bool get_record(thread_db* tdbb, if (skip->irsb_count == 0) { skip->irsb_count++; - if (get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) invalidate_child_rpbs(tdbb, rsb); return false; } skip->irsb_count++; - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; @@ -2242,7 +2244,7 @@ static bool get_record(thread_db* tdbb, invalidate_child_rpbs(tdbb, rsb); return false; } - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; #endif @@ -2250,11 +2252,11 @@ static bool get_record(thread_db* tdbb, case RSE_get_forward: while (skip->irsb_count > 1) { skip->irsb_count--; - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; } skip->irsb_count--; - if (!get_record(tdbb, rsb->rsb_next, NULL, mode)) + if (!RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, mode)) return false; break; } @@ -2396,8 +2398,8 @@ static bool get_record(thread_db* tdbb, rpb->rpb_number.setValid(true); break; - case rsb_virt_sequential: - if (!VirtualTable::get(tdbb, rsb)) + case rsb_record_stream: + if (!rsb->rsb_record_stream->get(tdbb)) { rpb->rpb_number.setValid(false); return false; @@ -2435,7 +2437,7 @@ static bool get_record(thread_db* tdbb, { push_rpbs(tdbb, request, rsb); impure->irsb_flags |= irsb_checking_singular; - if (get_record(tdbb, rsb, parent_rsb, mode)) { + if (RSE_internal_get_record(tdbb, rsb, parent_rsb, mode)) { impure->irsb_flags &= ~irsb_checking_singular; ERR_post(Arg::Gds(isc_sing_select_err)); } @@ -2501,7 +2503,7 @@ static bool get_union(thread_db* tdbb, RecordSource* rsb, IRSB impure) /* March thru the sub-streams (tributaries?) looking for a record */ - while (!get_record(tdbb, *rsb_ptr, NULL, RSE_get_forward)) + while (!RSE_internal_get_record(tdbb, *rsb_ptr, NULL, RSE_get_forward)) { RSE_close(tdbb, *rsb_ptr); impure->irsb_count += 2; @@ -2550,7 +2552,7 @@ static void invalidate_child_rpbs(thread_db* tdbb, RecordSource* rsb) case rsb_ext_sequential: case rsb_ext_indexed: case rsb_ext_dbkey: - case rsb_virt_sequential: + case rsb_record_stream: case rsb_procedure: rpb->rpb_number.setValid(false); return; @@ -2905,7 +2907,7 @@ static void open_sort(thread_db* tdbb, RecordSource* rsb, irsb_sort* impure) //, dsc to, temp; - while (get_record(tdbb, rsb->rsb_next, NULL, RSE_get_forward)) + while (RSE_internal_get_record(tdbb, rsb->rsb_next, NULL, RSE_get_forward)) { records++; @@ -3138,7 +3140,7 @@ static void pop_rpbs(jrd_req* request, RecordSource* rsb) case rsb_union: case rsb_recursive_union: case rsb_aggregate: - case rsb_virt_sequential: + case rsb_record_stream: { record_param* rpb = &request->req_rpb[rsb->rsb_stream]; restore_record(rpb); @@ -3253,7 +3255,7 @@ static void push_rpbs(thread_db* tdbb, jrd_req* request, RecordSource* rsb) case rsb_union: case rsb_recursive_union: case rsb_aggregate: - case rsb_virt_sequential: + case rsb_record_stream: { record_param* rpb = &request->req_rpb[rsb->rsb_stream]; save_record(tdbb, rpb); @@ -3689,7 +3691,7 @@ bool RSBRecurse::get(thread_db* tdbb, RecordSource* rsb, irsb_recurse* irsb) // Get the data -- if there is none go back one level and when // there isn't a previous level, we're done - while (!get_record(tdbb, *rsb_ptr, NULL, RSE_get_forward)) + while (!RSE_internal_get_record(tdbb, *rsb_ptr, NULL, RSE_get_forward)) { if (irsb->irsb_level == 1) { diff --git a/src/jrd/rse.h b/src/jrd/rse.h index 3006e8c0c8..153dbc677a 100644 --- a/src/jrd/rse.h +++ b/src/jrd/rse.h @@ -37,6 +37,7 @@ #include "../jrd/lls.h" #include "../jrd/sbm.h" +#include "../jrd/ExtEngineManager.h" #include "../jrd/RecordBuffer.h" struct dsc; @@ -52,6 +53,7 @@ struct sort_context; class CompilerScratch; class jrd_prc; class Format; +class RecordStream; class VaryingStr; class BtrPageGCLock; @@ -75,7 +77,7 @@ enum rsb_t rsb_navigate, // navigational walk on an index rsb_left_cross, // left outer join as a nested loop rsb_procedure, // stored procedure - rsb_virt_sequential, // sequential access to a virtual table + rsb_record_stream, // RecordStream class rsb_recursive_union // Recursive union }; @@ -111,6 +113,7 @@ public: StreamStack* rsb_left_streams; RsbStack* rsb_left_rsbs; VarInvariantArray *rsb_invariants; /* Invariant nodes bound to top-level RSB */ + RecordStream* rsb_record_stream; RecordSource* rsb_arg[1]; }; diff --git a/src/jrd/rse_proto.h b/src/jrd/rse_proto.h index 96d4291f7f..742d979799 100644 --- a/src/jrd/rse_proto.h +++ b/src/jrd/rse_proto.h @@ -35,6 +35,8 @@ namespace Jrd { void RSE_close(Jrd::thread_db*, Jrd::RecordSource*); bool RSE_get_record(Jrd::thread_db*, Jrd::RecordSource*, Jrd::rse_get_mode); +bool RSE_internal_get_record(Jrd::thread_db*, Jrd::RecordSource*, Jrd::RecordSource*, + Jrd::rse_get_mode); void RSE_open(Jrd::thread_db*, Jrd::RecordSource*); #endif // JRD_RSE_PROTO_H diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 1e277594f7..834b884360 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -73,9 +73,9 @@ static bool check_number(const UCHAR*, USHORT); static bool check_user_group(thread_db* tdbb, const UCHAR*, USHORT); static bool check_string(const UCHAR*, const Firebird::MetaName&); static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClass*, - const jrd_rel*, const Firebird::MetaName&, const Firebird::MetaName&); + const jrd_rel*, const Firebird::MetaName&, const Firebird::MetaName&, const Firebird::MetaName&); static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl&, const jrd_rel*, - const Firebird::MetaName&, const Firebird::MetaName&); + const Firebird::MetaName&, const Firebird::MetaName&, const Firebird::MetaName&); static inline void check_and_move(UCHAR from, Acl& to) { @@ -110,6 +110,7 @@ void SCL_check_access(thread_db* tdbb, SLONG view_id, const Firebird::MetaName& trg_name, const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name, SecurityClass::flags_t mask, const TEXT* type, const char* name) @@ -135,7 +136,7 @@ void SCL_check_access(thread_db* tdbb, Arg::Str(s_class->scl_name)); } - const Attachment& attachment = *tdbb->getAttachment(); + const Jrd::Attachment& attachment = *tdbb->getAttachment(); // Allow the database owner to back up a database even if he does not have // read access to all the tables in the database @@ -168,8 +169,8 @@ void SCL_check_access(thread_db* tdbb, if (view_id) { view = MET_lookup_relation_id(tdbb, view_id, false); } - if ((view || trg_name.length() || prc_name.length()) && - (compute_access(tdbb, s_class, view, trg_name, prc_name) & mask)) + if ((view || trg_name.hasData() || prc_name.hasData() || pkg_name.hasData()) && + (compute_access(tdbb, s_class, view, trg_name, prc_name, pkg_name) & mask)) { return; } @@ -202,6 +203,7 @@ void SCL_check_access(thread_db* tdbb, SLONG view_id, const Firebird::MetaName& trg_name, const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name, SecurityClass::flags_t mask, const TEXT* type, const Firebird::MetaName& name, @@ -229,7 +231,8 @@ void SCL_check_access(thread_db* tdbb, fullFieldName += name.c_str(); } - SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, mask, type, fullFieldName.c_str()); + SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, pkg_name, mask, type, + fullFieldName.c_str()); } @@ -314,7 +317,7 @@ void SCL_check_index(thread_db* tdbb, const Firebird::MetaName& index_name, UCHA return; } - SCL_check_access(tdbb, s_class, 0, NULL, NULL, mask, object_table, reln_name); + SCL_check_access(tdbb, s_class, 0, NULL, NULL, NULL, mask, object_table, reln_name); request = NULL; @@ -341,12 +344,15 @@ void SCL_check_index(thread_db* tdbb, const Firebird::MetaName& index_name, UCHA fullFieldName += '.'; fullFieldName += RF.RDB$FIELD_NAME; fullFieldName.rtrim(); - if (!RF.RDB$SECURITY_CLASS.NULL) { + if (!RF.RDB$SECURITY_CLASS.NULL) + { s_class = SCL_get_class(tdbb, RF.RDB$SECURITY_CLASS); - SCL_check_access(tdbb, s_class, 0, NULL, NULL, mask, object_column, fullFieldName); + SCL_check_access(tdbb, s_class, 0, NULL, NULL, NULL, mask, object_column, fullFieldName); } - else { - SCL_check_access(tdbb, default_s_class, 0, NULL, NULL, mask, object_column, fullFieldName); + else + { + SCL_check_access(tdbb, default_s_class, 0, NULL, NULL, NULL, mask, object_column, + fullFieldName); } END_FOR; @@ -362,6 +368,50 @@ void SCL_check_index(thread_db* tdbb, const Firebird::MetaName& index_name, UCHA } +void SCL_check_package(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) +{ +/************************************** + * + * S C L _ c h e c k _ p a c k a g e + * + ************************************** + * + * Functional description + * Given a package name, check for a set of privileges. The + * package in question may or may not have been created, let alone + * scanned. This is used exclusively for meta-data operations. + * + **************************************/ + SET_TDBB(tdbb); + + // Get the name in CSTRING format, ending on NULL or SPACE + fb_assert(dsc_name->dsc_dtype == dtype_text); + const Firebird::MetaName name(reinterpret_cast(dsc_name->dsc_address), + dsc_name->dsc_length); + + Database* dbb = tdbb->getDatabase(); + const SecurityClass* s_class = NULL; + + jrd_req* request = CMP_find_request(tdbb, irq_pkg_security, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE request) + PKG IN RDB$PACKAGES + WITH PKG.RDB$PACKAGE_NAME EQ name.c_str() + + if (!REQUEST(irq_pkg_security)) + REQUEST(irq_pkg_security) = request; + + if (!PKG.RDB$SECURITY_CLASS.NULL) + s_class = SCL_get_class(tdbb, PKG.RDB$SECURITY_CLASS); + END_FOR; + + if (!REQUEST(irq_pkg_security)) + REQUEST(irq_pkg_security) = request; + + SCL_check_access(tdbb, s_class, 0, NULL, NULL, name, mask, object_package, name); +} + + void SCL_check_procedure(thread_db* tdbb, const dsc* dsc_name, SecurityClass::flags_t mask) { /************************************** @@ -388,8 +438,10 @@ void SCL_check_procedure(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fl jrd_req* request = CMP_find_request(tdbb, irq_p_security, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request) SPROC IN RDB$PROCEDURES - WITH SPROC.RDB$PROCEDURE_NAME EQ name.c_str() + FOR (REQUEST_HANDLE request) + SPROC IN RDB$PROCEDURES + WITH SPROC.RDB$PROCEDURE_NAME EQ name.c_str() AND + SPROC.RDB$PACKAGE_NAME MISSING if (!REQUEST(irq_p_security)) REQUEST(irq_p_security) = request; @@ -401,7 +453,7 @@ void SCL_check_procedure(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fl if (!REQUEST(irq_p_security)) REQUEST(irq_p_security) = request; - SCL_check_access(tdbb, s_class, 0, NULL, name, mask, object_procedure, name); + SCL_check_access(tdbb, s_class, 0, NULL, name, NULL, mask, object_procedure, name); } @@ -445,7 +497,7 @@ void SCL_check_relation(thread_db* tdbb, const dsc* dsc_name, SecurityClass::fla if (!REQUEST(irq_v_security)) REQUEST(irq_v_security) = request; - SCL_check_access(tdbb, s_class, 0, NULL, NULL, mask, object_table, name); + SCL_check_access(tdbb, s_class, 0, NULL, NULL, NULL, mask, object_table, name); } @@ -480,7 +532,7 @@ SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) return NULL; } - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); // Look for the class already known @@ -493,7 +545,7 @@ SecurityClass* SCL_get_class(thread_db* tdbb, const TEXT* par_string) MemoryPool& pool = *attachment->att_pool; SecurityClass* const s_class = FB_NEW(pool) SecurityClass(pool, string); - s_class->scl_flags = compute_access(tdbb, s_class, NULL, NULL, NULL); + s_class->scl_flags = compute_access(tdbb, s_class, NULL, NULL, NULL, NULL); if (s_class->scl_flags & SCL_exists) { @@ -527,7 +579,7 @@ SecurityClass::flags_t SCL_get_mask(thread_db* tdbb, const TEXT* relation_name, * **************************************/ SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); // Start with database security class @@ -699,7 +751,7 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) role_name = NULL_ROLE; } - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); MemoryPool& pool = *attachment->att_pool; UserId* const user = FB_NEW(pool) UserId(pool, tempId); user->usr_sql_role_name = role_name.c_str(); @@ -802,7 +854,7 @@ SecurityClass* SCL_recompute_class(thread_db* tdbb, const TEXT* string) return NULL; } - s_class->scl_flags = compute_access(tdbb, s_class, NULL, NULL, NULL); + s_class->scl_flags = compute_access(tdbb, s_class, NULL, NULL, NULL, NULL); if (s_class->scl_flags & SCL_exists) { return s_class; @@ -993,7 +1045,8 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClass* s_class, const jrd_rel* view, const Firebird::MetaName& trg_name, - const Firebird::MetaName& prc_name) + const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name) { /************************************** * @@ -1047,7 +1100,7 @@ static SecurityClass::flags_t compute_access(thread_db* tdbb, if (acl.getCount() > 0) { - privileges |= walk_acl(tdbb, acl, view, trg_name, prc_name); + privileges |= walk_acl(tdbb, acl, view, trg_name, prc_name, pkg_name); } END_FOR; @@ -1062,7 +1115,8 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, const Acl& acl, const jrd_rel* view, const Firebird::MetaName& trg_name, - const Firebird::MetaName& prc_name) + const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name) { /************************************** * @@ -1182,6 +1236,11 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, hit = false; break; + case id_package: + if (check_string(a, pkg_name)) + hit = false; + break; + case id_procedure: if (check_string(a, prc_name)) hit = false; diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 7bd0868fc2..aba2c0fcdb 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -146,6 +146,7 @@ public: const char* const object_table = "TABLE"; const char* const object_procedure = "PROCEDURE"; const char* const object_column = "COLUMN"; +const char* const object_package = "PACKAGE"; } //namespace Jrd diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 7b4239d5f7..4ebd7906f3 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -34,34 +34,39 @@ struct dsc; void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, const Firebird::MetaName&, - const Firebird::MetaName&, Jrd::SecurityClass::flags_t, const TEXT*, const char*); + const Firebird::MetaName&, const Firebird::MetaName&, + Jrd::SecurityClass::flags_t, const TEXT*, const char*); void SCL_check_access(Jrd::thread_db*, const Jrd::SecurityClass*, SLONG, const Firebird::MetaName&, - const Firebird::MetaName&, Jrd::SecurityClass::flags_t, - const TEXT*, const Firebird::MetaName&, const Firebird::MetaName&); + const Firebird::MetaName&, const Firebird::MetaName&, + Jrd::SecurityClass::flags_t, const TEXT*, const Firebird::MetaName&, + const Firebird::MetaName&); inline void SCL_check_access(Jrd::thread_db* tdbb, const Jrd::SecurityClass* s_class, SLONG view_id, const Firebird::MetaName& trg_name, const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name, Jrd::SecurityClass::flags_t mask, const TEXT* type, const Firebird::string& name) { - SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, mask, type, name.c_str()); + SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, pkg_name, mask, type, name.c_str()); } inline void SCL_check_access(Jrd::thread_db* tdbb, const Jrd::SecurityClass* s_class, SLONG view_id, const Firebird::MetaName& trg_name, const Firebird::MetaName& prc_name, + const Firebird::MetaName& pkg_name, Jrd::SecurityClass::flags_t mask, const TEXT* type, const Firebird::MetaName& name) { - SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, mask, type, name.c_str()); + SCL_check_access(tdbb, s_class, view_id, trg_name, prc_name, pkg_name, mask, type, name.c_str()); } void SCL_check_index(Jrd::thread_db*, const Firebird::MetaName&, UCHAR, Jrd::SecurityClass::flags_t); +void SCL_check_package(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); void SCL_check_procedure(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); void SCL_check_relation(Jrd::thread_db* tdbb, const dsc*, Jrd::SecurityClass::flags_t); Jrd::SecurityClass* SCL_get_class(Jrd::thread_db*, const TEXT*); diff --git a/src/jrd/shut.cpp b/src/jrd/shut.cpp index 22362bffa7..55718bd1ff 100644 --- a/src/jrd/shut.cpp +++ b/src/jrd/shut.cpp @@ -143,7 +143,7 @@ void SHUT_database(thread_db* tdbb, SSHORT flag, SSHORT delay) **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); /* Only platform's user locksmith can shutdown or bring online a database. */ @@ -341,7 +341,7 @@ void SHUT_online(thread_db* tdbb, SSHORT flag) **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); /* Only platform's user locksmith can shutdown or bring online a database. */ @@ -549,7 +549,7 @@ static bool shutdown_locks(thread_db* tdbb, SSHORT flag) fb_assert(false); } - Attachment* attachment; + Jrd::Attachment* attachment; for (attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) { @@ -577,7 +577,7 @@ static bool shutdown_locks(thread_db* tdbb, SSHORT flag) /* Since no attachment is actively running, release all attachment-specfic locks while they're not looking. */ - const Attachment* shut_attachment = NULL; + const Jrd::Attachment* shut_attachment = NULL; for (attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) { diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 65d2321ef0..1a2adb1a37 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -286,7 +286,8 @@ void TRA_cleanup(thread_db* tdbb) // First, make damn sure there are no outstanding transactions - for (Attachment* attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) + for (Jrd::Attachment* attachment = dbb->dbb_attachments; attachment; + attachment = attachment->att_next) { if (attachment->att_transactions) return; @@ -779,8 +780,8 @@ void TRA_invalidate(Database* database, ULONG mask) * modified a page that couldn't be written. * **************************************/ - for (Attachment* attachment = database->dbb_attachments; attachment; - attachment = attachment->att_next) + for (Jrd::Attachment* attachment = database->dbb_attachments; attachment; + attachment = attachment->att_next) { for (jrd_tra* transaction = attachment->att_transactions; transaction; transaction = transaction->tra_next) @@ -1033,7 +1034,7 @@ jrd_tra* TRA_reconnect(thread_db* tdbb, const UCHAR* id, USHORT length) SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); CHECK_DBB(dbb); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); // Cannot work on limbo transactions for ReadOnly database if (dbb->dbb_flags & DBB_read_only) @@ -1102,7 +1103,7 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction) **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (!transaction->tra_outer) { @@ -1473,7 +1474,7 @@ void TRA_set_state(thread_db* tdbb, jrd_tra* transaction, SLONG number, SSHORT s } -void TRA_shutdown_attachment(thread_db* tdbb, Attachment* attachment) +void TRA_shutdown_attachment(thread_db* tdbb, Jrd::Attachment* attachment) { /************************************** * @@ -1593,7 +1594,7 @@ jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout, Jrd::jrd_t **************************************/ SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); if (dbb->dbb_ast_flags & DBB_shut_tran) { @@ -1647,7 +1648,7 @@ jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb, Jrd::jrd_t **************************************/ SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if (dbb->dbb_ast_flags & DBB_shut_tran) { @@ -1976,7 +1977,7 @@ static int blocking_ast_transaction(void* ast_object) ThreadContextHolder tdbb; tdbb->setDatabase(dbb); - Attachment* att = transaction->tra_cancel_lock->lck_attachment; + Jrd::Attachment* att = transaction->tra_cancel_lock->lck_attachment; tdbb->setAttachment(att); Jrd::ContextPoolHolder context(tdbb, 0); @@ -2498,7 +2499,7 @@ static void link_transaction(thread_db* tdbb, jrd_tra* transaction) **************************************/ SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); transaction->tra_next = attachment->att_transactions; attachment->att_transactions = transaction; } @@ -3257,7 +3258,7 @@ static jrd_tra* transaction_start(thread_db* tdbb, jrd_tra* temp) **************************************/ SET_TDBB(tdbb); Database* const dbb = tdbb->getDatabase(); - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); WIN window(DB_PAGE_SPACE, -1); Lock* lock = create_transaction_lock(tdbb, temp); diff --git a/src/jrd/tra.h b/src/jrd/tra.h index ebaa85b89a..dd37a927a1 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -43,6 +43,7 @@ #include "../jrd/DatabaseSnapshot.h" #include "../jrd/TempSpace.h" +#include "../jrd/obj.h" namespace EDS { class Transaction; @@ -96,6 +97,29 @@ typedef Firebird::BePlusTree BlobIndexT // Transaction block +struct CallerName +{ + CallerName(int aType, const Firebird::MetaName& aName) + : type(aType), + name(aName) + { + } + + CallerName() + : type(obj_type_MAX) + { + } + + CallerName(const CallerName& o) + : type(o.type), + name(o.name) + { + } + + int type; + Firebird::MetaName name; +}; + const int DEFAULT_LOCK_TIMEOUT = -1; // infinite const char* const TRA_BLOB_SPACE = "fb_blob_"; const char* const TRA_UNDO_SPACE = "fb_undo_"; @@ -175,6 +199,7 @@ public: } } + FB_API_HANDLE tra_public_handle; // Public handle Attachment* tra_attachment; // database attachment SLONG tra_number; // transaction number SLONG tra_top; // highest transaction in snapshot @@ -209,8 +234,9 @@ public: DatabaseSnapshot* tra_db_snapshot; // Database state snapshot (for monitoring purposes) RuntimeStatistics tra_stats; Firebird::Array tra_open_cursors; + bool tra_in_use; // transaction in use (can't be committed or rolled back) jrd_tra* const tra_outer; // outer transaction of an autonomous transaction - jrd_req* tra_callback_caller; // caller request for execute statement + CallerName tra_caller_name; // caller object name Firebird::Array tra_transactions; SortOwner tra_sorts; @@ -396,6 +422,9 @@ enum dfw_t { dfw_begin_backup, dfw_end_backup, dfw_user_management, + dfw_drop_package_header, + dfw_drop_package_body, + dfw_check_not_null, // deferred works argument types dfw_arg_index_name, // index name for dfw_delete_expression_index, mandatory diff --git a/src/jrd/trace/TraceObjects.h b/src/jrd/trace/TraceObjects.h index df2a42ab14..2e733f6e8b 100644 --- a/src/jrd/trace/TraceObjects.h +++ b/src/jrd/trace/TraceObjects.h @@ -41,6 +41,7 @@ #include "../../jrd/RuntimeStatistics.h" #include "../../jrd/trace/TraceSession.h" +//// TODO: DDL triggers, packages and external procedures and functions support namespace Jrd { class Database; @@ -253,7 +254,7 @@ public: m_inputs(*getDefaultMemoryPool(), request->req_proc_caller, request->req_proc_inputs) {} - virtual const char* getProcName() { return m_request->req_procedure->prc_name.c_str(); } + virtual const char* getProcName() { return m_request->req_procedure->prc_name.identifier.c_str(); } virtual TraceParams* getInputs() { return &m_inputs; } virtual PerformanceInfo* getPerf() { return m_perf; }; diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 2f63c3b12d..63b4328605 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -168,6 +168,26 @@ static const UCHAR trigger1[] = blr_leave, 0, blr_end, blr_if, + blr_and, + blr_eql, + blr_field, 1, 15, 'R', 'D', 'B', '$', 'O', 'B', 'J', 'E', 'C', 'T', '_', + 'T', 'Y', 'P', 'E', + blr_literal, blr_long, 0, 18, 0, 0, 0, + blr_not, + blr_any, + blr_rse, 1, + blr_relation, 12, 'R', 'D', 'B', '$', 'P', 'A', 'C', 'K', 'A', 'G', 'E', + 'S', 21, + blr_boolean, + blr_eql, + blr_field, 21, 16, 'R', 'D', 'B', '$', 'P', 'A', 'C', 'K', 'A', 'G', 'E', + '_', 'N', 'A', 'M', 'E', + blr_field, 1, 17, 'R', 'D', 'B', '$', 'R', 'E', 'L', 'A', 'T', 'I', 'O', + 'N', '_', 'N', 'A', 'M', 'E', + blr_end, + blr_leave, 0, + blr_end, + blr_if, blr_or, blr_eql, blr_field, 1, 13, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', '_', 'T', 'Y', @@ -709,6 +729,111 @@ static const UCHAR trigger1[] = blr_leave, 3, blr_end, blr_end, + blr_if, + blr_eql, + blr_field, 1, 15, 'R', 'D', 'B', '$', 'O', 'B', 'J', 'E', 'C', 'T', '_', + 'T', 'Y', 'P', 'E', + blr_literal, blr_long, 0, 18, 0, 0, 0, + blr_for, + blr_rse, 1, + blr_relation, 12, 'R', 'D', 'B', '$', 'P', 'A', 'C', 'K', 'A', 'G', 'E', + 'S', 22, + blr_boolean, + blr_eql, + blr_field, 22, 16, 'R', 'D', 'B', '$', 'P', 'A', 'C', 'K', 'A', 'G', 'E', + '_', 'N', 'A', 'M', 'E', + blr_field, 1, 17, 'R', 'D', 'B', '$', 'R', 'E', 'L', 'A', 'T', 'I', 'O', + 'N', '_', 'N', 'A', 'M', 'E', + blr_end, + blr_begin, + blr_if, + blr_and, + blr_neq, + blr_field, 22, 14, 'R', 'D', 'B', '$', 'O', 'W', 'N', 'E', 'R', '_', 'N', + 'A', 'M', 'E', + blr_upcase, + blr_user_name, + blr_neq, + blr_upcase, + blr_user_name, + blr_literal, blr_text, 6, 0, 'S', 'Y', 'S', 'D', 'B', 'A', + blr_if, + blr_not, + blr_any, + blr_rse, 1, + blr_relation, 19, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', '_', 'P', 'R', + 'I', 'V', 'I', 'L', 'E', 'G', 'E', 'S', 24, + blr_boolean, + blr_and, + blr_eql, + blr_field, 24, 17, 'R', 'D', 'B', '$', 'R', 'E', 'L', 'A', 'T', 'I', 'O', + 'N', '_', 'N', 'A', 'M', 'E', + blr_field, 1, 17, 'R', 'D', 'B', '$', 'R', 'E', 'L', 'A', 'T', 'I', 'O', + 'N', '_', 'N', 'A', 'M', 'E', + blr_and, + blr_eql, + blr_field, 24, 15, 'R', 'D', 'B', '$', 'O', 'B', 'J', 'E', 'C', 'T', '_', + 'T', 'Y', 'P', 'E', + blr_literal, blr_long, 0, 18, 0, 0, 0, + blr_and, + blr_eql, + blr_field, 24, 13, 'R', 'D', 'B', '$', 'P', 'R', 'I', 'V', 'I', 'L', 'E', + 'G', 'E', + blr_field, 1, 13, 'R', 'D', 'B', '$', 'P', 'R', 'I', 'V', 'I', 'L', 'E', + 'G', 'E', + blr_and, + blr_eql, + blr_field, 24, 8, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', + blr_field, 1, 11, 'R', 'D', 'B', '$', 'G', 'R', 'A', 'N', 'T', 'O', 'R', + blr_and, + blr_eql, + blr_field, 24, 13, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', '_', 'T', 'Y', + 'P', 'E', + blr_literal, blr_long, 0, 8, 0, 0, 0, + blr_and, + blr_neq, + blr_field, 24, 16, 'R', 'D', 'B', '$', 'G', 'R', 'A', 'N', 'T', '_', 'O', + 'P', 'T', 'I', 'O', 'N', + blr_literal, blr_long, 0, 0, 0, 0, 0, + blr_or, + blr_missing, + blr_field, 24, 14, 'R', 'D', 'B', '$', 'F', 'I', 'E', 'L', 'D', '_', 'N', + 'A', 'M', 'E', + blr_eql, + blr_field, 24, 14, 'R', 'D', 'B', '$', 'F', 'I', 'E', 'L', 'D', '_', 'N', + 'A', 'M', 'E', + blr_field, 1, 14, 'R', 'D', 'B', '$', 'F', 'I', 'E', 'L', 'D', '_', 'N', + 'A', 'M', 'E', + blr_end, + blr_leave, 2, + blr_end, + blr_end, + blr_if, + blr_missing, + blr_field, 22, 18, 'R', 'D', 'B', '$', 'S', 'E', 'C', 'U', 'R', 'I', 'T', + 'Y', '_', 'C', 'L', 'A', 'S', 'S', + blr_modify, 22, 23, + blr_begin, + blr_assignment, + blr_cast, blr_varying2, 3, 0, 31, 0, + blr_concatenate, + blr_literal, blr_text2, 1, 0, 4, 0, 'S', 'Q', 'L', '$', + blr_gen_id, 18, 'R', 'D', 'B', '$', 'S', 'E', 'C', 'U', 'R', 'I', 'T', + 'Y', '_', 'C', 'L', 'A', 'S', 'S', + blr_literal, blr_long, 0, 1, 0, 0, 0, + blr_field, 23, 18, 'R', 'D', 'B', '$', 'S', 'E', 'C', 'U', 'R', 'I', 'T', + 'Y', '_', 'C', 'L', 'A', 'S', 'S', + blr_end, + blr_if, + blr_not, + blr_starting, + blr_field, 22, 18, 'R', 'D', 'B', '$', 'S', 'E', 'C', 'U', 'R', 'I', 'T', + 'Y', '_', 'C', 'L', 'A', 'S', 'S', + blr_cast, blr_varying2, 3, 0, 31, 0, + blr_literal, blr_text2, 1, 0, 4, 0, 'S', 'Q', 'L', '$', + blr_leave, 3, + blr_end, + blr_end, blr_end, blr_end, blr_eoc diff --git a/src/jrd/types.h b/src/jrd/types.h index 78130a18cc..73df7453cd 100644 --- a/src/jrd/types.h +++ b/src/jrd/types.h @@ -92,6 +92,8 @@ TYPE ("GENERATOR", obj_generator, nam_obj_type) TYPE ("UDF", obj_udf, nam_obj_type) TYPE ("BLOB_FILTER", obj_blob_filter, nam_obj_type) TYPE ("COLLATION", obj_collation, nam_obj_type) +TYPE ("PACKAGE", obj_package_header, nam_obj_type) +TYPE ("PACKAGE BODY", obj_package_body, nam_obj_type) TYPE("LIMBO", 1, nam_trans_state) TYPE("COMMITTED", 2, nam_trans_state) diff --git a/src/jrd/val.h b/src/jrd/val.h index 36b3c16c7e..2d5e3c6574 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -32,8 +32,10 @@ #include "../include/fb_blk.h" #include "../common/classes/array.h" #include "../common/classes/MetaName.h" +#include "../common/classes/QualifiedName.h" #include "../jrd/dsc.h" +#include "../jrd/ExtEngineManager.h" #define FLAG_BYTES(n) (((n + BITS_PER_LONG) & ~((ULONG)BITS_PER_LONG - 1)) >> 3) @@ -93,7 +95,7 @@ struct fun_repeat class UserFunction : public pool_alloc_rpt { public: - Firebird::MetaName fun_name; // Function name + Firebird::QualifiedName fun_name; // Function name Firebird::string fun_exception_message; /* message containing the exception error message */ UserFunction* fun_homonym; /* Homonym functions */ Symbol* fun_symbol; /* Symbol block */ @@ -103,7 +105,8 @@ public: USHORT fun_return_arg; /* Return argument */ USHORT fun_type; /* Type of function */ ULONG fun_temp_length; /* Temporary space required */ - fun_repeat fun_rpt[1]; + Jrd::ExtEngineManager::Function* fun_external; + fun_repeat fun_rpt[1]; public: explicit UserFunction(MemoryPool& p) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 93c69c52ac..370c95dfcb 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -596,7 +596,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb, return true; } - Attachment* attachment = transaction->tra_attachment; + Jrd::Attachment* attachment = transaction->tra_attachment; // OK, something about the record is fishy. Loop thru versions until a // satisfactory version is found or we run into a brick wall. Do any @@ -1162,7 +1162,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) **************************************/ // Revokee is only 32 bytes. UserId would be truncated. - SqlIdentifier relation_name, revokee, privilege, procedure_name; + SqlIdentifier relation_name, revokee, privilege, procedure_name, package_name; SET_TDBB(tdbb); jrd_req* request = tdbb->getRequest(); @@ -1239,14 +1239,27 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } break; + case rel_packages: + if (EVL_field(0, rpb->rpb_record, f_pkg_name, &desc)) + SCL_check_package(tdbb, &desc, SCL_delete); + break; + case rel_procedures: - if (EVL_field(0, rpb->rpb_record, f_prc_name, &desc)) - { - SCL_check_procedure(tdbb, &desc, SCL_delete); - } EVL_field(0, rpb->rpb_record, f_prc_id, &desc2); id = MOV_get_long(&desc2, 0); - DFW_post_work(transaction, dfw_delete_procedure, &desc, id); + + if (EVL_field(0, rpb->rpb_record, f_prc_pkg_name, &desc2)) + { + MOV_get_metadata_str(&desc2, package_name, sizeof(package_name)); + SCL_check_package(tdbb, &desc2, SCL_delete); + } + else + package_name[0] = '\0'; + + if (EVL_field(0, rpb->rpb_record, f_prc_name, &desc) && package_name[0] == '\0') + SCL_check_procedure(tdbb, &desc, SCL_delete); + + DFW_post_work(transaction, dfw_delete_procedure, &desc, id, package_name); MET_lookup_procedure_id(tdbb, id, false, true, 0); break; @@ -1347,12 +1360,26 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_prc_prms: EVL_field(0, rpb->rpb_record, f_prm_procedure, &desc); - SCL_check_procedure(tdbb, &desc, SCL_control); + + if (EVL_field(0, rpb->rpb_record, f_prm_pkg_name, &desc2)) + { + MOV_get_metadata_str(&desc2, package_name, sizeof(package_name)); + SCL_check_package(tdbb, &desc2, SCL_control); + } + else + { + package_name[0] = '\0'; + SCL_check_procedure(tdbb, &desc, SCL_control); + } + EVL_field(0, rpb->rpb_record, f_prm_name, &desc2); MOV_get_metadata_str(&desc, procedure_name, sizeof(procedure_name)); - if ( (procedure = MET_lookup_procedure(tdbb, procedure_name, true)) ) + + if ((procedure = MET_lookup_procedure(tdbb, + QualifiedName(procedure_name, package_name), true))) { - work = DFW_post_work(transaction, dfw_delete_prm, &desc2, procedure->prc_id); + work = DFW_post_work(transaction, dfw_delete_prm, &desc2, procedure->prc_id, + package_name); // procedure name to track parameter dependencies DFW_post_work_arg(transaction, work, &desc, procedure->prc_id, dfw_arg_proc_name); @@ -1414,7 +1441,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, work, &desc2, - MOV_get_long(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); } break; @@ -2022,7 +2049,7 @@ void VIO_init(thread_db* tdbb) * **************************************/ Database* dbb = tdbb->getDatabase(); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); if ((dbb->dbb_flags & DBB_read_only) || !(dbb->dbb_flags & DBB_gc_background)) { @@ -2197,14 +2224,33 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, DFW_post_work(transaction, dfw_update_format, &desc1, 0); break; + case rel_packages: + if (EVL_field(0, org_rpb->rpb_record, f_pkg_name, &desc1)) + SCL_check_package(tdbb, &desc1, SCL_protect); + check_class(tdbb, transaction, org_rpb, new_rpb, f_pkg_class); + break; + case rel_procedures: EVL_field(0, org_rpb->rpb_record, f_prc_name, &desc1); - SCL_check_procedure(tdbb, &desc1, SCL_protect); - check_class(tdbb, transaction, org_rpb, new_rpb, f_prc_class); - EVL_field(0, org_rpb->rpb_record, f_prc_id, &desc2); + { // scope + SqlIdentifier package_name; + if (EVL_field(0, org_rpb->rpb_record, f_prc_pkg_name, &desc2)) + { + MOV_get_metadata_str(&desc2, package_name, sizeof(package_name)); + SCL_check_package(tdbb, &desc2, SCL_protect); + } + else + { + package_name[0] = '\0'; + SCL_check_procedure(tdbb, &desc1, SCL_protect); + } + + check_class(tdbb, transaction, org_rpb, new_rpb, f_prc_class); + + EVL_field(0, org_rpb->rpb_record, f_prc_id, &desc2); const USHORT id = MOV_get_long(&desc2, 0); - DFW_post_work(transaction, dfw_modify_procedure, &desc1, id); + DFW_post_work(transaction, dfw_modify_procedure, &desc1, id, package_name); } // scope break; @@ -2220,9 +2266,20 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, break; case rel_rfr: - check_rel_field_class(tdbb, org_rpb, SCL_control, transaction); - check_rel_field_class(tdbb, new_rpb, SCL_control, transaction); - check_class(tdbb, transaction, org_rpb, new_rpb, f_rfr_class); + { + check_rel_field_class(tdbb, org_rpb, SCL_control, transaction); + check_rel_field_class(tdbb, new_rpb, SCL_control, transaction); + check_class(tdbb, transaction, org_rpb, new_rpb, f_rfr_class); + + bool rc1 = EVL_field(NULL, org_rpb->rpb_record, f_rfr_null_flag, &desc1); + bool rc2 = EVL_field(NULL, new_rpb->rpb_record, f_rfr_null_flag, &desc2); + + if ((!rc1 || MOV_get_long(&desc1, 0) == 0) && rc2 && MOV_get_long(&desc2, 0) != 0) + { + EVL_field(0, new_rpb->rpb_record, f_rfr_rname, &desc1); + DFW_post_work(transaction, dfw_check_not_null, &desc1, 0); + } + } break; case rel_fields: @@ -2294,7 +2351,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, if (EVL_field(0, new_rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, dw, &desc2, - MOV_get_long(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); } } break; @@ -2645,12 +2702,22 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) set_system_flag(tdbb, rpb, f_rel_sys_flag, 0); break; + case rel_packages: + set_system_flag(tdbb, rpb, f_pkg_sys_flag, 0); + break; + case rel_procedures: EVL_field(0, rpb->rpb_record, f_prc_name, &desc); - EVL_field(0, rpb->rpb_record, f_prc_id, &desc2); { // scope + SqlIdentifier package_name; + if (EVL_field(0, rpb->rpb_record, f_prc_pkg_name, &desc2)) + MOV_get_metadata_str(&desc2, package_name, sizeof(package_name)); + else + package_name[0] = '\0'; + + EVL_field(0, rpb->rpb_record, f_prc_id, &desc2); const USHORT id = MOV_get_long(&desc2, 0); - work = DFW_post_work(transaction, dfw_create_procedure, &desc, id); + work = DFW_post_work(transaction, dfw_create_procedure, &desc, id, package_name); bool check_blr = true; @@ -2752,7 +2819,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, work, &desc2, - MOV_get_long(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); } break; @@ -3351,7 +3418,7 @@ static void check_rel_field_class(thread_db* tdbb, // he may have access to relation as whole. try { - SCL_check_access(tdbb, s_class, 0, NULL, NULL, flags, "", ""); + SCL_check_access(tdbb, s_class, 0, NULL, NULL, NULL, flags, "", ""); } catch (const Firebird::Exception&) { @@ -3391,9 +3458,9 @@ static void check_class(thread_db* tdbb, if (!MOV_compare(&desc1, &desc2)) return; - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); - SCL_check_access(tdbb, attachment->att_security_class, 0, NULL, NULL, SCL_protect, + SCL_check_access(tdbb, attachment->att_security_class, 0, NULL, NULL, NULL, SCL_protect, "DATABASE", NULL); DFW_post_work(transaction, dfw_compute_security, &desc2, 0); } @@ -3414,9 +3481,9 @@ static void check_control(thread_db* tdbb) **************************************/ SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); + Jrd::Attachment* attachment = tdbb->getAttachment(); - SCL_check_access(tdbb, attachment->att_security_class, 0, NULL, NULL, SCL_control, + SCL_check_access(tdbb, attachment->att_security_class, 0, NULL, NULL, NULL, SCL_control, "DATABASE", NULL); } @@ -3874,7 +3941,7 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg) try { // Pseudo attachment needed for lock owner identification. - Attachment* const attachment = Attachment::create(dbb); + Jrd::Attachment* const attachment = Jrd::Attachment::create(dbb, 0); tdbb->setAttachment(attachment); attachment->att_filename = dbb->dbb_filename; attachment->att_flags = ATT_garbage_collector; @@ -3915,7 +3982,7 @@ static THREAD_ENTRY_DECLARE garbage_collector(THREAD_ENTRY_PARAM arg) if (dbb->dbb_flags & DBB_suspend_bgio) { - Attachment* attachment; + Jrd::Attachment* attachment; for (attachment = dbb->dbb_attachments; attachment; attachment = attachment->att_next) { @@ -4131,11 +4198,11 @@ gc_exit: TRA_commit(tdbb, transaction, false); } - Attachment* const attachment = tdbb->getAttachment(); + Jrd::Attachment* const attachment = tdbb->getAttachment(); if (attachment) { LCK_fini(tdbb, LCK_OWNER_attachment); - Attachment::destroy(attachment); // no need saving warning error strings here + Jrd::Attachment::destroy(attachment); // no need saving warning error strings here tdbb->setAttachment(NULL); } @@ -5213,3 +5280,24 @@ static void verb_post(thread_db* tdbb, garbage_collect_idx(tdbb, rpb, /*new_rpb,*/ old_data, undo); } } + + +//---------------------- + + +AutoSavePoint::AutoSavePoint(thread_db* tdbb, jrd_tra* aTransaction) + : transaction(aTransaction), + released(false) +{ + VIO_start_save_point(tdbb, transaction); +} + +AutoSavePoint::~AutoSavePoint() +{ + thread_db* tdbb = JRD_get_thread_data(); + + if (!released) + ++transaction->tra_save_point->sav_verb_count; + + VIO_verb_cleanup(tdbb, transaction); +} diff --git a/src/jrd/vio_proto.h b/src/jrd/vio_proto.h index ae859fa4a2..2aa9dec54a 100644 --- a/src/jrd/vio_proto.h +++ b/src/jrd/vio_proto.h @@ -70,5 +70,26 @@ bool VIO_sweep(Jrd::thread_db*, Jrd::jrd_tra*); void VIO_verb_cleanup(Jrd::thread_db*, Jrd::jrd_tra*); IPTR VIO_savepoint_large(const Jrd::Savepoint*, IPTR); +namespace Jrd +{ + // Starts a savepoint and rollback it in destructor if release() is not called. + class AutoSavePoint + { + public: + AutoSavePoint(thread_db* tdbb, jrd_tra* aTransaction); + ~AutoSavePoint(); + + public: + void release() + { + released = true; + } + + private: + jrd_tra* transaction; + bool released; + }; +} + #endif // JRD_VIO_PROTO_H diff --git a/src/jrd/why.cpp b/src/jrd/why.cpp index 5fbf233075..6949cd81c0 100644 --- a/src/jrd/why.cpp +++ b/src/jrd/why.cpp @@ -1470,12 +1470,14 @@ ISC_STATUS API_ROUTINE GDS_ATTACH_DATABASE(ISC_STATUS* user_status, continue; } - if (!CALL(PROC_ATTACH_DATABASE, n) (ptr, expanded_filename.c_str(), - &handle, newDpb.getBufferLength(), + attachment = new CAttachment(NULL, public_handle, n); + attachment->db_path = expanded_filename; + + if (!CALL(PROC_ATTACH_DATABASE, n) (ptr, *public_handle, expanded_filename.c_str(), + &attachment->handle, newDpb.getBufferLength(), reinterpret_cast(newDpb.getBuffer()))) { - attachment = new CAttachment(handle, public_handle, n); - attachment->db_path = expanded_filename; + handle = attachment->handle; status[0] = isc_arg_gds; status[1] = 0; @@ -1493,6 +1495,12 @@ ISC_STATUS API_ROUTINE GDS_ATTACH_DATABASE(ISC_STATUS* user_status, return status[1]; } + else + { + *public_handle = 0; + destroy(attachment); + } + if (ptr[1] != isc_unavailable) { ptr = temp; @@ -2042,10 +2050,19 @@ ISC_STATUS API_ROUTINE GDS_CREATE_DATABASE(ISC_STATUS* user_status, continue; } - if (!CALL(PROC_CREATE_DATABASE, n) (ptr, expanded_filename.c_str(), - &handle, newDpb.getBufferLength(), + attachment = new CAttachment(NULL, public_handle, n); +#ifdef WIN_NT + attachment->db_path = expanded_filename; +#else + attachment->db_path = org_filename; +#endif + + if (!CALL(PROC_CREATE_DATABASE, n) (ptr, *public_handle, expanded_filename.c_str(), + &attachment->handle, newDpb.getBufferLength(), reinterpret_cast(newDpb.getBuffer()))) { + handle = attachment->handle; + #ifdef WIN_NT // Now we can expand, the file exists expanded_filename = org_filename; @@ -2055,13 +2072,6 @@ ISC_STATUS API_ROUTINE GDS_CREATE_DATABASE(ISC_STATUS* user_status, ISC_systemToUtf8(expanded_filename); #endif - attachment = new CAttachment(handle, public_handle, n); -#ifdef WIN_NT - attachment->db_path = expanded_filename; -#else - attachment->db_path = org_filename; -#endif - status[0] = isc_arg_gds; status[1] = 0; if (status[2] != isc_arg_warning) @@ -2069,6 +2079,12 @@ ISC_STATUS API_ROUTINE GDS_CREATE_DATABASE(ISC_STATUS* user_status, return status[1]; } + else + { + *public_handle = 0; + destroy(attachment); + } + if (ptr[1] != isc_unavailable) ptr = temp; } @@ -4838,7 +4854,6 @@ ISC_STATUS API_ROUTINE GDS_START_MULTIPLE(ISC_STATUS* user_status, ISC_STATUS_ARRAY temp; Transaction transaction(NULL); Attachment attachment(NULL); - StoredTra* handle = NULL; Status status(user_status); @@ -4848,29 +4863,25 @@ ISC_STATUS API_ROUTINE GDS_START_MULTIPLE(ISC_STATUS* user_status, nullCheck(tra_handle, isc_bad_trans_handle); if (count <= 0 || !vector) - { status_exception::raise(Arg::Gds(isc_bad_teb_form)); - } - Transaction* ptr; - USHORT n; - for (n = 0, ptr = &transaction; n < count; n++, ptr = &(*ptr)->next, vector++) + if (vector->teb_tpb_length < 0 || (vector->teb_tpb_length > 0 && !vector->teb_tpb)) + status_exception::raise(Arg::Gds(isc_bad_tpb_form)); + + Transaction* ptr = &transaction; + + for (USHORT n = 0; n < count; n++, ptr = &(*ptr)->next, vector++) { - if (vector->teb_tpb_length < 0 || (vector->teb_tpb_length > 0 && !vector->teb_tpb)) - { - status_exception::raise(Arg::Gds(isc_bad_tpb_form)); - } - attachment = translate(vector->teb_database); - if (CALL(PROC_START_TRANSACTION, attachment->implementation) (status, &handle, 1, - &attachment->handle, vector->teb_tpb_length, vector->teb_tpb)) + *ptr = new CTransaction(0, 0, attachment); + + if (CALL(PROC_START_TRANSACTION, attachment->implementation) (status, + (*ptr)->public_handle, &(*ptr)->handle, 1, &attachment->handle, + vector->teb_tpb_length, vector->teb_tpb)) { status_exception::raise(status); } - - *ptr = new CTransaction(handle, 0, attachment); - handle = 0; } if (transaction->next) @@ -4886,7 +4897,7 @@ ISC_STATUS API_ROUTINE GDS_START_MULTIPLE(ISC_STATUS* user_status, { e.stuff_exception(status); - if (handle || transaction) + if (transaction) { *tra_handle = 0; } @@ -4905,11 +4916,6 @@ ISC_STATUS API_ROUTINE GDS_START_MULTIPLE(ISC_STATUS* user_status, { destroy(transaction); } - - if (handle && attachment) - { - CALL(PROC_ROLLBACK, attachment->implementation) (temp, &handle); - } } return status[1]; @@ -5419,7 +5425,6 @@ static PTR get_entrypoint(int proc, int implementation) return *entry; return &no_entrypoint; - } diff --git a/src/misc/blrtable.cpp b/src/misc/blrtable.cpp index c24ef2996c..89de88008a 100644 --- a/src/misc/blrtable.cpp +++ b/src/misc/blrtable.cpp @@ -224,6 +224,11 @@ static const VERB verbs[] = PAIR(nod_similar, blr_similar, 3, 3, TYPE_BOOL, VALUE), PAIR(nod_stmt_expr, blr_stmt_expr, e_stmt_expr_length, 2, VALUE, OTHER), PAIR(nod_derived_expr, blr_derived_expr, e_derived_expr_length, e_derived_expr_count, VALUE, VALUE), + PAIR(nod_procedure, blr_procedure2, e_prc_length, 2, RELATION, OTHER), + PAIR(nod_exec_proc, blr_exec_proc2, e_esp_length, 4, STATEMENT, OTHER), + PAIR(nod_function, blr_function2, 0, 0, VALUE, VALUE), + PAIR(nod_aggregate, blr_window, e_agg_length, 0, RELATION, OTHER), + PAIR(nod_continue_loop, blr_continue_loop, 1, 0, STATEMENT, OTHER), {0, NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index c7982ba48c..8bb761dfb1 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2009-10-02 20:33:37', 'JRD', 0, 668) +('2009-10-20 12:37:00', 'JRD', 0, 670) -- Reserved 513-529 by CVC ('2009-07-16 05:41:59', 'QLI', 1, 530) ('2008-11-28 20:27:04', 'GDEF', 2, 346) @@ -19,7 +19,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) -- Reserved 326-334 by CVC -('2009-07-27 03:59:31', 'GBAK', 12, 335) +('2009-08-07 22:42:00', 'GBAK', 12, 339) ('2009-06-05 23:07:00', 'SQLERR', 13, 970) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2006-09-10 03:04:31', 'JRD_BUGCHK', 15, 307) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 43c3613238..bab7c58f73 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -327,7 +327,7 @@ without specifying a character set.', NULL); ('primary_key_exists', 'trigger_messages', 'ini.e', NULL, 0, 228, NULL, 'Attempt to define a second PRIMARY KEY for the same table', NULL, NULL); ('systrig_update', 'trigger_messages', 'ini.e', NULL, 0, 229, NULL, 'cannot modify or erase a system trigger', NULL, NULL); ('not_rel_owner', 'trigger_messages', 'ini.e', NULL, 0, 230, NULL, 'only the owner of a table may reassign ownership', NULL, NULL); -('grant_obj_notfound', 'trigger_messages', 'ini.e', NULL, 0, 231, NULL, 'could not find table/procedure for GRANT', NULL, NULL); +('grant_obj_notfound', 'trigger_messages', 'ini.e', NULL, 0, 231, NULL, 'could not find table/procedure/package for GRANT', NULL, NULL); ('grant_fld_notfound', 'trigger_messages', 'ini.e', NULL, 0, 232, NULL, 'could not find column for GRANT', NULL, NULL); ('grant_nopriv', 'trigger_messages', 'ini.e', NULL, 0, 233, NULL, 'user does not have GRANT privileges for operation', NULL, NULL); ('nonsql_security_rel', 'trigger_messages', 'ini.e', NULL, 0, 234, NULL, 'table/procedure has non-SQL security class defined', NULL, NULL); @@ -598,11 +598,11 @@ without specifying a character set.', NULL); ('io_32bit_exceeded_err', 'seek_file', 'unix.c', NULL, 0, 499, NULL, 'File exceeded maximum size of 2GB. Add another database file or use a 64 bit I/O version of Firebird.', NULL, NULL); ('invalid_savepoint', 'looper', 'exe.cpp', NULL, 0, 500, NULL, 'Unable to find savepoint with name @1 in transaction context', NULL, NULL); ('dsql_column_pos_err', '(several)', 'pass1.cpp', NULL, 0, 501, NULL, 'Invalid column position used in the @1 clause', NULL, NULL); -('dsql_agg_where_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 502, NULL, 'Cannot use an aggregate function in a WHERE clause, use HAVING instead', NULL, NULL); -('dsql_agg_group_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 503, NULL, 'Cannot use an aggregate function in a GROUP BY clause', NULL, NULL); +('dsql_agg_where_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 502, NULL, 'Cannot use an aggregate or window function in a WHERE clause, use HAVING (for aggregate only) instead', NULL, NULL); +('dsql_agg_group_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 503, NULL, 'Cannot use an aggregate or window function in a GROUP BY clause', NULL, NULL); ('dsql_agg_column_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 504, NULL, 'Invalid expression in the @1 (not contained in either an aggregate function or the GROUP BY clause)', NULL, NULL); ('dsql_agg_having_err', 'pass1_rse', 'pass1.cpp', NULL, 0, 505, NULL, 'Invalid expression in the @1 (neither an aggregate function nor a part of the GROUP BY clause)', NULL, NULL); -('dsql_agg_nested_err', 'invalid_reference', 'pass1.cpp', NULL, 0, 506, NULL, 'Nested aggregate functions are not allowed', NULL, NULL); +('dsql_agg_nested_err', 'invalid_reference', 'pass1.cpp', NULL, 0, 506, NULL, 'Nested aggregate and window functions are not allowed', NULL, NULL); ('exec_sql_invalid_arg', '(several)', '(several)', NULL, 0, 507, NULL, 'Invalid argument in EXECUTE STATEMENT - cannot convert to string', NULL, NULL); ('exec_sql_invalid_req', '(several)', 'dsql.cpp', NULL, 0, 508, NULL, 'Wrong request type in EXECUTE STATEMENT ''@1''', NULL, NULL); ('exec_sql_invalid_var', '(several)', NULL, NULL, 0, 509, NULL, 'Variable type (position @1) in EXECUTE STATEMENT ''@2'' INTO does not match returned column type', NULL, NULL); @@ -775,6 +775,8 @@ Data source : @4', NULL, NULL) ('out_of_temp_space', 'setupFile', 'TempSpace.cpp', NULL, 0, 665, NULL, 'No free space found in temporary directories', NULL, NULL); ('eds_expl_tran_ctrl', NULL, '', NULL, 0, 666, NULL, 'Explicit transaction control is not allowed', NULL, NULL) ('no_trusted_spb', NULL, 'svc.cpp', NULL, 0, 667, NULL, 'Use of TRUSTED switches in spb_command_line is prohibited', NULL, NULL) +('package_name', 'check_dependencies', 'dfw.epp', NULL, 0, 668, NULL, 'PACKAGE @1', NULL, NULL); +('cannot_make_not_null', 'check_not_null', 'dfw.epp', NULL, 0, 669, NULL, 'Cannot make field @1 NOT NULL because there are NULLs present', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); @@ -1938,24 +1940,24 @@ COMMIT WORK; (NULL, NULL, 'dyn.c', NULL, 8, 34, NULL, 'STORE RDB$VIEW_RELATIONS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 35, NULL, 'ERASE RDB$FIELDS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 36, NULL, 'ERASE BLOB FILTER failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 37, NULL, 'BLOB Filter @1 not found', 'Define blob filter in RDB$BLOB_FILTERS.', 'Blob filter was not found.'); +('dyn_filter_not_found', NULL, 'dyn.c', NULL, 8, 37, NULL, 'BLOB Filter @1 not found', 'Define blob filter in RDB$BLOB_FILTERS.', 'Blob filter was not found.'); (NULL, NULL, 'dyn.c', NULL, 8, 38, NULL, 'unsupported DYN verb', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 39, NULL, 'ERASE RDB$FUNCTION_ARGUMENTS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 40, NULL, 'ERASE RDB$FUNCTIONS failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 41, NULL, 'Function @1 not found', NULL, NULL); +('dyn_func_not_found', NULL, 'dyn.c', NULL, 8, 41, NULL, 'Function @1 not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 42, NULL, 'unsupported DYN verb', NULL, NULL); (NULL, NULL, 'dyn_del.epp', NULL, 8, 43, NULL, 'Domain @1 is used in table @2 (local name @3) and cannot be dropped', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 44, NULL, 'ERASE RDB$FIELDS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 45, NULL, 'ERASE RDB$FIELDS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 46, NULL, 'Column not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 47, NULL, 'ERASE RDB$INDICES failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 48, NULL, 'Index not found', NULL, NULL); +('dyn_index_not_found', NULL, 'dyn.c', NULL, 8, 48, NULL, 'Index not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 49, NULL, 'ERASE RDB$INDEX_SEGMENTS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 50, NULL, 'No segments found for index', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 51, NULL, 'No table specified in ERASE RFR', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 52, NULL, 'Column @1 from table @2 is referenced in view @3', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 53, NULL, 'ERASE RDB$RELATION_FIELDS failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 54, NULL, 'View @1 not found', NULL, NULL); +('dyn_view_not_found', NULL, 'dyn.c', NULL, 8, 54, NULL, 'View @1 not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 55, NULL, 'Column not found for table', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 56, NULL, 'ERASE RDB$INDEX_SEGMENTS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 57, NULL, 'ERASE RDB$INDICES failed', NULL, NULL); @@ -1990,7 +1992,7 @@ COMMIT WORK; (NULL, NULL, 'dyn_mod.epp', NULL, 8, 86, NULL, 'MODIFY RDB$COLLATIONS failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 87, NULL, 'MODIFY RDB$FIELDS failed', NULL, NULL); (NULL, NULL, 'dyn_mod.epp', NULL, 8, 88, NULL, 'MODIFY RDB$BLOB_FILTERS failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 89, NULL, 'Domain not found', NULL, NULL); +('dyn_domain_not_found', NULL, 'dyn.c', NULL, 8, 89, NULL, 'Domain not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 90, NULL, 'unsupported DYN verb', NULL, NULL); (NULL, NULL, 'dyn_mod.epp', NULL, 8, 91, NULL, 'MODIFY RDB$INDICES failed', NULL, NULL); (NULL, NULL, 'dyn_mod.epp', NULL, 8, 92, NULL, 'MODIFY RDB$FUNCTIONS failed', NULL, NULL); @@ -2010,7 +2012,7 @@ COMMIT WORK; (NULL, NULL, 'dyn.c', NULL, 8, 106, NULL, 'Create metadata BLOB failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 107, NULL, 'Write metadata BLOB failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 108, NULL, 'Close metadata BLOB failed', NULL, NULL); -(NULL, NULL, 'dyn.c', NULL, 8, 109, NULL, 'Triggers created automatically cannot be modified', NULL, NULL); +('dyn_cant_modify_auto_trig', NULL, 'dyn.c', NULL, 8, 109, NULL, 'Triggers created automatically cannot be modified', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 110, NULL, 'unsupported DYN verb', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 111, NULL, 'ERASE RDB$USER_PRIVILEGES failed in revoke(1)', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 112, NULL, 'Access to RDB$USER_PRIVILEGES failed in revoke(2)', NULL, NULL); @@ -2041,26 +2043,26 @@ COMMIT WORK; (NULL, 'define_intl_info', 'dyn.e', NULL, 8, 137, NULL, 'Store into system table @1 failed', NULL, NULL); (NULL, 'delete_procedure', 'dyn.e', NULL, 8, 138, NULL, 'ERASE RDB$PROCEDURE_PARAMETERS failed', NULL, NULL); (NULL, 'delete_procedure', 'dyn.e', NULL, 8, 139, NULL, 'ERASE RDB$PROCEDURES failed', NULL, NULL); -(NULL, 'delete_procedure', 'dyn.e', NULL, 8, 140, NULL, 'Procedure @1 not found', NULL, NULL); +('dyn_proc_not_found', 'delete_procedure', 'dyn.e', NULL, 8, 140, NULL, 'Procedure @1 not found', NULL, NULL); (NULL, 'modify_procedure', 'dyn.e', NULL, 8, 141, NULL, 'MODIFY RDB$PROCEDURES failed', NULL, NULL); (NULL, 'define_exception', 'dyn.e', NULL, 8, 142, NULL, 'DEFINE EXCEPTION failed', NULL, NULL); (NULL, 'delete_exception', 'dyn.e', NULL, 8, 143, NULL, 'ERASE EXCEPTION failed', NULL, NULL); -(NULL, 'delete_exception', 'dyn.e', NULL, 8, 144, NULL, 'Exception not found', NULL, NULL); +('dyn_exception_not_found', 'delete_exception', 'dyn.e', NULL, 8, 144, NULL, 'Exception not found', NULL, NULL); (NULL, 'modify_exception', 'dyn.e', NULL, 8, 145, NULL, 'MODIFY EXCEPTION failed', NULL, NULL); -(NULL, 'delete_parameter', 'dyn.e', NULL, 8, 146, NULL, 'Parameter @1 in procedure @2 not found', NULL, NULL); -(NULL, 'modify_trigger', 'dyn.e', NULL, 8, 147, NULL, 'Trigger @1 not found', NULL, NULL); +('dyn_proc_param_not_found', 'delete_parameter', 'dyn.e', NULL, 8, 146, NULL, 'Parameter @1 in procedure @2 not found', NULL, NULL); +('dyn_trig_not_found', 'modify_trigger', 'dyn.e', NULL, 8, 147, NULL, 'Trigger @1 not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 148, NULL, 'Only one data type change to the domain @1 allowed at a time', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 149, NULL, 'Only one data type change to the field @1 allowed at a time', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 150, NULL, 'STORE RDB$FILES failed', NULL, NULL); -(NULL, NULL, 'dyn_mod.epp', NULL, 8, 151, NULL, 'Character set @1 not found', NULL, NULL); -(NULL, NULL, 'dyn_mod.epp', NULL, 8, 152, NULL, 'Collation @1 not found', NULL, NULL); +('dyn_charset_not_found', NULL, 'dyn_mod.epp', NULL, 8, 151, NULL, 'Character set @1 not found', NULL, NULL); +('dyn_collation_not_found', NULL, 'dyn_mod.epp', NULL, 8, 152, NULL, 'Collation @1 not found', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 153, NULL, 'ERASE RDB$LOG_FILES failed', NULL, NULL); (NULL, NULL, 'dyn.c', NULL, 8, 154, NULL, 'STORE RDB$LOG_FILES failed', NULL, NULL); -(NULL, NULL, 'dyn_mod.epp', NULL, 8, 155, NULL, 'Role @1 not found', NULL, NULL); +('dyn_role_not_found', NULL, 'dyn_mod.epp', NULL, 8, 155, NULL, 'Role @1 not found', NULL, NULL); (NULL, NULL, 'dyn.e', NULL, 8, 156, NULL, 'Difference file lookup failed', NULL, NULL); (NULL, 'define_shadow', 'dyn.e', NULL, 8, 157, NULL, 'DEFINE SHADOW failed', NULL, NULL); (NULL, 'modify_role', 'dyn_mod.epp', NULL, 8, 158, NULL, 'MODIFY RDB$ROLES failed', NULL, NULL); -(NULL, 'get_string', 'dyn.e', NULL, 8, 159, NULL, 'Name longer than database column size', NULL, NULL); +('dyn_name_longer', 'get_string', 'dyn.e', NULL, 8, 159, NULL, 'Name longer than database column size', NULL, NULL); (NULL, 'modify_global_field', 'dyn', NULL, 8, 160, NULL, '"Only one constraint allowed for a domain"', NULL, NULL); (NULL, 'generate_field_position', 'dyn.e', NULL, 8, 162, NULL, 'Looking up column position failed', NULL, NULL); (NULL, 'define_relation', 'dyn.e', NULL, 8, 163, NULL, 'A node name is not permitted in a table with external file definition', NULL, NULL); @@ -2107,7 +2109,7 @@ COMMIT WORK; ('dyn_virmemexh', 'DYN_modify_sql/global_field', 'dyn_mod.e', NULL, 8, 211, NULL, 'unable to allocate memory from the operating system', NULL, NULL); ('dyn_zero_len_id', 'DYN_create_exception', 'dyn_def.e', NULL, 8, 212, NULL, 'Zero length identifiers are not allowed', NULL, NULL); ('del_gen_fail', 'DYN_delete_generator', 'dyn_del.e', NULL, 8, 213, NULL, 'ERASE RDB$GENERATORS failed', NULL, NULL); -('gen_not_found', 'DYN_delete_generator', 'dyn_del.e', NULL, 8, 214, NULL, 'Generator @1 not found', NULL, NULL); +('dyn_gen_not_found', 'DYN_delete_generator', 'dyn_del.e', NULL, 8, 214, NULL, 'Generator @1 not found', NULL, NULL); (NULL, 'change_backup_mode', 'dyn_mod.epp', NULL, 8, 215, NULL, 'Difference file is not defined', NULL, NULL); (NULL, 'change_backup_mode', 'dyn_mod.epp', NULL, 8, 216, NULL, 'Difference file is already defined', NULL, NULL); (NULL, 'DYN_define_difference', 'dyn_def.epp', NULL, 8, 217, NULL, 'Database is already in the physical backup mode', NULL, NULL); @@ -2134,7 +2136,7 @@ COMMIT WORK; (NULL, 'DYN_delete_collation', 'dyn_del.epp', NULL, 8, 238, NULL, 'Cannot delete default collation of CHARACTER SET @1', NULL, NULL); (NULL, NULL, 'dyn_del.epp', NULL, 8, 239, NULL, 'Domain @1 is used in procedure @2 (parameter name @3) and cannot be dropped', NULL, NULL); (NULL, 'DYN_define_index', 'dyn_def.epp', NULL, 8, 240, NULL, 'Field @1 cannot be used twice in index @2', NULL, NULL); -(NULL, 'DYN_define_index', 'dyn_def.epp', NULL, 8, 241, NULL, 'Table @1 not found', NULL, NULL); +('dyn_table_not_found', 'DYN_define_index', 'dyn_def.epp', NULL, 8, 241, NULL, 'Table @1 not found', NULL, NULL); (NULL, 'DYN_define_index', 'dyn_def.epp', NULL, 8, 242, NULL, 'attempt to reference a view (@1) in a foreign key', NULL, NULL); (NULL, 'DYN_delete_collation', 'dyn_del.epp', NULL, 8, 243, NULL, 'Collation @1 is used in procedure @2 (parameter name @3) and cannot be dropped', NULL, NULL); -- Do not change the arguments of the previous DYN messages. @@ -2493,6 +2495,10 @@ ERROR: Backup incomplete', NULL, NULL); (NULL, 'burp_usage', 'burp.cpp', NULL, 12, 323, NULL, 'backup options are:', NULL, NULL); (NULL, 'burp_usage', 'burp.cpp', NULL, 12, 324, NULL, 'restore options are:', NULL, NULL); (NULL, 'burp_usage', 'burp.cpp', NULL, 12, 325, NULL, 'general options are:', NULL, NULL); +(NULL, 'write_packages', 'backup.epp', NULL, 12, 335, NULL, 'writing package @1', NULL, NULL); +(NULL, 'BACKUP_backup', 'backup.epp', NULL, 12, 336, NULL, 'writing packages', NULL, NULL); +(NULL, 'get_package', 'restore.epp', NULL, 12, 337, NULL, 'restoring package @1', NULL, NULL); +(NULL, 'get_package', 'restore.epp', NULL, 12, 338, NULL, 'package', NULL, NULL); -- SQLERR (NULL, NULL, NULL, NULL, 13, 1, NULL, 'Firebird error', NULL, NULL); (NULL, NULL, NULL, NULL, 13, 74, NULL, 'Rollback not performed', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index d90f4b1cf4..da081ee9e4 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -673,6 +673,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, 'HY', '000', 0, 665, 'out_of_temp_space', NULL, NULL) (-901, '42', '000', 0, 666, 'eds_expl_tran_ctrl', NULL, NULL) (-902, '28', '000', 0, 667, 'no_trusted_spb', NULL, NULL) +(-901, '42', '000', 0, 668, 'package_name', NULL, NULL) +(-901, '22', '006', 0, 669, 'cannot_make_not_null', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) @@ -733,7 +735,21 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-817, '42', '000', 7, 30, 'dsql_incompatible_trigger_type', NULL, NULL) (-817, '42', '000', 7, 31, 'dsql_db_trigger_type_cant_change', NULL, NULL) -- DYN +(-901, '42', '000', 8, 37, 'dyn_filter_not_found', NULL, NULL) +(-901, '42', '000', 8, 41, 'dyn_func_not_found', NULL, NULL) +(-901, '42', '000', 8, 48, 'dyn_index_not_found', NULL, NULL) +(-901, '42', '000', 8, 54, 'dyn_view_not_found', NULL, NULL) +(-901, '42', '000', 8, 89, 'dyn_domain_not_found', NULL, NULL) +(-901, '42', '000', 8, 109, 'dyn_cant_modify_auto_trig', NULL, NULL) (-901, '42', 'S01', 8, 132, 'dyn_dup_table', NULL, NULL) +(-901, '42', '000', 8, 140, 'dyn_proc_not_found', NULL, NULL) +(-901, '42', '000', 8, 144, 'dyn_exception_not_found', NULL, NULL) +(-901, '42', '000', 8, 146, 'dyn_proc_param_not_found', NULL, NULL) +(-901, '42', '000', 8, 147, 'dyn_trig_not_found', NULL, NULL) +(-901, '42', '000', 8, 151, 'dyn_charset_not_found', NULL, NULL) +(-901, '42', '000', 8, 152, 'dyn_collation_not_found', NULL, NULL) +(-901, '42', '000', 8, 155, 'dyn_role_not_found', NULL, NULL) +(-901, '42', '000', 8, 159, 'dyn_name_longer', NULL, NULL) (-901, '42', 'S22', 8, 176, 'dyn_column_does_not_exist', NULL, NULL) (-901, '28', '000', 8, 188, 'dyn_role_does_not_exist', NULL, NULL) (-901, '28', '000', 8, 189, 'dyn_no_grant_admin_opt', NULL, NULL) @@ -752,9 +768,11 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-829, '42', '000', 8, 209, 'dyn_invalid_dtype_conversion', NULL, NULL) (-829, '42', '000', 8, 210, 'dyn_dtype_conv_invalid', NULL, NULL) (-901, '42', '000', 8, 212, 'dyn_zero_len_id', NULL, NULL) +(-901, '42', '000', 8, 214, 'dyn_gen_not_found', NULL, NULL) (-829, '2C', '000', 8, 221, 'max_coll_per_charset', NULL, NULL) (-829, 'HY', '000', 8, 222, 'invalid_coll_attr', NULL, NULL) (-901, 'HY', '000', 8, 232, 'dyn_wrong_gtt_scope', NULL, NULL) +(-901, '42', '000', 8, 241, 'dyn_table_not_found', NULL, NULL) (-829, '42', '000', 8, 244, 'dyn_scale_too_big', NULL, NULL) (-829, '42', '000', 8, 245, 'dyn_precision_too_small', NULL, NULL) (106, '42', '000', 8, 247, 'dyn_miss_priv_warning', NULL, NULL) diff --git a/src/plugins/udr_engine/UdrEngine.cpp b/src/plugins/udr_engine/UdrEngine.cpp new file mode 100644 index 0000000000..7198d62aa7 --- /dev/null +++ b/src/plugins/udr_engine/UdrEngine.cpp @@ -0,0 +1,790 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "FirebirdUdr.h" +#include "FirebirdUdrCpp.h" +#include "FirebirdPluginApi.h" +#include "FirebirdApi.h" +#include "FirebirdExternalApi.h" +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" +#include "../common/classes/init.h" +#include "../common/classes/fb_string.h" +#include "../common/classes/GenericMap.h" +#include "../common/classes/objects_array.h" +#include "../jrd/os/mod_loader.h" +#include "../jrd/os/path_utils.h" + + +namespace Firebird +{ + namespace Udr + { +//------------------------------------------------------------------------------ + + +struct Node +{ + Node() + : module(*getDefaultMemoryPool()) + { + } + + PathName module; +}; + +struct FunctionNode : public Node +{ + FunctionFactory* factory; + FunctionNode* next; +}; + +struct ProcedureNode : public Node +{ + ProcedureFactory* factory; + ProcedureNode* next; +}; + +struct TriggerNode : public Node +{ + TriggerFactory* factory; + TriggerNode* next; +}; + + +class Engine : public ExternalEngine, public PermanentStorage +{ +public: + Engine() + : PermanentStorage(*getDefaultMemoryPool()), + functions(getPool()), + procedures(getPool()), + triggers(getPool()) + { + } + + virtual ~Engine(); + +public: + void loadModule(const string& str, PathName* moduleName, string* entryPoint, string* info); + template ObjType* getChild( + GenericMap > >& children, SharedObjType* sharedObj, + ExternalContext* context, NodeType* nodes, SortedArray& sharedObjs, + const PathName& moduleName); + template void deleteChildren( + GenericMap > >& children); + + template T* findNode(T* nodes, const PathName& moduleName, + T2* metaInfo); + +private: + template T2* getNode(T* nodes, const PathName& moduleName, + T3* metaInfo); + +public: + virtual int FB_CALL getVersion(Error* error); + virtual void FB_CALL open(Error* error, ExternalContext* context, Utf8* name, uint nameSize); + virtual void FB_CALL openAttachment(Error* error, ExternalContext* context); + virtual void FB_CALL closeAttachment(Error* error, ExternalContext* context); + virtual ExternalFunction* FB_CALL makeFunction(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body); + virtual ExternalProcedure* FB_CALL makeProcedure(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body); + virtual ExternalTrigger* FB_CALL makeTrigger(Error* error, ExternalContext* context, + const char* name, const char* entryPoint, const char* body, const char* table, + ExternalTrigger::Type type); + +public: + virtual void FB_CALL dispose(Error* error); + +private: + Mutex childrenMutex; + +public: + SortedArray functions; + SortedArray procedures; + SortedArray triggers; +}; + + +class ModulesMap : public GenericMap > > +{ +public: + ModulesMap(MemoryPool& p) + : GenericMap > >(p) + { + } + + ~ModulesMap(); +}; + + +//-------------------------------------- + + +static ModuleLoader::Module* libraryModule = NULL; + +static GlobalPtr libraryName; +static GlobalPtr > paths; + +static GlobalPtr modulesMutex; +static GlobalPtr modules; + +static InitInstance loadingModule; +static FunctionNode* registeredFunctions = NULL; +static ProcedureNode* registeredProcedures = NULL; +static TriggerNode* registeredTriggers = NULL; + + +//-------------------------------------- + + +class SharedFunction : public ExternalFunction +{ +public: + SharedFunction(Engine* aEngine, const string& aPackage, const string& aName, + const string& aEntryPoint, const string& aBody) + : engine(aEngine), + package(*getDefaultMemoryPool(), aPackage), + name(*getDefaultMemoryPool(), aName), + entryPoint(*getDefaultMemoryPool()), + body(*getDefaultMemoryPool(), aBody), + moduleName(*getDefaultMemoryPool()), + info(*getDefaultMemoryPool()), + children(*getDefaultMemoryPool()) + { + engine->loadModule(aEntryPoint, &moduleName, &entryPoint, &info); + metaInfo.package = package.nullStr(); + metaInfo.name = name.c_str(); + metaInfo.entryPoint = entryPoint.c_str(); + metaInfo.body = body.c_str(); + metaInfo.info = info.c_str(); + + engine->findNode(registeredFunctions, moduleName, &metaInfo); + } + + virtual ~SharedFunction() + { + engine->deleteChildren(children); + } + +public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + +public: + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) + { + strncpy(name, context->getClientCharSet(), nameSize); + + try + { + ExternalFunction* function = engine->getChild( + children, this, context, registeredFunctions, engine->functions, moduleName); + if (function) + function->getCharSet(error, context, name, nameSize); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + } + } + + virtual void FB_CALL execute(Error* error, ExternalContext* context, Values* params, + Value* result) + { + ExternalFunction* function = engine->getChild( + children, this, context, registeredFunctions, engine->functions, moduleName); + if (function) + function->execute(error, context, params, result); + } + +public: + MetaInfo metaInfo; + Engine* engine; + string package; + string name; + string entryPoint; + string body; + PathName moduleName; + string info; + GenericMap > > children; +}; + + +//-------------------------------------- + + +class SharedProcedure : public ExternalProcedure +{ +public: + SharedProcedure(Engine* aEngine, const string& aPackage, const string& aName, + const string& aEntryPoint, const string& aBody) + : engine(aEngine), + package(*getDefaultMemoryPool(), aPackage), + name(*getDefaultMemoryPool(), aName), + entryPoint(*getDefaultMemoryPool(), aEntryPoint), + body(*getDefaultMemoryPool(), aBody), + moduleName(*getDefaultMemoryPool()), + info(*getDefaultMemoryPool()), + children(*getDefaultMemoryPool()) + { + engine->loadModule(aEntryPoint, &moduleName, &entryPoint, &info); + metaInfo.package = package.nullStr(); + metaInfo.name = name.c_str(); + metaInfo.entryPoint = entryPoint.c_str(); + metaInfo.body = body.c_str(); + metaInfo.info = info.c_str(); + + engine->findNode(registeredProcedures, moduleName, &metaInfo); + } + + virtual ~SharedProcedure() + { + engine->deleteChildren(children); + } + +public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + +public: + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) + { + strncpy(name, context->getClientCharSet(), nameSize); + + try + { + ExternalProcedure* procedure = engine->getChild( + children, this, context, registeredProcedures, engine->procedures, moduleName); + if (procedure) + procedure->getCharSet(error, context, name, nameSize); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + } + } + + virtual ExternalResultSet* FB_CALL open(Error* error, ExternalContext* context, + Values* params, Values* results) + { + try + { + ExternalProcedure* procedure = engine->getChild( + children, this, context, registeredProcedures, engine->procedures, moduleName); + return procedure ? procedure->open(error, context, params, results) : NULL; + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + return NULL; + } + } + +public: + MetaInfo metaInfo; + Engine* engine; + string package; + string name; + string entryPoint; + string body; + PathName moduleName; + string info; + GenericMap > > children; +}; + + +//-------------------------------------- + + +class SharedTrigger : public ExternalTrigger +{ +public: + SharedTrigger(Engine* aEngine, const string& aName, const string& aEntryPoint, + const string& aBody, const string& aTable, ExternalTrigger::Type aType) + : engine(aEngine), + name(*getDefaultMemoryPool(), aName), + entryPoint(*getDefaultMemoryPool(), aEntryPoint), + body(*getDefaultMemoryPool(), aBody), + moduleName(*getDefaultMemoryPool()), + info(*getDefaultMemoryPool()), + table(*getDefaultMemoryPool(), aTable), + type(aType), + children(*getDefaultMemoryPool()) + { + engine->loadModule(aEntryPoint, &moduleName, &entryPoint, &info); + metaInfo.package = NULL; + metaInfo.name = name.c_str(); + metaInfo.entryPoint = entryPoint.c_str(); + metaInfo.body = body.c_str(); + metaInfo.info = info.c_str(); + metaInfo.type = type; + metaInfo.table = table.nullStr(); + + engine->findNode(registeredTriggers, moduleName, &metaInfo); + } + + virtual ~SharedTrigger() + { + engine->deleteChildren(children); + } + +public: + virtual void FB_CALL dispose(Firebird::Error* error) + { + delete this; + } + +public: + virtual void FB_CALL getCharSet(Error* error, ExternalContext* context, + Utf8* name, uint nameSize) + { + strncpy(name, context->getClientCharSet(), nameSize); + + try + { + ExternalTrigger* trigger = engine->getChild( + children, this, context, registeredTriggers, engine->triggers, moduleName); + if (trigger) + trigger->getCharSet(error, context, name, nameSize); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + } + } + + virtual void FB_CALL execute(Error* error, ExternalContext* context, + ExternalTrigger::Action action, const Values* oldValues, Values* newValues) + { + ExternalTrigger* trigger = engine->getChild( + children, this, context, registeredTriggers, engine->triggers, moduleName); + if (trigger) + trigger->execute(error, context, action, oldValues, newValues); + } + +public: + TriggerMetaInfo metaInfo; + Engine* engine; + string name; + string entryPoint; + string body; + PathName moduleName; + string info; + string table; + ExternalTrigger::Type type; + GenericMap > > children; +}; + + +//-------------------------------------- + + +extern "C" void fbUdrRegFunction(FunctionFactory* factory) +{ + FunctionNode* node = new FunctionNode(); + node->module = loadingModule(); + node->factory = factory; + node->next = registeredFunctions; + registeredFunctions = node; +} + + +extern "C" void fbUdrRegProcedure(ProcedureFactory* factory) +{ + ProcedureNode* node = new ProcedureNode(); + node->module = loadingModule(); + node->factory = factory; + node->next = registeredProcedures; + registeredProcedures = node; +} + + +extern "C" void fbUdrRegTrigger(TriggerFactory* factory) +{ + TriggerNode* node = new TriggerNode(); + node->module = loadingModule(); + node->factory = factory; + node->next = registeredTriggers; + registeredTriggers = node; +} + + +extern "C" void* fbUdrGetFunction(const char* symbol) +{ + return libraryModule->findSymbol(symbol); +} + + +ModulesMap::~ModulesMap() +{ + FunctionNode* func = registeredFunctions; + + while (func) + { + FunctionNode* del = func; + func = func->next; + delete del; + } + + ProcedureNode* proc = registeredProcedures; + + while (proc) + { + ProcedureNode* del = proc; + proc = proc->next; + delete del; + } + + TriggerNode* trig = registeredTriggers; + + while (trig) + { + TriggerNode* del = trig; + trig = trig->next; + delete del; + } + + registeredFunctions = NULL; + registeredProcedures = NULL; + registeredTriggers = NULL; + + Accessor accessor(this); + for (bool cont = accessor.getFirst(); cont; cont = accessor.getNext()) + delete accessor.current()->second; + + delete libraryModule; +} + + +//-------------------------------------- + + +Engine::~Engine() +{ +} + + +void Engine::loadModule(const string& str, PathName* moduleName, string* entryPoint, string* info) +{ + size_t pos = str.find('!'); + if (pos == string::npos) + { + static const ISC_STATUS statusVector[] = { + isc_arg_gds, + isc_random, + isc_arg_string, (ISC_STATUS) "Invalid entry point", + isc_arg_end + }; + + ThrowError::check(statusVector); + } + + *moduleName = PathName(str.substr(0, pos).c_str()); + // Do not allow module names with directory separators as a security measure. + if (moduleName->find_first_of("/\\") != string::npos) + { + static const ISC_STATUS statusVector[] = { + isc_arg_gds, + isc_random, + isc_arg_string, (ISC_STATUS) "Invalid module name", + isc_arg_end + }; + + ThrowError::check(statusVector); + } + + *entryPoint = str.substr(pos + 1); + + size_t n = entryPoint->find('!'); + *info = (n == string::npos ? "" : entryPoint->substr(n + 1)); + *entryPoint = (n == string::npos ? *entryPoint : entryPoint->substr(0, n)); + + MutexLockGuard guard(modulesMutex); + + if (modules->exist(*moduleName)) + return; + + loadingModule() = *moduleName; + + for (ObjectsArray::iterator i = paths->begin(); i != paths->end(); ++i) + { + PathName path; + PathUtils::concatPath(path, *i, *moduleName); + + ModuleLoader::Module* module = ModuleLoader::loadModule(path); + if (!module) + { + ModuleLoader::doctorModuleExtention(path); + module = ModuleLoader::loadModule(path); + } + + if (module) + { + modules->put(*moduleName, module); + break; + } + else + { + static const ISC_STATUS statusVector[] = { + isc_arg_gds, + isc_random, + isc_arg_string, (ISC_STATUS) "Module not found", + isc_arg_end + }; + + ThrowError::check(statusVector); + } + } +} + + +template ObjType* Engine::getChild( + GenericMap > >& children, SharedObjType* sharedObj, + ExternalContext* context, NodeType* nodes, SortedArray& sharedObjs, + const PathName& moduleName) +{ + MutexLockGuard guard(childrenMutex); + + if (!sharedObjs.exist(sharedObj)) + sharedObjs.add(sharedObj); + + ObjType* obj; + if (!children.get(context, obj)) + { + obj = getNode(nodes, moduleName, &sharedObj->metaInfo); + if (obj) + children.put(context, obj); + } + + return obj; +} + + +template void Engine::deleteChildren( + GenericMap > >& children) +{ + // No need to lock childrenMutex as if there are more threads simultaneously accessing + // this children in this moment there will be a memory corruption anyway. + + typedef typename GenericMap > >::Accessor ChildrenAccessor; + ChildrenAccessor accessor(&children); + for (bool found = accessor.getFirst(); found; found = accessor.getNext()) + delete accessor.current()->second; +} + + +template T* Engine::findNode(T* nodes, + const PathName& moduleName, T2* params) +{ + string entryPoint(params->entryPoint); + + for (T* node = nodes; node; node = node->next) + { + if (node->module == moduleName && entryPoint == node->factory->getName()) + return node; + } + + static const ISC_STATUS statusVector[] = { + isc_arg_gds, + isc_random, + isc_arg_string, (ISC_STATUS) "Entry point not found", + isc_arg_end + }; + + ThrowError::check(statusVector); + + return NULL; +} + + +template T2* Engine::getNode(T* nodes, + const PathName& moduleName, T3* params) +{ + T* node = findNode(nodes, moduleName, params); + return node->factory->newItem(params); +} + + +int FB_CALL Engine::getVersion(Error* error) +{ + return EXTERNAL_VERSION_1; +} + + +void FB_CALL Engine::open(Error* error, ExternalContext* context, Utf8* name, uint nameSize) +{ + strncpy(name, "UTF-8", nameSize); +} + + +void FB_CALL Engine::openAttachment(Error* error, ExternalContext* context) +{ +} + + +void FB_CALL Engine::closeAttachment(Error* error, ExternalContext* context) +{ + MutexLockGuard guard(childrenMutex); + + for (SortedArray::iterator i = functions.begin(); i != functions.end(); ++i) + { + ExternalFunction* function; + if ((*i)->children.get(context, function)) + { + function->dispose(error); + (*i)->children.remove(context); + } + } + + for (SortedArray::iterator i = procedures.begin(); i != procedures.end(); ++i) + { + ExternalProcedure* procedure; + if ((*i)->children.get(context, procedure)) + { + procedure->dispose(error); + (*i)->children.remove(context); + } + } + + for (SortedArray::iterator i = triggers.begin(); i != triggers.end(); ++i) + { + ExternalTrigger* trigger; + if ((*i)->children.get(context, trigger)) + { + trigger->dispose(error); + (*i)->children.remove(context); + } + } +} + + +ExternalFunction* FB_CALL Engine::makeFunction(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body) +{ + try + { + return new SharedFunction(this, (package ? package : ""), name, + (entryPoint ? entryPoint : ""), (body ? body : "")); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + return NULL; + } +} + + +ExternalProcedure* FB_CALL Engine::makeProcedure(Error* error, ExternalContext* context, + const char* package, const char* name, const char* entryPoint, const char* body) +{ + try + { + return new SharedProcedure(this, (package ? package : ""), name, + (entryPoint ? entryPoint : ""), (body ? body : "")); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + return NULL; + } +} + + +ExternalTrigger* FB_CALL Engine::makeTrigger(Error* error, ExternalContext* context, + const char* name, const char* entryPoint, const char* body, const char* table, + ExternalTrigger::Type type) +{ + try + { + return new SharedTrigger(this, name, (entryPoint ? entryPoint : ""), (body ? body : ""), + table, type); + } + catch (const ThrowError::Exception& e) + { + e.stuff(error); + return NULL; + } +} + + +void FB_CALL Engine::dispose(Error* error) +{ + delete this; +} + + +//-------------------------------------- + + +class ExternalEngineFactoryImpl : public ExternalEngineFactory +{ +public: + virtual ExternalEngine* FB_CALL createEngine(Error* error, int version, + const char* name) + { + if (strcmp(name, "UDR") == 0) + return new Engine(); + else + { + const char* const msg = "Engine not implemented"; + + error->addCode(isc_arg_gds); + error->addCode(isc_random); + error->addString(msg, strlen(msg)); + return NULL; + } + } +} factory; + + +extern "C" void FB_PLUGIN_ENTRY_POINT(Error* error, Plugin* plugin) +{ + if (plugin->getLibraryName()) + libraryName->assign(plugin->getLibraryName()); + libraryModule = ModuleLoader::loadModule(libraryName); + + for (int count = plugin->getConfigInfoCount(), i = 0; i < count; ++i) + { + const char* key; + const char* value; + + plugin->getConfigInfo(error, i, &key, &value); + if (strcmp(key, "path") == 0) + paths->add(value); + } + + plugin->setExternalEngineFactory(&factory); +} + + +//------------------------------------------------------------------------------ + } // namespace Udr +} // namespace Firebird diff --git a/src/plugins/udr_engine/udr_engine.conf b/src/plugins/udr_engine/udr_engine.conf new file mode 100644 index 0000000000..8cc99d34c2 --- /dev/null +++ b/src/plugins/udr_engine/udr_engine.conf @@ -0,0 +1,13 @@ + + plugin_module UDR_engine + + + + filename $(this)/udr_engine + plugin_config UDR_config + + + + path $(this)/udr + + diff --git a/src/remote/inter_proto.h b/src/remote/inter_proto.h index 970bfb32f1..82bef826e7 100644 --- a/src/remote/inter_proto.h +++ b/src/remote/inter_proto.h @@ -28,8 +28,8 @@ extern "C" { #endif -ISC_STATUS REM_attach_database(ISC_STATUS* user_status, const TEXT* file_name, struct Rdb** handle, - SSHORT dpb_length, const SCHAR* dpb); +ISC_STATUS REM_attach_database(ISC_STATUS* user_status, FB_API_HANDLE public_handle, + const TEXT* file_name, struct Rdb** handle, SSHORT dpb_length, const SCHAR* dpb); ISC_STATUS REM_blob_info(ISC_STATUS* user_status, struct Rbl** blob_handle, SSHORT item_length, const UCHAR* items, SSHORT buffer_length, UCHAR* buffer); @@ -44,8 +44,8 @@ ISC_STATUS REM_compile_request(ISC_STATUS* user_status, struct Rdb** db_handle, ISC_STATUS REM_create_blob2(ISC_STATUS* user_status, struct Rdb** db_handle, struct Rtr** rtr_handle, struct Rbl** blob_handle, BID blob_id, USHORT bpb_length, const UCHAR* bpb); -ISC_STATUS REM_create_database(ISC_STATUS* user_status, const TEXT* file_name, struct Rdb** handle, - SSHORT dpb_length, const SCHAR* dpb); +ISC_STATUS REM_create_database(ISC_STATUS* user_status, FB_API_HANDLE public_handle, + const TEXT* file_name, struct Rdb** handle, SSHORT dpb_length, const SCHAR* dpb); ISC_STATUS REM_database_info(ISC_STATUS* user_status, struct Rdb** handle, SSHORT item_length, const UCHAR* items, SSHORT buffer_length, UCHAR* buffer); @@ -133,8 +133,8 @@ ISC_STATUS REM_start_and_send(ISC_STATUS* user_status, struct Rrq** req_handle, struct Rtr** rtr_handle, USHORT msg_type, USHORT msg_length, UCHAR* msg, SSHORT level); ISC_STATUS REM_start_request(ISC_STATUS* user_status, struct Rrq** req_handle, struct Rtr** rtr_handle, USHORT level); -ISC_STATUS REM_start_transaction(ISC_STATUS* user_status, struct Rtr** rtr_handle, - SSHORT count, struct Rdb** db_handle, SSHORT tpb_length, const UCHAR* tpb); +ISC_STATUS REM_start_transaction(ISC_STATUS* user_status, FB_API_HANDLE public_handle, + struct Rtr** rtr_handle, SSHORT count, struct Rdb** db_handle, SSHORT tpb_length, const UCHAR* tpb); ISC_STATUS REM_transact_request(ISC_STATUS* user_status, struct Rdb** db_handle, struct Rtr** rtr_handle, USHORT blr_length, UCHAR* blr, USHORT in_msg_length, UCHAR* in_msg, USHORT out_msg_length, UCHAR* out_msg); diff --git a/src/remote/interface.cpp b/src/remote/interface.cpp index dec6e549c5..61c9831894 100644 --- a/src/remote/interface.cpp +++ b/src/remote/interface.cpp @@ -263,6 +263,7 @@ inline bool defer_packet(rem_port* port, PACKET* packet, ISC_STATUS* status, boo ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status, + FB_API_HANDLE public_handle, const TEXT* filename, Rdb** handle, SSHORT dpb_length, @@ -796,6 +797,7 @@ ISC_STATUS GDS_CREATE_BLOB2(ISC_STATUS* user_status, ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status, + FB_API_HANDLE public_handle, const TEXT* filename, Rdb** handle, SSHORT dpb_length, @@ -4249,6 +4251,7 @@ ISC_STATUS GDS_START(ISC_STATUS* user_status, ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS* user_status, + FB_API_HANDLE public_handle, Rtr** rtr_handle, SSHORT /*count*/, Rdb** db_handle,