├── .gitignore ├── LICENSE ├── Makefile.in ├── README.FreeBSD ├── README.Linux ├── README.MacOSX ├── README.md ├── TODO.txt ├── aclocal.m4 ├── casstcl.tcl ├── configure.in ├── demos ├── README.md ├── blobbie.tcl ├── connect.tcl ├── demo.cql ├── example.tcl ├── gen-dataset.tcl ├── numbers.tcl ├── run.tcl └── selnumbers.tcl ├── generic ├── casstcl.h ├── casstcl_batch.c ├── casstcl_batch.h ├── casstcl_cassandra.c ├── casstcl_cassandra.h ├── casstcl_consistency.c ├── casstcl_consistency.h ├── casstcl_error.c ├── casstcl_error.h ├── casstcl_event.c ├── casstcl_event.h ├── casstcl_future.c ├── casstcl_future.h ├── casstcl_log.c ├── casstcl_log.h ├── casstcl_prepared.c ├── casstcl_prepared.h ├── casstcl_types.c ├── casstcl_types.h └── tclcasstcl.c ├── pkgIndex.tcl.in ├── tclconfig ├── ChangeLog ├── README.txt ├── install-sh └── tcl.m4 ├── tests ├── all.tcl └── cass.test └── update_ver.sh /.gitignore: -------------------------------------------------------------------------------- 1 | autom4te.cache 2 | configure 3 | config.log 4 | config.status 5 | Makefile 6 | pkgIndex.tcl 7 | *.o 8 | lib*.so 9 | lib*.so.* 10 | *.dylib 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, FlightAware LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | * Neither the name of the FlightAware LLC nor the names of its 16 | contributors may be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | # Makefile.in -- 2 | # 3 | # This file is a Makefile for Sample TEA Extension. If it has the name 4 | # "Makefile.in" then it is a template for a Makefile; to generate the 5 | # actual Makefile, run "./configure", which is a configuration script 6 | # generated by the "autoconf" program (constructs like "@foo@" will get 7 | # replaced in the actual Makefile. 8 | # 9 | # Copyright (c) 1999 Scriptics Corporation. 10 | # Copyright (c) 2002-2005 ActiveState Corporation. 11 | # 12 | # See the file "license.terms" for information on usage and redistribution 13 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 | # 15 | # RCS: @(#) $Id: Makefile.in,v 1.2 2007-03-11 22:43:12 karl Exp $ 16 | 17 | #======================================================================== 18 | # Add additional lines to handle any additional AC_SUBST cases that 19 | # have been added in a customized configure script. 20 | #======================================================================== 21 | 22 | #SAMPLE_NEW_VAR = @SAMPLE_NEW_VAR@ 23 | 24 | #======================================================================== 25 | # Nothing of the variables below this line should need to be changed. 26 | # Please check the TARGETS section below to make sure the make targets 27 | # are correct. 28 | #======================================================================== 29 | 30 | #======================================================================== 31 | # The names of the source files is defined in the configure script. 32 | # The object files are used for linking into the final library. 33 | # This will be used when a dist target is added to the Makefile. 34 | # It is not important to specify the directory, as long as it is the 35 | # $(srcdir) or in the generic, win or unix subdirectory. 36 | #======================================================================== 37 | 38 | PKG_SOURCES = @PKG_SOURCES@ 39 | PKG_OBJECTS = @PKG_OBJECTS@ 40 | 41 | PKG_STUB_SOURCES = @PKG_STUB_SOURCES@ 42 | PKG_STUB_OBJECTS = @PKG_STUB_OBJECTS@ 43 | 44 | #======================================================================== 45 | # PKG_TCL_SOURCES identifies Tcl runtime files that are associated with 46 | # this package that need to be installed, if any. 47 | #======================================================================== 48 | 49 | PKG_TCL_SOURCES = @PKG_TCL_SOURCES@ 50 | 51 | #======================================================================== 52 | # This is a list of public header files to be installed, if any. 53 | #======================================================================== 54 | 55 | PKG_HEADERS = @PKG_HEADERS@ 56 | 57 | #======================================================================== 58 | # "PKG_LIB_FILE" refers to the library (dynamic or static as per 59 | # configuration options) composed of the named objects. 60 | #======================================================================== 61 | 62 | PKG_LIB_FILE = @PKG_LIB_FILE@ 63 | PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@ 64 | 65 | lib_BINARIES = $(PKG_LIB_FILE) 66 | BINARIES = $(lib_BINARIES) 67 | 68 | SHELL = @SHELL@ 69 | 70 | srcdir = @srcdir@ 71 | prefix = @prefix@ 72 | exec_prefix = @exec_prefix@ 73 | 74 | bindir = @bindir@ 75 | libdir = @libdir@ 76 | includedir = @includedir@ 77 | datarootdir = @datarootdir@ 78 | datadir = @datadir@ 79 | mandir = @mandir@ 80 | 81 | DESTDIR = 82 | 83 | PKG_MAJ_MIN = @PKG_MAJ_MIN@ 84 | PKG_DIR = $(PACKAGE_NAME)$(PKG_MAJ_MIN) 85 | pkgdatadir = $(datadir)/$(PKG_DIR) 86 | pkglibdir = $(libdir)/$(PKG_DIR) 87 | pkgincludedir = $(includedir)/$(PKG_DIR) 88 | 89 | top_builddir = . 90 | 91 | INSTALL = @INSTALL@ 92 | INSTALL_PROGRAM = @INSTALL_PROGRAM@ 93 | INSTALL_DATA = @INSTALL_DATA@ 94 | INSTALL_SCRIPT = @INSTALL_SCRIPT@ 95 | 96 | PACKAGE_NAME = @PACKAGE_NAME@ 97 | PACKAGE_VERSION = @PACKAGE_VERSION@ 98 | CC = @CC@ 99 | CFLAGS_DEFAULT = @CFLAGS_DEFAULT@ 100 | CFLAGS_WARNING = @CFLAGS_WARNING@ 101 | EXEEXT = @EXEEXT@ 102 | LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@ 103 | MAKE_LIB = @MAKE_LIB@ 104 | MAKE_SHARED_LIB = @MAKE_SHARED_LIB@ 105 | MAKE_STATIC_LIB = @MAKE_STATIC_LIB@ 106 | MAKE_STUB_LIB = @MAKE_STUB_LIB@ 107 | OBJEXT = @OBJEXT@ 108 | RANLIB = @RANLIB@ 109 | RANLIB_STUB = @RANLIB_STUB@ 110 | SHLIB_CFLAGS = @SHLIB_CFLAGS@ 111 | SHLIB_LD = @SHLIB_LD@ 112 | SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ 113 | STLIB_LD = @STLIB_LD@ 114 | #TCL_DEFS = @TCL_DEFS@ 115 | TCL_BIN_DIR = @TCL_BIN_DIR@ 116 | TCL_SRC_DIR = @TCL_SRC_DIR@ 117 | #TK_BIN_DIR = @TK_BIN_DIR@ 118 | #TK_SRC_DIR = @TK_SRC_DIR@ 119 | 120 | # Not used, but retained for reference of what libs Tcl required 121 | #TCL_LIBS = @TCL_LIBS@ 122 | 123 | #======================================================================== 124 | # TCLLIBPATH seeds the auto_path in Tcl's init.tcl so we can test our 125 | # package without installing. The other environment variables allow us 126 | # to test against an uninstalled Tcl. Add special env vars that you 127 | # require for testing here (like TCLX_LIBRARY). 128 | #======================================================================== 129 | 130 | EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR) 131 | #EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR):$(TK_BIN_DIR) 132 | TCLLIBPATH = $(top_builddir) 133 | TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \ 134 | @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \ 135 | PATH="$(EXTRA_PATH):$(PATH)" \ 136 | TCLLIBPATH="$(TCLLIBPATH)" 137 | # TK_LIBRARY=`@CYGPATH@ $(TK_SRC_DIR)/library` 138 | 139 | TCLSH_PROG = @TCLSH_PROG@ 140 | TCLSH = $(TCLSH_ENV) $(TCLSH_PROG) 141 | 142 | #WISH_PROG = @WISH_PROG@ 143 | #WISH = $(TCLSH_ENV) $(WISH_PROG) 144 | 145 | 146 | SHARED_BUILD = @SHARED_BUILD@ 147 | 148 | # NB FIX the configure script to do this the right way 149 | CASSANDRAINCLUDES=-I"/usr/local/include" 150 | CASSANDRALIBS=-L"/usr/local/lib" -lcassandra 151 | 152 | INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ $(CASSANDRAINCLUDES) 153 | #INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@ 154 | 155 | PKG_CFLAGS = @PKG_CFLAGS@ 156 | 157 | # TCL_DEFS is not strictly need here, but if you remove it, then you 158 | # must make sure that configure.in checks for the necessary components 159 | # that your library may use. TCL_DEFS can actually be a problem if 160 | # you do not compile with a similar machine setup as the Tcl core was 161 | # compiled with. 162 | #DEFS = $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS) 163 | DEFS = @DEFS@ $(PKG_CFLAGS) 164 | 165 | # Move pkgIndex.tcl to 'BINARIES' var if it is generated in the Makefile 166 | CONFIG_CLEAN_FILES = Makefile pkgIndex.tcl 167 | CLEANFILES = @CLEANFILES@ 168 | 169 | CPPFLAGS = @CPPFLAGS@ 170 | LIBS = $(CASSANDRALIBS) @PKG_LIBS@ @LIBS@ 171 | AR = @AR@ 172 | CFLAGS = @CFLAGS@ 173 | COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) 174 | 175 | #======================================================================== 176 | # Start of user-definable TARGETS section 177 | #======================================================================== 178 | 179 | #======================================================================== 180 | # TEA TARGETS. Please note that the "libraries:" target refers to platform 181 | # independent files, and the "binaries:" target inclues executable programs and 182 | # platform-dependent libraries. Modify these targets so that they install 183 | # the various pieces of your package. The make and install rules 184 | # for the BINARIES that you specified above have already been done. 185 | #======================================================================== 186 | 187 | all: binaries libraries doc 188 | 189 | #======================================================================== 190 | # The binaries target builds executable programs, Windows .dll's, unix 191 | # shared/static libraries, and any other platform-dependent files. 192 | # The list of targets to build for "binaries:" is specified at the top 193 | # of the Makefile, in the "BINARIES" variable. 194 | #======================================================================== 195 | 196 | binaries: $(BINARIES) 197 | 198 | libraries: 199 | 200 | #======================================================================== 201 | # Your doc target should differentiate from doc builds (by the developer) 202 | # and doc installs (see install-doc), which just install the docs on the 203 | # end user machine when building from source. 204 | #======================================================================== 205 | 206 | doc: 207 | 208 | #install: all install-binaries install-libraries install-doc 209 | install: all install-binaries install-libraries 210 | 211 | install-binaries: binaries install-lib-binaries install-bin-binaries 212 | 213 | #======================================================================== 214 | # This rule installs platform-independent files, such as header files. 215 | # The list=...; for p in $$list handles the empty list case x-platform. 216 | #======================================================================== 217 | 218 | install-libraries: libraries 219 | @mkdir -p $(DESTDIR)$(includedir) 220 | @echo "Installing header files in $(DESTDIR)$(includedir)" 221 | @list='$(PKG_HEADERS)'; for i in $$list; do \ 222 | echo "Installing $(srcdir)/$$i" ; \ 223 | $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \ 224 | done; 225 | 226 | #======================================================================== 227 | # Install documentation. Unix manpages should go in the $(mandir) 228 | # directory. 229 | #======================================================================== 230 | 231 | # install-doc: doc 232 | # @mkdir -p $(DESTDIR)$(mandir)/mann 233 | # @echo "Installing documentation in $(DESTDIR)$(mandir)" 234 | # @list='$(srcdir)/doc/*.n'; for i in $$list; do \ 235 | # echo "Installing $$i"; \ 236 | # rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \ 237 | # $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \ 238 | # done 239 | 240 | test: binaries libraries 241 | $(TCLSH) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS) 242 | 243 | shell: binaries libraries 244 | @$(TCLSH) $(SCRIPT) 245 | 246 | gdb: 247 | $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT) 248 | 249 | depend: 250 | 251 | #======================================================================== 252 | # $(PKG_LIB_FILE) should be listed as part of the BINARIES variable 253 | # mentioned above. That will ensure that this target is built when you 254 | # run "make binaries". 255 | # 256 | # The $(PKG_OBJECTS) objects are created and linked into the final 257 | # library. In most cases these object files will correspond to the 258 | # source files above. 259 | #======================================================================== 260 | 261 | $(PKG_LIB_FILE): $(PKG_OBJECTS) 262 | -rm -f $(PKG_LIB_FILE) 263 | ${MAKE_LIB} 264 | $(RANLIB) $(PKG_LIB_FILE) 265 | 266 | $(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS) 267 | -rm -f $(PKG_STUB_LIB_FILE) 268 | ${MAKE_STUB_LIB} 269 | $(RANLIB_STUB) $(PKG_STUB_LIB_FILE) 270 | 271 | #======================================================================== 272 | # We need to enumerate the list of .c to .o lines here. 273 | # 274 | # In the following lines, $(srcdir) refers to the toplevel directory 275 | # containing your extension. If your sources are in a subdirectory, 276 | # you will have to modify the paths to reflect this: 277 | # 278 | # sample.$(OBJEXT): $(srcdir)/generic/sample.c 279 | # $(COMPILE) -c `@CYGPATH@ $(srcdir)/generic/sample.c` -o $@ 280 | # 281 | # Setting the VPATH variable to a list of paths will cause the makefile 282 | # to look into these paths when resolving .c to .obj dependencies. 283 | # As necessary, add $(srcdir):$(srcdir)/compat:.... 284 | #======================================================================== 285 | 286 | VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win 287 | 288 | .c.@OBJEXT@: 289 | $(COMPILE) -c `@CYGPATH@ $<` -o $@ 290 | 291 | #======================================================================== 292 | # Distribution creation 293 | # You may need to tweak this target to make it work correctly. 294 | #======================================================================== 295 | 296 | #COMPRESS = tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar 297 | COMPRESS = gtar zcvf $(PKG_DIR).tar.gz $(PKG_DIR) 298 | DIST_ROOT = /tmp/dist 299 | DIST_DIR = $(DIST_ROOT)/$(PKG_DIR) 300 | 301 | dist-clean: 302 | rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.* 303 | 304 | dist: dist-clean 305 | mkdir -p $(DIST_DIR) 306 | cp -p $(srcdir)/ChangeLog $(srcdir)/README* $(srcdir)/license* \ 307 | $(srcdir)/aclocal.m4 $(srcdir)/configure $(srcdir)/*.in \ 308 | $(DIST_DIR)/ 309 | chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4 310 | chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.in 311 | 312 | for i in $(srcdir)/*.[ch]; do \ 313 | if [ -f $$i ]; then \ 314 | cp -p $$i $(DIST_DIR)/ ; \ 315 | fi; \ 316 | done; 317 | 318 | mkdir $(DIST_DIR)/tclconfig 319 | cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \ 320 | $(DIST_DIR)/tclconfig/ 321 | chmod 664 $(DIST_DIR)/tclconfig/tcl.m4 322 | chmod +x $(DIST_DIR)/tclconfig/install-sh 323 | 324 | list='demos doc generic library mac tests unix win'; \ 325 | for p in $$list; do \ 326 | if test -d $(srcdir)/$$p ; then \ 327 | mkdir $(DIST_DIR)/$$p; \ 328 | cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \ 329 | fi; \ 330 | done 331 | 332 | (cd $(DIST_ROOT); $(COMPRESS);) 333 | 334 | #======================================================================== 335 | # End of user-definable section 336 | #======================================================================== 337 | 338 | #======================================================================== 339 | # Don't modify the file to clean here. Instead, set the "CLEANFILES" 340 | # variable in configure.in 341 | #======================================================================== 342 | 343 | clean: 344 | -test -z "$(BINARIES)" || rm -f $(BINARIES) 345 | -rm -f *.$(OBJEXT) core *.core 346 | -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) 347 | 348 | distclean: clean 349 | -rm -f *.tab.c 350 | -rm -f $(CONFIG_CLEAN_FILES) 351 | -rm -f config.cache config.log config.status 352 | 353 | #======================================================================== 354 | # Install binary object libraries. On Windows this includes both .dll and 355 | # .lib files. Because the .lib files are not explicitly listed anywhere, 356 | # we need to deduce their existence from the .dll file of the same name. 357 | # Library files go into the lib directory. 358 | # In addition, this will generate the pkgIndex.tcl 359 | # file in the install location (assuming it can find a usable tclsh shell) 360 | # 361 | # You should not have to modify this target. 362 | #======================================================================== 363 | 364 | install-lib-binaries: binaries 365 | @mkdir -p $(DESTDIR)$(pkglibdir) 366 | @list='$(lib_BINARIES)'; for p in $$list; do \ 367 | if test -f $$p; then \ 368 | echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \ 369 | $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \ 370 | stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \ 371 | if test "x$$stub" = "xstub"; then \ 372 | echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \ 373 | $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \ 374 | else \ 375 | echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \ 376 | $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \ 377 | fi; \ 378 | ext=`echo $$p|sed -e "s/.*\.//"`; \ 379 | if test "x$$ext" = "xdll"; then \ 380 | lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \ 381 | if test -f $$lib; then \ 382 | echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \ 383 | $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \ 384 | fi; \ 385 | fi; \ 386 | fi; \ 387 | done 388 | @list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ 389 | if test -f $(srcdir)/$$p; then \ 390 | destp=`basename $$p`; \ 391 | echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \ 392 | $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \ 393 | fi; \ 394 | done 395 | @if test "x$(SHARED_BUILD)" = "x1"; then \ 396 | echo " Install pkgIndex.tcl $(DESTDIR)$(pkglibdir)"; \ 397 | $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \ 398 | fi 399 | 400 | #======================================================================== 401 | # Install binary executables (e.g. .exe files and dependent .dll files) 402 | # This is for files that must go in the bin directory (located next to 403 | # wish and tclsh), like dependent .dll files on Windows. 404 | # 405 | # You should not have to modify this target, except to define bin_BINARIES 406 | # above if necessary. 407 | #======================================================================== 408 | 409 | install-bin-binaries: binaries 410 | @mkdir -p $(DESTDIR)$(bindir) 411 | @list='$(bin_BINARIES)'; for p in $$list; do \ 412 | if test -f $$p; then \ 413 | echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \ 414 | $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \ 415 | fi; \ 416 | done 417 | 418 | .SUFFIXES: .c .$(OBJEXT) 419 | 420 | Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status 421 | cd $(top_builddir) \ 422 | && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status 423 | 424 | uninstall-binaries: 425 | list='$(lib_BINARIES)'; for p in $$list; do \ 426 | rm -f $(DESTDIR)$(pkglibdir)/$$p; \ 427 | done 428 | list='$(PKG_TCL_SOURCES)'; for p in $$list; do \ 429 | p=`basename $$p`; \ 430 | rm -f $(DESTDIR)$(pkglibdir)/$$p; \ 431 | done 432 | list='$(bin_BINARIES)'; for p in $$list; do \ 433 | rm -f $(DESTDIR)$(bindir)/$$p; \ 434 | done 435 | 436 | .PHONY: all binaries clean depend distclean doc install libraries test 437 | 438 | # Tell versions [3.59,3.63) of GNU make to not export all variables. 439 | # Otherwise a system limit (for SysV at least) may be exceeded. 440 | .NOEXPORT: 441 | -------------------------------------------------------------------------------- /README.FreeBSD: -------------------------------------------------------------------------------- 1 | # 2 | # casstcl configure for FreeBSD 3 | # 4 | 5 | autoreconf 6 | ./configure --with-tcl=/usr/local/lib/tcl8.6 --mandir=/usr/local/man 7 | #--enable-symbols 8 | 9 | -------------------------------------------------------------------------------- /README.Linux: -------------------------------------------------------------------------------- 1 | 2 | autoreconf 3 | ./configure --mandir=/usr/share/man "$@" 4 | 5 | # Add "--enable-symbols" if you want debugging. 6 | 7 | -------------------------------------------------------------------------------- /README.MacOSX: -------------------------------------------------------------------------------- 1 | # configure arguments for Mac OS X when Tcl is installed from macports.org 2 | # 3 | ./configure --prefix=/opt/local --with-tcl=/opt/local/lib/tclConfig.sh 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CassTcl, a Tcl language interface to the Cassandra database 2 | === 3 | 4 | CassTcl provides a Tcl interface to the Cassandra database using the cpp-driver C/C++ API. 5 | 6 | Functionality 7 | --- 8 | 9 | - Completely asynchronous 10 | - Provides a natural Tcl interface 11 | - Works out and manages all the data type conversions behind your back 12 | - Thread safe 13 | - Ad-hoc queries in CQL 14 | - Supports prepared statements 15 | - Supports batches 16 | - Supports all types of Cassandra collections 17 | - Compatible with binary protocol version 1 and 2 18 | - Supports authentication via credentials using SASL PLAIN 19 | - Supports SSL 20 | - Production quality 21 | - Free! 22 | 23 | License 24 | --- 25 | 26 | Open source under the permissive Berkeley copyright, see file LICENSE 27 | 28 | Requirements 29 | --- 30 | Requires the Datastaxx cpp-driver be installed. (https://github.com/datastax/cpp-driver) 31 | 32 | Building 33 | --- 34 | 35 | ```sh 36 | autoconf 37 | configure 38 | make 39 | sudo make install 40 | ``` 41 | 42 | For FreeBSD, something like 43 | 44 | ```sh 45 | ./configure --with-tcl=/usr/local/lib/tcl8.6 --mandir=/usr/local/man --enable-symbols 46 | ``` 47 | 48 | Accessing from Tcl 49 | --- 50 | 51 | ```tcl 52 | package require casstcl 53 | ``` 54 | 55 | To connect to a cluster (example): 56 | 57 | ```tcl 58 | set cass [::casstcl::cass create #auto] 59 | 60 | $cass contact_points $host 61 | $cass port $port ;# 9042 62 | $cass credentials $user $password 63 | $cass connect 64 | ``` 65 | 66 | Or a convenience one-liner: 67 | 68 | ```tcl 69 | set cass [::casstcl::connect -host $host -port $port -user $user -password $password] 70 | ``` 71 | 72 | CassTcl objects 73 | --- 74 | 75 | CassTcl provides object creation commands... 76 | 77 | ::casstcl::cass create handle, to create a cassandra handling object. 78 | 79 | ```tcl 80 | set cass [::casstcl::cass create #auto] 81 | 82 | $cass exec "insert into foo ..." 83 | ``` 84 | 85 | ...or... 86 | 87 | ```tcl 88 | set cassdb [::casstcl::cass create cass] 89 | 90 | cass exec "insert into foo ..." 91 | ``` 92 | 93 | 94 | Methods of cassandra cluster interface object 95 | --- 96 | 97 | * *$cassdb* **connect** *?-callback callbackRoutine?* *?keyspaceName?* 98 | 99 | Attempts to connect to the specified database, optionally setting the keyspace. Since the keyspace can be changed in CQL, this is not tracked and not recommended ... always provide a fully qualified table name. 100 | 101 | If the **-callback** argument is specified then the next argument is a callback routine that will be invoked when successfully connected. 102 | 103 | If the connection fails, the callback may not be invoked (i.e. a script error may be generated instead). 104 | 105 | The callback routine will be invoked with a single argument, which is the name of the future object created (such as *::future17*) when the request was made. 106 | 107 | * *$cassdb* **exec** *?-callback callbackRoutine?* *?-head?* *?-error_only?* *?-table tableName?* *?-array arrayName?* *?-prepared preparedObjectName?* *?-batch batchObjectName?* *?-consistency consistencyLevel?* *$request* *?arg...?* 108 | 109 | * *$cassdb* **async** *?-callback callbackRoutine?* *?-head?* *?-table tableName?* *?-array arrayName?* *?-prepared preparedObjectName?* *?-batch batchObjectName?* *?-consistency consistencyLevel?* *?$request?* *?arg...?* 110 | 111 | * *$cassdb* **exec** *?-callback callbackRoutine?* *?-head?* *?-error_only?* *-upsert* *?-mapunknown columnName?* *?-nocomplain?* *?-ifnotexists?* *tableName* *argList* 112 | 113 | * *$cassdb* **async** *?-callback callbackRoutine?* *?-head?* *-upsert* *?-mapunknown columnName?* *?-nocomplain?* *?-ifnotexists?* *tableName* *argList* 114 | 115 | Perform a request. The request is normally a CQL statement. Waits for it to complete if **exec** is used without **-callback** (synchronous). Does not wait if **async** is used or **exec** is used with **-callback** (asynchronous). 116 | 117 | If synchronous then tcl waits for the cassandra call to complete and gives you back an error if an error occurs. 118 | 119 | If used asynchronously then the request is issued to cassandra and a result object called a *future* object is created and returned. 120 | 121 | You can use the methods of the future object to find out the status of your statement, iterate over select results, etc. Asynchronous operation allows for considerable performance gains over synchronous at the cost of greater code complexity. 122 | 123 | If the **-callback** argument is specified then the next argument is a callback routine that will be invoked when the Cassandra request has completed or errored or whatnot. The callback routine will be invoked with a single argument, which is the name of the future object created (such as *::future17*) when the request was made. 124 | 125 | If **-head** is specified then when the callback even occurs it is queued at the head of the notifier queue rather than at the tail, causing the event to be processed ahead of other events already in the queue. This can be useful when you are getting a lot of events to make sure that your important cassandra completion events get priority above, say, the events that are causing your casstcl batches to get added to. 126 | 127 | **-error_only** instructs casstcl to only call the callback function on error. If error-only is specified and the callback from the cassandra cpp-driver indicates the asynchronous request was successful, the future object is deleted and the callback is not taken. Assuming most requests are succeeding this greatly reduces invocations of the Tcl interpreter, and can bring a major performance increase. 128 | 129 | If **-batch** is specified the request is a batch object and that is used as the source of the statement(s). 130 | 131 | If **-table** is specified it is the fully qualified name of a table and *-array* is also required, and vice versa. These specify the affected table name and an array that the data elements will come from. Args are zero or more arguments which are element names for the array and also legal column names for the table. This technology will infer the data types and handle them behind your back as long as import_column_type_map has been run on the connection. 132 | 133 | If **-prepared** is specified it is the name of a prepared statement object and the final argument is a list of key value pairs where the key corresponds to the name of a value in the prepared statement and the value is to be correspondingly bound to the matching **?** argument in the statement. 134 | 135 | If **-consistency** is specified it is the consistency level to use for any created statement(s). Cannot be used with **-batch**. 136 | 137 | If **-upsert** is specified then the final arguments are a table name and a list of key-value pairs where the key corresponds to the name of a column and the value corresponds to the new value for that column. The new values will be "upserted" into the table based on the primary key. 138 | 139 | If **-mapunknown columnName** is specified then unknown arguments in the upsert key-value list will be mapped to a *map* column. 140 | 141 | If **-nocomplain** is specified then unknown arguments in the upsert key-value list will be silently ignored. 142 | 143 | If **-ifnotexists** is specified then the row is only inserted if it doesn't already exist. 144 | 145 | If none of *-table*, *-array*, *-batch*, *-upsert*, or *-prepared* have been specified, the arguments to the right of the statement need to be alternating between data and data type, like *14 int 3.7 float*. This is the simplest for casstcl but requires the code to be more intimate with the data types than it otherwise would be. If you use this style and you change a data type in the schema you also have to change it in the code. So we don't like it. 146 | 147 | See also the future object. 148 | 149 | * *$cassdb* **select** *?-pagesize n?* *?-consistency consistencyLevel?* *?-withnulls?* **$statement array code** 150 | 151 | Iterate filling array with results of the select statement and executing code upon it. break, continue and return from the code is supported. 152 | 153 | Unless you specify *-withnulls*, null values are unset from the array, so you have to use info exists to see if a value is not null. 154 | 155 | The array is cleared each iteration for you automatically so you don't neeed to jack with unset. 156 | 157 | If the **-pagesize** argument is present then it should be followed by an integer which is the number of query results that should be returned "per pass". Changing this should transparent to the caller but smaller pagesize numbers should allow greater concurrency in many cases by allowing the application to process some results while the cluster is still producing them. The default pagesize is 100 rows. 158 | 159 | If the **-consistency** argument is present then it should be followed by a consistency level, which will be used when creating any statement(s). 160 | 161 | * *$cassdb* **prepare** *objName* *tableName* *$statement* 162 | 163 | Prepare the specified statement and creates a prepared object named *objName*. Although the table name shouldn't technically need to be specified, since the cpp-driver API doesn't provide access to the data types of the elements of the statement (yet; they have a ticket open to do it), we need the table name so we can look up the data types of the values being substituted when a prepared statement is being bound. (It's OK to specify the table name since Cassandra doesn't support joins and whatnot so there shouldn't be more than one table referenced.) 164 | 165 | The result is a prepared statement object that currently has two methods, **delete**, which does the needful, and **statement**, which returns the string that was prepared as a statement. The important thing is that prepared objects can be passed as arguments to the **batch**, **async** and **exec** methods. 166 | 167 | Here's an example of defining a prepared statement and a subsequent use of it to add to a batch. 168 | 169 | ```tcl 170 | set ::positionsPrepped [$::cass prepare #auto hummingbird.latest_positions_by_facility "INSERT INTO hummingbird.latest_positions_by_facility (facility, hexid, c, s, type, ident, t, alt, clock, gs, heading, lat, lon, reg, squawk, aircrafttype) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) using TTL 3600;"] 171 | 172 | $::batch add -prepared $::positionsPrepped [array get row] 173 | ``` 174 | 175 | * *$cassdb* **cluster_version** 176 | 177 | Return the Cassandra cluster version as a list of {major minor patchlevel}. 178 | 179 | Requires CPP driver >= v2.3.0 180 | 181 | * *$cassdb* **keyspaces** 182 | 183 | Return a list of all of the keyspaces known to the cluster. 184 | 185 | * *$cassdb* **tables** *$keyspace* 186 | 187 | Return a list of all of the tables within the named keyspace. 188 | 189 | * *$cassdb* **columns** *$keyspace* *$table* 190 | 191 | Returns a list of the names of all of the columns within the specified table within the specified keyspace. 192 | 193 | * *$cassdb* **columns_with_types** *$keyspace* *$table* 194 | 195 | Returns a list of key-value pairs consisting of all of the columns and their Cassandra data types. 196 | 197 | All of the schema-accessing functions, *list_keyspaces*, *list_tables*, *list_columns* and *list_column_types* locate this information using the metadata access capabilities provided by the cpp-driver. As the cpp-driver will update its metadata on the fly as changes to the schema are made, the schema-accessing functions likewise will reflect any changes in the schema if called subsequently to schema changes occurring. 198 | 199 | * *$cassdb* **reimport_type_map** 200 | 201 | Casstcl maintains the data type of each column for each table for each schema in the cluster. As the metadata can change on the fly, long-running programs that want to try to adapt to changes in the cluster schema can invoke this to regenerate casstcl's column-to-datatype mapping cache. 202 | 203 | * *$cassdb* **contact_points** *$addressList* 204 | 205 | Provide a list of one or more addresses to contact the cluster at. 206 | 207 | The first call sets the contact points and any subsequent calls appends additional contact points. Passing an empty string will clear the contact points. White space is striped from the contact points. 208 | 209 | * *$cassdb* **port** *$port* 210 | 211 | Specify a port to connect to the cluster on. Since the cpp-driver uses the native binary CQL protocol, this would normally be 9042. 212 | 213 | * *$cassdb* **protocol_version** *$protocolVersion* 214 | 215 | Sets the protocol version. This will theoretically automatically downgrade to protocol version 1 if necessary. The default value is 2. 216 | 217 | * *$cassdb* **heartbeat_interval** *heartbeatInterval* 218 | 219 | To prevent intermediate network devices from disconnecting pooled connections due to inactivity, the driver periodically sends a lightweight heartbeat request, by default every 30 seconds. This can be changed with this method; use zero to disable connection heartbeats. 220 | 221 | * *$cassdb* **whitelist_filtering** *?hostList?* 222 | 223 | Limit cassandra host connection attempts to the set of hosts provided in *hostList*. It's not a good idea to use this to limit connections to hosts in a local data center; please see the cpp-driver documentation for details. 224 | 225 | * *$cassdb* **num_threads_io** *$numThreads* 226 | 227 | Set the number of IO threads. This is the number of threads that will handle query requests. The default is 1. 228 | 229 | * *$cassdb* **queue_size_io** *$queueSize* 230 | 231 | Sets the size of the the fixed size queue that stores pending requests. The default is 4096. 232 | 233 | * *$cassdb* **queue_size_event** *$queueSize* 234 | 235 | Sets the size of the the fixed size queue that stores pending requests. The default is 4096. 236 | 237 | * *$cassdb* **queue_size_log** *$logSize* 238 | 239 | Sets the size of the the fixed size queue that stores log messages. The default is 4096. 240 | 241 | * *$cassdb* **core_connections_per_host** *$numConnections* 242 | 243 | Sets the number of connections made to each server in each IO thread. Defaults to 1. 244 | 245 | * *$cassdb* **max_connections_per_host** *$numConnections* 246 | 247 | Sets the maximum number of connections made to each server in each IO thread. Defaults to 2. 248 | 249 | * *$cassdb* **max_concurrent_creation** *$numConnections* 250 | 251 | Sets the maximum number of connections that will be created concurrently. Connections are created when the current connections are unable to keep up with request throughput. The default is 1. 252 | 253 | * *$cassdb* **max_concurrent_requests_threshold** *$numRequests* 254 | 255 | Sets the threshold for the maximum number of concurrent requests in-flight on a connection before creating a new connection. The number of new connections created will not exceed max_connections_per_host. Default 100. 256 | 257 | * *$cassdb* **max_requests_per_flush** *$numRequests* 258 | 259 | Set the maximum number of requests processed by an I/O worker per flush. Default 128. 260 | 261 | * *$cassdb* **write_bytes_high_water_mark** *$highWaterMarkBytes* 262 | 263 | Sets the high water mark for the number of bytes outstanding on a connection. Disables writes to a connection if the number of bytes queued exceeds this value. cpp-driver documented default is 64 KB. 264 | 265 | * *$cassdb* **write_bytes_low_water_mark** *$lowWaterMarkBytes* 266 | 267 | Sets the low water mark for the number of bytes outstanding on a connection. After exceeding high water mark bytes, writes will only resume once the number of outstanding bytes falls below this value. Default is 32 KB. 268 | 269 | * *$cassdb* **pending_requests_high_water_mark** *$highWaterMark* 270 | 271 | Sets the high water mark for the number of requests queued waiting for a connection in a connection pool. Disables writes to a host on an IO worker if the number of requests queued exceeds this value. Default is 128 * max_connections_per_host. 272 | 273 | * *$cassdb* **pending_requests_low_water_mark** *$lowWaterMark* 274 | 275 | Sets the low water mark for the number of requests queued waiting for a connection in a connection pool. After exceeding high water mark requests, writes to a host will only resume once the number of requests falls below this value. Default is 64 * max_connections_per_host. 276 | 277 | * *$cassdb* **connect_timeout** *$timeoutMS* 278 | 279 | Sets the timeout for connecting to a node. Timeout is in milliseconds. The default is 5000 ms. 280 | 281 | * *$cassdb* **request_timeout** *$timeoutMS* 282 | 283 | Sets the timeout for waiting for a response from a node. Timeout is in milliseconds. The default is 12000 ms. 284 | 285 | * *$cassdb* **reconnect_wait_time** *$timeoutMS* 286 | 287 | Sets the amount of time to wait before attempting to reconnect. Default 2000 ms. 288 | 289 | * *$cassdb* **credentials** *$user $password* 290 | 291 | Set credentials for plaintext authentication. 292 | 293 | * *$cassdb* **tcp_nodelay** *$enabled* 294 | 295 | Enable/Disable Nagel's algorithm on connections. The default is disabled. 296 | 297 | * *$cassdb* **load_balance_round_robin** 298 | 299 | Requests that the driver discover all nodes in a cluster and cycles through the per request. All are considered local. 300 | 301 | * *$cassdb* **load_balance_dc_aware** *localDC* *usedHostsPerRemoteDC* *allowRemoteDCs* 302 | 303 | Configures the cluster to use data center-aware load balancing. For each query all the live inodes in the local data center are tried first, followed by any node from other data centers. 304 | 305 | This is the default and does not need to called unless switching an existing from another policy or changing settings. 306 | 307 | Without further configuration a default local data center is chosen from the first connected contact point and no remote hosts are considered in query plans. If you use this mechanism be sure to use only contact points from the local data center. 308 | 309 | *localDC* is the name of the data center to try first. *usedHostsPerRemoteDC* is the number of hosts used in each remote data center if no hosts are available in the local data center. *allowRemoteDCs* is a boolean. If true it allows remote hosts to be used to no local data center hosts are available and the consistency level is *one* or *quorum*. 310 | 311 | * *$cassdb* **token_aware_routing** *$enabled* 312 | 313 | Configures the cluster to use Token-aware request routing, or not. 314 | 315 | This routing policy composes the base routing policy, routing requests first to replicas on nodes considered 'local' by the base load balancing policy. 316 | 317 | The default is enabled. 318 | 319 | * *$cassdb* **latency_aware_routing** *$enabled* 320 | 321 | Configures the cluster to use latency-aware request routing, or not. 322 | 323 | This routing policy composes the base routing policy tracks the exponentially weighted moving average of query latencies to nodes in the cluster. If a given node's latency exceeds an exclusion threshold, it is no longer queried. 324 | 325 | * *$cassdb* **tcp_keepalive** *$enabled $delaySecs* 326 | 327 | Enables/Disables TCP keep-alive. Default is disabled. delaySecs is the initial delay in seconds; it is ignored when disabled. 328 | 329 | * *$cassdb* **delete** 330 | 331 | Disconnect from the cluster and delete the cluster connection object. 332 | 333 | * *$cassdb* **close** 334 | 335 | Disconnect from the cluster. 336 | 337 | * *$cassdb* **metrics** 338 | 339 | Obtain metrics from the cassandra cluster and return them as a list of key-value pairs. The returned values and their meanings are as follows: 340 | 341 | |key|value 342 | ----|----- 343 | requests.min|requests minimum in microseconds 344 | requests.max|requests maximum in microseconds 345 | requests.mean|requests mean in microseconds 346 | requests.stddev|requests standard deviation in microseconds 347 | requests.median|requests median in microseconds 348 | requests.percentile_75th|75th percentile in microseconds 349 | requests.percentile_95th|95th percentile in microseconds 350 | requests.percentile_98th|98th percentile in microseconds 351 | requests.percentile_99th|99th percentile in microseconds 352 | requests.percentile_999th|999th percentile in microseconds 353 | requests.mean_rate|mean rate in requests per second 354 | requests.one_minute_rate|one-minute rate in requests per second 355 | requests.five_minute_rate|five-minute rate in requests per second 356 | requests.fifteen_minute_rate|fifteen-minute rate in requests per second 357 | stats.total_connections|the total number of connections 358 | stats.available_connections|the number of connections available to take requests 359 | stats.exceeded_pending_requests_water_mark|occurrences when requests exceeded a pool's high water mark 360 | stats.exceeded_write_bytes_water_mark|occurrences when number of bytes exceeded a pool's high water mark 361 | errors.connection_timeouts|occurrences of a connection timeout 362 | errors.pending_request_timeouts|Occurrences of requests that timed out waiting for a connection 363 | errors.request_timeouts|Occurrences of requests that timed out waiting for a request to finish 364 | 365 | Batches 366 | --- 367 | 368 | Batches are a way to provide atomicity, so if anything in a batch fails, none of the actions occur. 369 | 370 | While you can construct batches out of straight CQL with BEGIN BATCH and APPLY BATCH, casstcl provides batch objects to help you construct, manage and use batches. 371 | 372 | Create a new batch using the batch method of the cassandra object: 373 | 374 | ```tcl 375 | $cassdb batch mybatch 376 | set mybatch [$cassdb batch #auto] 377 | ``` 378 | 379 | Both styles work. 380 | 381 | The default batch type is *logged*. An optional argument to the batch creation command is the batch type, which can be *logged*, *unlogged* or *counter*. 382 | 383 | ```tcl 384 | set mybatch [$cassdb batch #auto unlogged] 385 | ``` 386 | 387 | * *$batch* **add** *?-table tableName?* *?-array arrayName?* *?-prepared preparedObjectName? ?args..? 388 | 389 | Adds the specified statement to the batch. Processes arguments similarly to the **exec** and **async** methods. 390 | 391 | * *$batch* **consistency** *?$consistencyLevel?* 392 | 393 | Sets the batch write consistency level to the specified level. Consistency must be **any**, **one**, **two**, **three**, **quorum**, **all**, **local_quorum**, **each_quorum**, **serial**, **local_serial**, or **local_one**. 394 | 395 | * *$batch* **upsert** ?-mapunknown mapcolumn? ?-nocomplain? ?-ifnotexists? *$table* *$list* 396 | 397 | Generate in upsert into the batch. List contains key value pairs where keys should be columns that exist in the fully qualified table. 398 | 399 | A typical usage would be: 400 | 401 | ```tcl 402 | $batch upsert wx.wx_metar [array get row] 403 | ``` 404 | 405 | If *-nocomplain* is specified then any column names not found in the column map for the specified table are silently dropped along with their values. 406 | 407 | If *-mapunknown* is specified then an additional argument containing a column name should be specified. With this usage any column names not found in the column map are written to a map collection of the specified column name. The column name must exist as a column in the table and be of the *map* type and the key and values types of the map must be *text*. 408 | 409 | * *$batch* **count** 410 | 411 | Return the number of rows added to the batch using *add* or *upsert*. 412 | 413 | * *$batch* **reset** 414 | 415 | Reset the batch by deleting all of its data. 416 | 417 | * *$batch* **delete** 418 | 419 | Delete the batch object and all of its data. 420 | 421 | Note on batch size 422 | ---- 423 | 424 | As of Cassandra 3.1 there is a limit on the maximum size of a batch in the vicinity of 16 megabytes. Generating batches that are too large will result in "no hosts available" when trying to send the batch and illegal argument exceptions on the backend from java about a mutation being too large. 425 | 426 | Note on batch performance 427 | ---- 428 | 429 | Batching isn't for performance. Google for details. However since batching reduces the number of callbacks, casstcl can run faster with batches if it is heavily loaded. A recent addition to the async and exec methods, **-error_only** specifies that completion callbacks are only done for errors and this improves casstcl throughput considerably when under heavy load. 430 | 431 | Note on logged versus unlogged batches 432 | ---- 433 | 434 | casstcl batches are logged by default. From the Cassandra documentation: 435 | 436 | Cassandra uses a batch log to ensure all operations in a batch are applied atomically. (Note that the operations are still only isolated within a single partition.) 437 | 438 | There is a performance penalty for batch atomicity when a batch spans multiple partitions. If you do not want to incur this penalty, you can tell Cassandra to skip the batchlog with the UNLOGGED option. If the UNLOGGED option is used, operations are only atomic within a single partition. 439 | 440 | Configuring SSL Connections 441 | --- 442 | 443 | * *$cassdb* **ssl_cert** *$pemFormattedCertString* 444 | 445 | This sets the client-side certificate chain. This is used by the server to authenticate the client. This should contain the entire certificate chain starting with the certificate itself. 446 | 447 | * *$cassdb* **ssl_private_key** *$pemFormattedCertString $password* 448 | 449 | This sets the client-side private key. This is used by the server to authenticate the client. 450 | 451 | * *$cassdb* **add_trusted_cert** *$pemFormattedCertString* 452 | 453 | This adds a trusted certificate. This is used to verify the peer's certificate. 454 | 455 | * *$cassdb* **ssl_verify_flag** *$flag* 456 | 457 | This sets the verification that the client will perform on the peer's certificate. **none** selects that no verification will be performed, while **verify_peer_certificate** will verify that a certificate is present and valid. 458 | 459 | Finally, **verify_peer_identity** will match the IP address to the certificate's common name or one of its subject alternative names. This implies that the certificate is also present. 460 | 461 | * *$cassdb* **ssl_enable** 462 | 463 | This attaches the SSL context configured using the aforementioned SSL methods to our cassandra cluster structure and enables SSL. 464 | 465 | Future Objects 466 | --- 467 | 468 | Future objects are created when casstcl's async or exec methods are invoked with a callback specified. It is up to the user to ensure that the future objects returned have their methods invoked to enquire as to the status and ultimate disposition of their requests. The results of selects, for instance, can be iterated using the object's **foreach** method. After you're finished with a future object, you should delete it. 469 | 470 | ```tcl 471 | set future [$cassObj async "select * from wx_metar where airport = 'KHOU' order by time desc limit 1"] 472 | 473 | # see if the request was successful 474 | if {[$future isready]} { 475 | if {[$future status] != "CASS_OK"} { 476 | set errorString [$future error_message] 477 | } 478 | } 479 | 480 | $future foreach row { 481 | parray row 482 | puts "" 483 | } 484 | 485 | $future delete 486 | ``` 487 | 488 | * *$future* **isready** 489 | 490 | Returns 1 if the future (query result) is ready, else 0. 491 | 492 | * *$future* **wait** *?us?* 493 | 494 | Waits for the request to complete. If the optional argument *us* is specified, times out and returns after that number of microseconds have elapsed without the request having completed. 495 | 496 | * *$future* **foreach** *rowArray code* 497 | 498 | Iterate through the query results, filling the named array with the columns of the row and their values and executing code thereupon. 499 | 500 | * *$future* **status** 501 | 502 | Return the cassandra status code converted back to a string, like CASS_OK and CASS_ERROR_SSL_NO_PEER_CERT and whatnot. If it's anything other than CASS_OK then whatever you did didn't work. 503 | 504 | * *$future* **error_message** 505 | 506 | Return the cassandra error message for the future, or an empty string if none. 507 | 508 | * *$future* **delete** 509 | 510 | Delete the future. Delete futures when you are done with them or you will leak memory. 511 | 512 | Exec and select can kind of hide the future object to make things simpler. Sometimes you may like to go synchronously because given the nature of your application you're willing to wait for the result and you don't want to jack around with callbacks, yadda. Great. Exec and select are normally pretty good for that but future objects have some capabilities you can't get with them so you can do an async without the callback then wait immediately on the future object. Remember to delete your future objects when you're done with them. 513 | 514 | ```tcl 515 | set future [$cassObj async "select * from wx_metar where airport = 'KHOU' order by time desc limit 1"] 516 | $future wait 517 | if {[$future status] != "CASS_OK"} { 518 | set errorString [$future error_message] 519 | } 520 | ... 521 | $future delete 522 | ``` 523 | 524 | Casstcl logging callback 525 | --- 526 | 527 | The Cassandra cpp-driver supports custom logging callbacks to allow an application to receive logging messages rather than having them go to stderr, which is what happens by default, and casstcl provides Tcl-level support for this capability. 528 | 529 | The logging callback is defined like this: 530 | 531 | ```tcl 532 | ::casstcl::cass logging_callback callbackFunction 533 | ``` 534 | 535 | When a log message callback occurs from the Cassandra cpp-driver, casstcl will obtain that and invoke the specified callback function one argument containing a list of key value pairs representing the (currently six) things that are received in a logging object: 536 | 537 | * "clock" and a floating point epoch clock with millisecond accuracy 538 | 539 | * "severity" and the cassandra log level as a string. value will be one of **disabled**, **critical**, **error**, **warn**, **info**, **debug**, **trace** or **unknown**. 540 | 541 | * "file" and possibly the name of some file, or an empty string... not sure what it is, maybe the source file of the cpp-driver that is throwing the error. 542 | 543 | * "line" and some line number, not sure what it is, probably the line number of the source file of the cpp-driver that is throwing the error 544 | 545 | * "function" and the name of some function, probably the cpp-driver function that is throwing the error 546 | 547 | * "message" and the error message itself 548 | 549 | The level of detail queued to the logging callback may be adjusted via: 550 | 551 | ```tcl 552 | ::casstcl::cass log_level level 553 | ``` 554 | 555 | This sets the log level for the cpp-driver. The legal values, in order from least output (none) to most output (all) are "disabled", "critical", "error", "warn", "info", "debug", and "trace". 556 | 557 | The default is "warn". 558 | 559 | A sample implementation: 560 | 561 | ```tcl 562 | proc logging_callback {pairList} { 563 | puts "logging_callback called..." 564 | array set log $pairList 565 | parray log 566 | puts "" 567 | } 568 | 569 | casstcl::cass logging_callback logging_callback 570 | casstcl::cass log_level info 571 | ``` 572 | 573 | According to the cpp-driver documentation, logging configuration should be done before calling any other driver function, so if you're going to use this, invoke it after package requiring casstcl and before invoking ::casstcl::cass create. 574 | 575 | Also note that logging is global. That is, it's not tied to a specific connection if you had more than one connection for some reason. Also currently it is not fully cleaned up if you unload the package. 576 | 577 | It is important to at least capture this information as useful debuggin information sometimes comes back through this interface. For example you might just get a "Query error" back from the library but then you get a logging callback that provides much more information, such as 578 | 579 | ``` 580 | :371:int32_t cass::decode_buffer_size(const cass::Buffer &)): Routing key cannot contain an empty value or a collection 581 | ``` 582 | 583 | The above messages makes the problem much more clear. The caller probably didn't specify a value for the primary key. 584 | 585 | Casstcl library functions 586 | --- 587 | 588 | The library functions such as *download_schema*, *list_schema*, *list_tables*, etc, have been removed. These capabilities are now provided by the metadata-traversing methods *list_keyspaces*, *list_tables*, *list_columns* and *list_column_types* documented above. 589 | 590 | 591 | * **::casstcl::validator_to_type** *$type* 592 | 593 | Given a validator type such as "org.apache.cassandra.db.marshal.AsciiType" and return the corresponding Cassandra type. Can decode complex type references including reversed, list, set, and map. This function is used by casstcl's *list_column_types* method. 594 | 595 | * **::casstcl::quote** *$value* *$type* 596 | 597 | Return a quote of the specified value according to the specified cassandra type. 598 | 599 | * **casstcl::typeof** *table.columnName* 600 | 601 | * **casstcl::quote_typeof** *table.columnName* *value* *?subType?* 602 | 603 | Quote the value according to the field *columnName* in table *table*. *subType* can be **key** for collection sets and lists and can be **key** or **value** for collection maps. For these usages it returns the corresponding data type. If the subType is specified and the column is a collection you get a list back like *list text* and *map int text*. 604 | 605 | * **casstcl::assemble_statement** *statementVar* *line* 606 | 607 | Given the name of a variable (initially set to an empty string) to contain a CQL statement, and a line containing possibly a statement or part of a statement, append the line to the statement and return 1 if a complete statement is present, else 0. 608 | 609 | Comments and blank lines are skipped. 610 | 611 | (The check for a complete statement is the mere presence of a semicolon anywhere on the line, which is kind of meatball.) 612 | 613 | * **run_fp** *cassdb* *fp* 614 | 615 | Read from a file pointer and execute complete commands through the specified cassandra object. 616 | 617 | * **run_file** *cassdb* *fileName* 618 | 619 | Run CQL commands from a file. 620 | 621 | * **interact** *cassdb* 622 | 623 | Enter a primitive cqlsh-like shell. Provides a prompt of *tcqlsh>* when no partial command has been entered or *......>* when a partial command is present. Once a semicolon is seen via one input line or multiple, the command is executed. "help" for meta-commands. "exit" to exit the interact proc. 624 | 625 | errorCode 626 | --- 627 | 628 | When propagating an error from the cpp-driver back to Tcl, the errorCode will be a list containing three our four elements. The first will be the literal string **CASSANDRA**, the second will be the cassandra error code in text form matching the error codes in the cassandra.h include file, for example **CASS_ERROR_LIB_NO_HOSTS_AVAILBLE** or **CASS_ERROR_SSL_INVALID_CERT**. 629 | 630 | The third value will be the output of the cpp-drivers *cass_error_desc()* call, returning a result such as "Invalid query". 631 | 632 | The fourth value, if present, will be the output of *cass_future_error_message()*. When present it is usually pretty specific as to the nature of the problem and quite useful to aid in debugging. 633 | 634 | Both error messages are used to generate the error string itself, to the degree that they are available when the tcl error return is being generated. 635 | 636 | Bugs 637 | --- 638 | 639 | It seems to be working pretty well and we haven't found memory leaks in quite a while. Still, the code is pretty new and hasn't gotten a ton of use. Nonetheless, the workhorse functions **async**, **exec** and **select** seem to work pretty well. 640 | 641 | FlightAware 642 | --- 643 | FlightAware has released over a dozen applications (under the free and liberal BSD license) into the open source community. FlightAware's repositories are available on GitHub for public use, discussion, bug reports, and contribution. Read more at https://flightaware.com/about/code/ 644 | 645 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | * make sure UTF-8 support is correctly handled 3 | 4 | Is Cassandra all UTF-8 or are some types not? Is there anything we need 5 | to do for it to work? 6 | 7 | * create a demonstration that uses SSL for the connection to the cassandra cluster (the Tcl/C interface has interfaces to their slightly lame SSL setup calls. Use them to create the demo.) 8 | 9 | The interfaces are there but they take the arguments as strings. It'll need some scaffolding to get files from the right plances into the right calls. A demo will show connecting over SSL to a cluster and document that. 10 | 11 | * allow where the cassandra cpp-driver library is located to be specified rather than assuming it is in /usr/local/include and /usr/local/lib 12 | 13 | * maybe detect if tcl isn't built with threads and stop because cassandra doesn't work with threads 14 | 15 | * support the following data types in casstcl_cass_value_to_tcl_obj: 16 | * custom 17 | * decimal 18 | * inet 19 | * varint 20 | * timeuuid 21 | 22 | -------------------------------------------------------------------------------- /aclocal.m4: -------------------------------------------------------------------------------- 1 | # 2 | # Include the TEA standard macro set 3 | # 4 | 5 | builtin(include,tclconfig/tcl.m4) 6 | 7 | # 8 | # Add here whatever m4 macros you want to define for your package 9 | # 10 | -------------------------------------------------------------------------------- /casstcl.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # casstcl support functions 3 | # 4 | # 5 | # 6 | 7 | #package provide casstcl 8 | 9 | namespace eval ::casstcl { 10 | variable schemaDict 11 | variable schemaDownloaded 0 12 | variable simpleValidatorToType 13 | variable validatorTypeLookupCache 14 | variable columnTypeMap 15 | 16 | array set simpleValidatorToType [list \ 17 | "org.apache.cassandra.db.marshal.AsciiType" ascii \ 18 | "org.apache.cassandra.db.marshal.LongType" bigint \ 19 | "org.apache.cassandra.db.marshal.BytesType" blob \ 20 | "org.apache.cassandra.db.marshal.BooleanType" boolean \ 21 | "org.apache.cassandra.db.marshal.CounterColumnType" counter \ 22 | "org.apache.cassandra.db.marshal.SimpleDateType" date \ 23 | "org.apache.cassandra.db.marshal.DecimalType" decimal \ 24 | "org.apache.cassandra.db.marshal.DoubleType" double \ 25 | "org.apache.cassandra.db.marshal.FloatType" float \ 26 | "org.apache.cassandra.db.marshal.InetAddressType" inet \ 27 | "org.apache.cassandra.db.marshal.Int32Type" int \ 28 | "org.apache.cassandra.db.marshal.UTF8Type" text \ 29 | "org.apache.cassandra.db.marshal.TimeType" time \ 30 | "org.apache.cassandra.db.marshal.DateType" timestamp \ 31 | "org.apache.cassandra.db.marshal.TimestampType" timestamp \ 32 | "org.apache.cassandra.db.marshal.DurationType" duration \ 33 | "org.apache.cassandra.db.marshal.TimeUUIDType" timeuuid \ 34 | "org.apache.cassandra.db.marshal.UUIDType" uuid \ 35 | "org.apache.cassandra.db.marshal.IntegerType" varint \ 36 | "org.apache.cassandra.db.marshal.ListType" list \ 37 | "org.apache.cassandra.db.marshal.MapType" map \ 38 | "org.apache.cassandra.db.marshal.SetType" set \ 39 | "org.apache.cassandra.db.marshal.TupleType" tuple \ 40 | "org.apache.cassandra.db.marshal.CompositeType" composite ] 41 | 42 | # load the cache with all the simple types 43 | array set validatorTypeLookupCache [array get simpleValidatorToType] 44 | 45 | # 46 | # validator_to_type - perform translations from simple and complex cassandra 47 | # java validator types to cassandra datatypes, for example the ones 48 | # done by translate_simple_validator_type above plus things like 49 | # 50 | # org.apache.cassandra.db.marshal.MapType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.UTF8Type) to {map text text} 51 | # 52 | # org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type) to {set text} 53 | # 54 | # org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.TimestampType) to timestamp 55 | # 56 | proc validator_to_type {validator} { 57 | variable simpleValidatorToType 58 | variable validatorTypeLookupCache 59 | 60 | # see if it's cached either by preloading or by having already been 61 | # figured out 62 | if {[info exists validatorTypeLookupCache($validator)]} { 63 | return $validatorTypeLookupCache($validator) 64 | } 65 | 66 | if {[regexp {([A-Za-z]*)Type\(([^)]*)\)$} $validator dummy complexType subType]} { 67 | set complexType [string tolower $complexType] 68 | 69 | switch $complexType { 70 | "reversed" { 71 | set result $simpleValidatorToType($subType) 72 | } 73 | 74 | "set" - 75 | "list" { 76 | set result [list $complexType $simpleValidatorToType($subType)] 77 | } 78 | 79 | "map" { 80 | lassign [split $subType ","] keyType valueType 81 | set result [list map $simpleValidatorToType($keyType) $simpleValidatorToType($valueType)] 82 | } 83 | 84 | "tuple" { 85 | set result [list tuple] 86 | 87 | foreach subType [split $subType ","] { 88 | lappend result $simpleValidatorToType($subType) 89 | } 90 | } 91 | 92 | default { 93 | error "unrecognized complex type '$complexType'" 94 | } 95 | } 96 | 97 | # cache this for next time 98 | set validatorTypeLookupCache($validator) $result 99 | return $result 100 | } 101 | } 102 | 103 | # 104 | # import_column_type_map - given a casstcl object, traverse the metadata 105 | # identifying the keyspaces and, for each keyspace, the table and, for 106 | # each table, the columns. For eacch column write the keyspace, table, 107 | # and column as the key of the columnTypeMap array with the textual 108 | # type being the value. 109 | # 110 | proc import_column_type_map {obj} { 111 | variable columnTypeMap 112 | 113 | unset -nocomplain columnTypeMap 114 | 115 | foreach keyspace [$obj keyspaces] { 116 | foreach table [$obj tables $keyspace] { 117 | foreach "column type" [$obj columns_with_types $keyspace $table] { 118 | set columnTypeMap($keyspace.$table.$column) $type 119 | } 120 | } 121 | } 122 | } 123 | 124 | proc typeof {name {subType ""}} { 125 | variable columnTypeMap 126 | 127 | if {$subType == ""} { 128 | return $columnTypeMap($name) 129 | } 130 | 131 | set list $columnTypeMap($name) 132 | 133 | switch -exact -- [lindex $list 0] { 134 | "list" - 135 | "set" { 136 | if {$subType != "value"} { 137 | error "[lindex $list 0] subtype of '$subType' must be value if specified" 138 | } 139 | return [lindex $List 1] 140 | } 141 | 142 | "map" { 143 | switch $subType { 144 | "key" { 145 | return [lindex $list 1] 146 | } 147 | 148 | "value" { 149 | return [lindex $list 2] 150 | } 151 | 152 | default { 153 | error "map subtype must be 'key' or 'value', you said '$subType'" 154 | } 155 | } 156 | } 157 | 158 | "tuple" { 159 | if {![string is integer -strict $subType]} { 160 | error "tuple subtype must be an integer, you said '$subType'" 161 | } 162 | 163 | set upper [expr {[llength $list] - 1}] 164 | 165 | if {$subType < 0 || $subType > $upper} { 166 | error "tuple subtype must be 0 to $upper, you said '$subType'" 167 | } 168 | 169 | return [lindex $list $subType] 170 | } 171 | 172 | default { 173 | error "invalid column type '$list'" 174 | } 175 | } 176 | 177 | error "bug" 178 | } 179 | 180 | # 181 | # quote - quote a cassandra element based on the data type of the element 182 | # 183 | proc quote {value {type text}} { 184 | switch $type { 185 | "ascii" - 186 | "varchar" - 187 | "text" { 188 | return "'[string map {' ''} $value]'" 189 | } 190 | 191 | "boolean" { 192 | return [expr {$value ? "true" : "false"}] 193 | } 194 | 195 | default { 196 | return $value 197 | } 198 | } 199 | } 200 | 201 | # 202 | # quote_typeof - quote_typeof wx.wx_metar $vlaue 203 | # 204 | proc quote_typeof {tableColumn value {subType ""}} { 205 | set type [typeof $tableColumn $subType] 206 | return [quote $value $type] 207 | } 208 | 209 | # 210 | # ::casstcl::connect - convenience function to create a cassandra object 211 | # with optional specification of host and port 212 | # 213 | # set cassHandle [::casstcl::connect -host $host -port $port -user $user -password $password] 214 | # 215 | proc connect {args} { 216 | package require cmdline 217 | 218 | set options { 219 | {host.arg "127.0.0.1" "address of cluster host"} 220 | {port.arg "9042" "port of cluster host"} 221 | {user.arg "" "specify user credentials"} 222 | {password.arg "" "specify password credentials"} 223 | } 224 | 225 | set usage "connect ?-host host? ?-port port? ?-user user? ?-password password?" 226 | 227 | if {[catch {array set params [::cmdline::getoptions args $options $usage]} catchResult] == 1} { 228 | error $catchResult 229 | } 230 | 231 | set cass [::casstcl::cass create #auto] 232 | 233 | $cass contact_points $params(host) 234 | $cass port $params(port) 235 | 236 | if {[info exists params(user)] && [info exists params(password)]} { 237 | $cass credentials $params(user) $params(password) 238 | } 239 | 240 | $cass connect 241 | 242 | return $cass 243 | } 244 | 245 | # 246 | # assemble_statement - given the name of a variable to contain a CQL 247 | # statement and a line containing possibly a statement or part of 248 | # a statement, append the line to the statement and return 1 if 249 | # a complete statement is present, else 0 250 | # 251 | # comments and blank lines are skipped 252 | # 253 | # the check for a complete statement is the mere presence of any semicolon, 254 | # kind of meatball, that means a semicolon can't appear in a quoted part 255 | # or anything 256 | # 257 | proc assemble_statement {statementVar line} { 258 | upvar $statementVar statement 259 | 260 | #puts "line: $line" 261 | 262 | set line [string trim $line] 263 | 264 | # drop blank lines and comments 265 | if {$line == "" || [string range $line 0 1] == "--"} { 266 | return 0 267 | } 268 | 269 | if {$statement == ""} { 270 | set statement $line 271 | } else { 272 | append statement " $line" 273 | } 274 | 275 | return [expr {[string first ";" $line] >= 0}] 276 | } 277 | 278 | # 279 | # run_fp - read from a file pointer and execute commands in cassandra 280 | # 281 | proc run_fp {cassHandle fp} { 282 | set query "" 283 | while {[gets $fp line] >= 0} { 284 | if {[assemble_statement query $line]} { 285 | puts "query: $query" 286 | $cassHandle exec $query 287 | set query "" 288 | } 289 | } 290 | } 291 | 292 | # 293 | # run_file - run CQL commands from a file 294 | # 295 | proc run_file {cassHandle file} { 296 | set fp [open $file] 297 | run_fp $cassHandle $fp 298 | close $fp 299 | } 300 | 301 | # 302 | # interact - provide a primitive cqlsh-like shell 303 | # 304 | proc interact {cassHandle} { 305 | set query "" 306 | set style parray 307 | 308 | while true { 309 | # emit prompt 310 | if {$query == ""} { 311 | puts -nonewline "tcqlsh> " 312 | } else { 313 | puts -nonewline "........> " 314 | } 315 | flush stdout 316 | 317 | gets stdin line 318 | 319 | if {[eof stdin]} { 320 | return 321 | } 322 | 323 | # Handle builtin commands, first line only. 324 | if {$query eq ""} { 325 | if {[catch {set builtin_args [lassign $line builtin]} catchResult]} { 326 | puts $catchResult 327 | puts "$::errorCode" 328 | continue 329 | } 330 | switch -- [string tolower $builtin] { 331 | cluster_version - 332 | keyspaces - 333 | tables - 334 | columns - 335 | columns_with_types { 336 | if {[catch {set result [$cassHandle $builtin {*}$builtin_args]} catchResult]} { 337 | puts $catchResult 338 | puts "$::errorCode" 339 | } else { 340 | puts $result 341 | } 342 | continue 343 | } 344 | schema { 345 | if {[catch {puts_schema $cassHandle {*}$builtin_args} catchResult]} { 346 | puts $catchResult 347 | puts "$::errorCode" 348 | } 349 | continue 350 | } 351 | style { 352 | if {[llength $builtin_args]} { 353 | set style [lindex $builtin_args 0] 354 | } else { 355 | puts $style 356 | } 357 | continue 358 | } 359 | exit { 360 | return 361 | } 362 | ? - 363 | help { 364 | puts "cluster_version, keyspaces, tables, columns, columns_with_types, schema, style, help, exit, or cql command" 365 | continue 366 | } 367 | } 368 | } 369 | 370 | if {[assemble_statement query $line]} { 371 | if {[catch {perform_select $style $cassHandle $query} catchResult] == 1} { 372 | puts "$catchResult" 373 | puts "$::errorCode" 374 | } 375 | set query "" 376 | } 377 | } 378 | } 379 | 380 | proc perform_select {style cassHandle query} { 381 | switch -exact $style { 382 | cols - 383 | columns { 384 | $cassHandle select -withnulls $query row { 385 | if {![info exists headers]} { 386 | set headers [lsort [array names row]] 387 | puts [join $headers "|"] 388 | } 389 | unset -nocomplain result 390 | foreach column $headers { 391 | lappend result $row($column) 392 | } 393 | puts [join $result "|"] 394 | } 395 | } 396 | default { 397 | $cassHandle select $query row { 398 | parray row 399 | puts "" 400 | } 401 | } 402 | } 403 | } 404 | 405 | proc puts_schema {cassHandle keyspace args} { 406 | if {![llength $args]} { 407 | if {[string match "*.*" $keyspace]} { 408 | set args [lassign [split $keyspace "."] keyspace] 409 | } else { 410 | set args [$cassHandle tables $keyspace] 411 | } 412 | } 413 | foreach table $args { 414 | set l [$cassHandle columns_with_types $keyspace $table] 415 | if {![llength $l]} { continue } 416 | puts "table $keyspace.$table (" 417 | foreach {col type} $l { 418 | puts "\t$col\t$type," 419 | } 420 | puts ")\n" 421 | } 422 | } 423 | 424 | # 425 | # translate_array_elements - given a list of pairs of element names, rename 426 | # any elements matching the first value to have the name of the second 427 | # 428 | # 429 | proc translate_array_elements {_row list} { 430 | upvar $_row row 431 | 432 | foreach "iVar oVar" $list { 433 | if {[info exists row($iVar)]} { 434 | set row($oVar) $row($iVar) 435 | unset row($iVar) 436 | } 437 | } 438 | } 439 | 440 | # 441 | # confirm_array_elements - return 1 if all elements in list are present in 442 | # array else 0 443 | # 444 | proc confirm_array_elements {_row list} { 445 | upvar $_row row 446 | 447 | foreach var $list { 448 | if {![info exists row($var)]} { 449 | return 0 450 | } 451 | } 452 | return 1 453 | } 454 | 455 | 456 | } ;# namespace ::casstcl 457 | 458 | # vim: set ts=4 sw=4 sts=4 noet : 459 | 460 | -------------------------------------------------------------------------------- /configure.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash -norc 2 | dnl This file is an input file used by the GNU "autoconf" program to 3 | dnl generate the file "configure", which is run during Tcl installation 4 | dnl to configure the system for the local environment. 5 | # 6 | # RCS: @(#) $Id: configure.in,v 1.50 2010/08/12 01:19:36 hobbs Exp $ 7 | 8 | #----------------------------------------------------------------------- 9 | # configure.in for casstcl 10 | #----------------------------------------------------------------------- 11 | 12 | #----------------------------------------------------------------------- 13 | # __CHANGE__ 14 | # Set your package name and version numbers here. 15 | # 16 | # This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION 17 | # set as provided. These will also be added as -D defs in your Makefile 18 | # so you can encode the package version directly into the source files. 19 | #----------------------------------------------------------------------- 20 | 21 | AC_INIT([casstcl], [2.14.0]) 22 | 23 | #----- 24 | # Version with patch stripped 25 | #----- 26 | 27 | AC_SUBST(PKG_MAJ_MIN,`expr $PACKAGE_VERSION : '\(.*\)\..*'`) 28 | 29 | #-------------------------------------------------------------------- 30 | # Call TEA_INIT as the first TEA_ macro to set up initial vars. 31 | # This will define a ${TEA_PLATFORM} variable == "unix" or "windows" 32 | # as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE. 33 | #-------------------------------------------------------------------- 34 | 35 | TEA_INIT([3.9]) 36 | 37 | AC_CONFIG_AUX_DIR(tclconfig) 38 | 39 | #-------------------------------------------------------------------- 40 | # Load the tclConfig.sh file 41 | #-------------------------------------------------------------------- 42 | 43 | TEA_PATH_TCLCONFIG 44 | TEA_LOAD_TCLCONFIG 45 | 46 | #-------------------------------------------------------------------- 47 | # Load the tkConfig.sh file if necessary (Tk extension) 48 | #-------------------------------------------------------------------- 49 | 50 | #TEA_PATH_TKCONFIG 51 | #TEA_LOAD_TKCONFIG 52 | 53 | #----------------------------------------------------------------------- 54 | # Handle the --prefix=... option by defaulting to what Tcl gave. 55 | # Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. 56 | #----------------------------------------------------------------------- 57 | 58 | TEA_PREFIX 59 | 60 | #----------------------------------------------------------------------- 61 | # Standard compiler checks. 62 | # This sets up CC by using the CC env var, or looks for gcc otherwise. 63 | # This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create 64 | # the basic setup necessary to compile executables. 65 | #----------------------------------------------------------------------- 66 | 67 | TEA_SETUP_COMPILER 68 | 69 | #----------------------------------------------------------------------- 70 | # __CHANGE__ 71 | # Specify the C source files to compile in TEA_ADD_SOURCES, 72 | # public headers that need to be installed in TEA_ADD_HEADERS, 73 | # stub library C source files to compile in TEA_ADD_STUB_SOURCES, 74 | # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. 75 | # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS 76 | # and PKG_TCL_SOURCES. 77 | #----------------------------------------------------------------------- 78 | 79 | TEA_ADD_SOURCES([tclcasstcl.c casstcl_batch.c casstcl_event.c 80 | casstcl_cassandra.c casstcl_consistency.c casstcl_error.c casstcl_future.c 81 | casstcl_log.c casstcl_prepared.c casstcl_types.c]) 82 | TEA_ADD_HEADERS([generic/casstcl.h generic/casstcl_batch.h 83 | generic/casstcl_event.h generic/casstcl_cassandra.h 84 | generic/casstcl_consistency.h generic/casstcl_error.h 85 | generic/casstcl_future.h generic/casstcl_log.h 86 | generic/casstcl_prepared.h generic/casstcl_types.h]) 87 | TEA_ADD_INCLUDES([]) 88 | TEA_ADD_LIBS([]) 89 | TEA_ADD_CFLAGS([]) 90 | TEA_ADD_STUB_SOURCES([]) 91 | TEA_ADD_TCL_SOURCES([casstcl.tcl]) 92 | 93 | #-------------------------------------------------------------------- 94 | # __CHANGE__ 95 | # A few miscellaneous platform-specific items: 96 | # 97 | # Define a special symbol for Windows (BUILD_sample in this case) so 98 | # that we create the export library with the dll. 99 | # 100 | # Windows creates a few extra files that need to be cleaned up. 101 | # You can add more files to clean if your extension creates any extra 102 | # files. 103 | # 104 | # TEA_ADD_* any platform specific compiler/build info here. 105 | #-------------------------------------------------------------------- 106 | #CLEANFILES="" 107 | if test "${TEA_PLATFORM}" = "windows" ; then 108 | AC_DEFINE(BUILD_tclspline, 1, [Build windows export dll]) 109 | CLEANFILES="$CLEANFILES *.lib *.dll *.exp *.ilk *.pdb vc*.pch" 110 | #TEA_ADD_SOURCES([win/winFile.c]) 111 | #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"]) 112 | else 113 | # Ensure no empty else clauses 114 | : 115 | #CLEANFILES="" 116 | #TEA_ADD_SOURCES([unix/unixFile.c]) 117 | #TEA_ADD_LIBS([-lsuperfly]) 118 | fi 119 | AC_SUBST(CLEANFILES) 120 | 121 | #-------------------------------------------------------------------- 122 | # __CHANGE__ 123 | # Choose which headers you need. Extension authors should try very 124 | # hard to only rely on the Tcl public header files. Internal headers 125 | # contain private data structures and are subject to change without 126 | # notice. 127 | # This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG 128 | #-------------------------------------------------------------------- 129 | 130 | TEA_PUBLIC_TCL_HEADERS 131 | #TEA_PRIVATE_TCL_HEADERS 132 | 133 | #TEA_PUBLIC_TK_HEADERS 134 | #TEA_PRIVATE_TK_HEADERS 135 | #TEA_PATH_X 136 | 137 | #-------------------------------------------------------------------- 138 | # Check whether --enable-threads or --disable-threads was given. 139 | # This auto-enables if Tcl was compiled threaded. 140 | #-------------------------------------------------------------------- 141 | 142 | TEA_ENABLE_THREADS 143 | 144 | #-------------------------------------------------------------------- 145 | # The statement below defines a collection of symbols related to 146 | # building as a shared library instead of a static library. 147 | #-------------------------------------------------------------------- 148 | 149 | TEA_ENABLE_SHARED 150 | 151 | #-------------------------------------------------------------------- 152 | # This macro figures out what flags to use with the compiler/linker 153 | # when building shared/static debug/optimized objects. This information 154 | # can be taken from the tclConfig.sh file, but this figures it all out. 155 | #-------------------------------------------------------------------- 156 | 157 | TEA_CONFIG_CFLAGS 158 | 159 | #-------------------------------------------------------------------- 160 | # Set the default compiler switches based on the --enable-symbols option. 161 | #-------------------------------------------------------------------- 162 | 163 | TEA_ENABLE_SYMBOLS 164 | 165 | #-------------------------------------------------------------------- 166 | # Everyone should be linking against the Tcl stub library. If you 167 | # can't for some reason, remove this definition. If you aren't using 168 | # stubs, you also need to modify the SHLIB_LD_LIBS setting below to 169 | # link against the non-stubbed Tcl library. Add Tk too if necessary. 170 | #-------------------------------------------------------------------- 171 | 172 | AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs]) 173 | #AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs]) 174 | 175 | #-------------------------------------------------------------------- 176 | # This macro generates a line to use when building a library. It 177 | # depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS, 178 | # and TEA_LOAD_TCLCONFIG macros above. 179 | #-------------------------------------------------------------------- 180 | 181 | TEA_MAKE_LIB 182 | 183 | #-------------------------------------------------------------------- 184 | # Determine the name of the tclsh and/or wish executables in the 185 | # Tcl and Tk build directories or the location they were installed 186 | # into. These paths are used to support running test cases only, 187 | # the Makefile should not be making use of these paths to generate 188 | # a pkgIndex.tcl file or anything else at extension build time. 189 | #-------------------------------------------------------------------- 190 | 191 | TEA_PROG_TCLSH 192 | #TEA_PROG_WISH 193 | 194 | #-------------------------------------------------------------------- 195 | # Finally, substitute all of the various values into the Makefile. 196 | # You may alternatively have a special pkgIndex.tcl.in or other files 197 | # which require substituting th AC variables in. Include these here. 198 | #-------------------------------------------------------------------- 199 | 200 | AC_OUTPUT([Makefile pkgIndex.tcl]) 201 | -------------------------------------------------------------------------------- /demos/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | assumes you have a cluster on 127.0.0.1 port 9042 4 | 5 | if you don't, set up a .casstclrc file in your home directory that is something like 6 | 7 | ```tcl 8 | set ::params(host) 172.16.32.1 9 | ``` 10 | 11 | To create the test keyspace and tables, feed to cqlshrc the file **demo.cql** or run 12 | 13 | ```sh 14 | tclsh run.tcl -file demo.cql 15 | ``` 16 | 17 | Run the program to insert into casstcl_test.numbers: 18 | 19 | ```sh 20 | tclsh numbers.tcl 21 | ``` 22 | 23 | Poke around in cqlsh and do a select on that 24 | 25 | Run selnumbers.tcl to see the row in casstcl_test.numbers: 26 | 27 | ```sh 28 | tclsh selnumbers.tcl 29 | ``` 30 | 31 | 32 | -------------------------------------------------------------------------------- /demos/blobbie.tcl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | proc insert_blobbie {n} { 5 | set row(row) $n 6 | set row(blobbie) "will it blend" 7 | 8 | set query "insert into casstcl_test.rock_blobster (row, blobbie) values (?, ?);" 9 | set prepared [$::cass prepare #auto casstcl_test.rock_blobster $query] 10 | 11 | $::cass exec -prepared $prepared [array get row] 12 | } 13 | 14 | source connect.tcl 15 | 16 | cass_connect 17 | 18 | insert_blobbie 0 19 | 20 | 21 | -------------------------------------------------------------------------------- /demos/connect.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # connect to cassandra 3 | # 4 | # change contact points and possibly port below if needed to connect to 5 | # your desired cassandra cluster 6 | # 7 | 8 | if {[lsearch /usr/local/lib $auto_path] < 0} { 9 | lappend auto_path /usr/local/lib 10 | } 11 | 12 | package require casstcl 13 | package require cmdline 14 | 15 | if {![info exists ::params(host)]} { 16 | set ::params(host) 127.0.0.1 17 | } 18 | 19 | if {![info exists ::params(port)]} { 20 | set ::params(port) 9042 21 | } 22 | 23 | proc import_casstclrc {} { 24 | if {[file readable ~/.casstclrc]} { 25 | uplevel #0 source ~/.casstclrc 26 | } 27 | } 28 | 29 | import_casstclrc 30 | 31 | proc cass_connect {} { 32 | set ::cass [::casstcl::cass create #auto] 33 | 34 | $::cass contact_points $::params(host) 35 | $::cass port $::params(port) 36 | $::cass connect 37 | 38 | return $::cass 39 | } 40 | 41 | -------------------------------------------------------------------------------- /demos/demo.cql: -------------------------------------------------------------------------------- 1 | 2 | 3 | DROP KEYSPACE IF EXISTS casstcl_test; 4 | 5 | CREATE KEYSPACE casstcl_test WITH replication = {'class': 'SimpleStrategy', 'replication_factor':1}; 6 | 7 | USE casstcl_test; 8 | 9 | DROP TABLE IF EXISTS numbers; 10 | 11 | CREATE TABLE numbers ( 12 | row int, 13 | mylist list, 14 | myset set, 15 | mymap map, 16 | primary key(row) 17 | ); 18 | 19 | CREATE TABLE rock_blobster ( 20 | row int, 21 | blobbie blob, 22 | primary key(row) 23 | ); 24 | 25 | -------------------------------------------------------------------------------- /demos/example.tcl: -------------------------------------------------------------------------------- 1 | 2 | # mac 3 | lappend auto_path /usr/local/lib 4 | 5 | package require casstcl 6 | 7 | set cass [::casstcl::cass create #auto] 8 | 9 | $cass contact_points 127.0.0.1 10 | $cass port 9042 11 | $cass connect 12 | 13 | $cass exec "CREATE KEYSPACE examples WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '1' };" 14 | 15 | $cass exec "CREATE TABLE examples.basic (key text, bln boolean, flt float, dbl double, i32 int, i64 bigint, PRIMARY KEY (key));" 16 | 17 | $cass exec "insert into examples.basic (key, bln, flt, dbl, i32, i64) values ('1', true, 0.001, 0.0002, 1, 2);" 18 | 19 | $cass select "select * from examples.basic;" row {parray row} 20 | 21 | 22 | -------------------------------------------------------------------------------- /demos/gen-dataset.tcl: -------------------------------------------------------------------------------- 1 | 2 | 3 | set firstNames [list "安 An" "波 Bo" "成 Cheng" "德 De" "东 Dong" "峰 Feng" "刚 Gang" "国 Guo" "辉 Hui" "健 Jian" "杰 Jie" "康 Kang" "亮 Liang" "宁 Ning" "鹏 Peng" "涛 Tao" "伟 Wei" "勇 Yong" "文 Wen" "爱 Ai" "碧 Bi" "彩 Cai" "丹 Dan" "芳 Fang" "红 Hong" "惠 Hui" "娟 Juan" "兰 Lan" "莉 Li" "丽 Li" "莲 Lian" "娜 Na" "妮 Ni" "倩 Qian" "琼 Qiong" "珊 Shan" "淑 Shu" "婷 Ting" "霞 Xia" "娴 Xian" "嫣 Yan" "云 Yun" "贞 Zhen" Noel Aron Alesandro Enrik Flavio Luis Aleskio Oliver Jack Harry Jacob Charlie Thomas Oscar William James "Alexander (Александр)" "Sergei (Сергей)" "Dmitry (Дмитрий)" "Andrei (Андрей)" "Alexey (Алексей)" "Maxim (Максим)" "Evgeny (Евгений)" "Ivan (Иван)" "Mikhail (Михаил)" "Ilya (Илья)" Laia Carlota Emma Lara Martina Aina Maria Blanca Laura "Anastasia (Анастасия)" "Yelena (Елена)" "Olga (Ольга)" "Natalia (Наталья)" "Yekaterina (Екатерина)" "Anna (Анна)" "Tatiana (Татьяна)" "Maria (Мария)" "Irina (Ирина)" "Yulia (Юлия)" Amelia Olivia Emily Jessica Ava Isla Poppy Isabella Sophie Emilia Ella Helmi Linnea Birta Elizabete Lachlan Charlotte Ruby Chloe Mia Maia "Maryam مريم" "Jana جنى" "Noor نور" Thiago Juan Felipe Daniel Mark Karl Nugget Sean Adam Jeff James Ashley Sherron Lindsay Ryan Tim Vakesa Vishan Zachery Kim Philip Rijul Baron Bill Debbie Aarav Mohammad Vihaan Aditya Sai Arjun Aryan Reyansh Vivaan Krishna Aadhya Ananya Saanvi Aaradhya Anaya Aanya Anika Pari Myra Shanaya Ellyn Karyn Annette Agnes Joshua Nathan Dorothy Kristopher Shawn Morgan Robert Eugene] 4 | 5 | set lastNames [list Devi Singh Kumar Das Kaur Cohen Levi Mizrachi Peretz ბერიძე მამედოვი 王 李 张 刘 陈 杨 黄 Baker Duell McNett Zhōu Sulak "佐藤" "鈴木" "高橋" "田中" "渡辺" "伊藤" 金김李Santos Reyes Cruz Bautista Ocampo "del Rosario" Gonzales Lopez Garcia "dela Cruz" Mendoza Yılmaz Kaya Demir Şahin Çelik Yıldız "Nguyễn 阮" "Trần 陳" "Lê 黎" "Phạm 范" Díaz Mora Gruber Huber Bauer Wagner Məmmədov Əliyev Ivanoŭ Peeters Janssens Maes Jacobs Mertens Horvat Nováková Svobodová Dvořáková Nielsen Jensen Sørensen Jørgensen Papadopoulos Angelopoulos Nagy Tóth Balogh Farkas Murphy "Ó Ceallaigh" Rossi Russo Ferrari Esposito Bianchi Romano Ricci "De Luca" Giordano Rizzo Lombardi Moretti Sante Ravelli Zogaj Gashi Grasniqi Morina Kelmendi Beqiri Jakupi Kastrati Avdiu Borg Camilleri Vella Farrugia Andov Angelov Kitanovski "De Jong" "De Vries" "Van Dijk" Bakker Nowak Kowalski Wiśniewski Wójcik Смирно́в Ивано́в Кузнецо́в Козло́в Лебедев Соловьёв Виногра́дов Jovanović Zupančič Smith Johnson Williams Brown Jones Miller Davis Garcia Rodriguez Wilson Martinez Anderson Taylor Thomas Hernandez Moore Martin Jackson] 6 | 7 | proc gen_nameset {} { 8 | global firstNames lastNames 9 | 10 | foreach last2 $lastNames { 11 | foreach first $firstNames { 12 | foreach last1 $lastNames { 13 | puts [list $first [list $last1 $last2]] 14 | } 15 | } 16 | } 17 | } 18 | 19 | gen_nameset 20 | -------------------------------------------------------------------------------- /demos/numbers.tcl: -------------------------------------------------------------------------------- 1 | 2 | 3 | array set numbers [list 0 zero 1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 eight 9 nine 10 ten 11 eleven 12 twelve 13 thirteen 14 fourteen 15 fifteen 16 sixteen 17 seventeen 18 eighteen 19 nineteen 20 twenty 30 thirty 40 forty 50 fifty 60 sixty 70 seventy 80 eighty 90 ninety 100 hundred 1000 thousand 1000000 million 1000000000 billion] 4 | 5 | proc number_name {number} { 6 | if {[info exists ::numbers($number)]} { 7 | return $::numbers($number) 8 | } 9 | } 10 | 11 | proc insert_numbers {n} { 12 | set row(row) $n 13 | set row(mylist) [array names ::numbers] 14 | set row(myset) [array names ::numbers] 15 | set row(mymap) [array get ::numbers] 16 | 17 | set query "insert into casstcl_test.numbers (row, mylist, myset, mymap) values (?, ?, ?, ?);" 18 | set prepared [$::cass prepare #auto casstcl_test.numbers $query] 19 | 20 | $::cass exec -prepared $prepared [array get row] 21 | } 22 | 23 | source connect.tcl 24 | 25 | cass_connect 26 | 27 | insert_numbers 0 28 | 29 | 30 | -------------------------------------------------------------------------------- /demos/run.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # run cql files 3 | # 4 | # 5 | 6 | source connect.tcl 7 | 8 | proc read_and_run {fp} { 9 | set query "" 10 | while {[gets $fp line] >= 0} { 11 | #puts "line: $line" 12 | if {$line == ""} { 13 | continue 14 | } 15 | 16 | append query " [string trim $line]" 17 | 18 | if {[string first ";" $line] < 0} { 19 | continue 20 | } 21 | 22 | puts "query: $query" 23 | $::cass exec $query 24 | set query "" 25 | } 26 | } 27 | 28 | proc run_file {file} { 29 | set fp [open $file] 30 | read_and_run $fp 31 | close $fp 32 | } 33 | 34 | proc usage {} { 35 | puts stderr "usage: $::argv0 cqlFile" 36 | } 37 | 38 | proc main {{argv ""}} { 39 | set options { 40 | {host.arg "127.0.0.1" "address of cluster host"} 41 | {port.arg "9042" "port of cluster host"} 42 | {file.arg "" "specify file to read cql from"} 43 | } 44 | 45 | set usage ":$::argv0 ?-host host? ?-port port? ?-file file?" 46 | 47 | if {[catch {array set ::params [::cmdline::getKnownOptions argv $options $usage]} catchResult] == 1} { 48 | puts stderr $catchResult 49 | exit 1 50 | } 51 | 52 | import_casstclrc 53 | 54 | cass_connect 55 | 56 | if {$::params(file) == ""} { 57 | read_and_run stdin 58 | } else { 59 | run_file $::params(file) 60 | } 61 | } 62 | 63 | if {!$tcl_interactive} { 64 | main $argv 65 | } 66 | -------------------------------------------------------------------------------- /demos/selnumbers.tcl: -------------------------------------------------------------------------------- 1 | 2 | 3 | proc select_numbers {n} { 4 | set query "select * from casstcl_test.numbers" 5 | set prepared [$::cass prepare #auto casstcl_test.numbers $query] 6 | 7 | $::cass select $query row { 8 | parray row 9 | puts "" 10 | } 11 | } 12 | 13 | source connect.tcl 14 | 15 | cass_connect 16 | 17 | select_numbers 0 18 | 19 | 20 | -------------------------------------------------------------------------------- /generic/casstcl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl package 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #if (CASS_VERSION_MAJOR < 2 || (CASS_VERSION_MAJOR == 2 && CASS_VERSION_MINOR < 8)) 24 | #define CASS_PRE_2_8 25 | #endif 26 | 27 | #if (CASS_VERSION_MAJOR < 2 || (CASS_VERSION_MAJOR == 2 && CASS_VERSION_MINOR < 10)) 28 | #define CASS_PRE_2_10 29 | #endif 30 | 31 | #if CASS_VERSION_MAJOR > 2 || \ 32 | (CASS_VERSION_MAJOR == 2 && (CASS_VERSION_MINOR > 3 || \ 33 | (CASS_VERSION_MINOR == 3 && \ 34 | CASS_VERSION_PATCH >= 0))) 35 | #define CASS_POST_2_3_0 36 | #endif 37 | 38 | /* 39 | * NOTE: If we are using Tcl 8.5, there are several new or modified 40 | * things we use from Tcl 8.6 that require slight workarounds. 41 | */ 42 | #if !defined(TCL_MAJOR_VERSION) || !defined(TCL_MINOR_VERSION) || \ 43 | (TCL_MAJOR_VERSION < 8) || ((TCL_MAJOR_VERSION == 8) && \ 44 | (TCL_MINOR_VERSION < 6)) 45 | # define Tcl_GetErrorLine(interp) ((interp)->errorLine) 46 | #endif 47 | 48 | #define CASS_SESSION_MAGIC 7138570 49 | #define CASS_FUTURE_MAGIC 71077345 50 | #define CASS_BATCH_MAGIC 14215469 51 | #define CASS_PREPARED_MAGIC 713832281 52 | 53 | #define CASSTCL_FUTURE_QUEUE_HEAD_FLAG 1 54 | #define CASSTCL_FUTURE_CALLBACK_ON_ERROR_ONLY 2 55 | 56 | /* 57 | * This is the absolute limit on the whole number of seconds that we can 58 | * support for the Cassandra 'timestamp' data type normalization routines. 59 | * When multiplied by 1000 and having 1000 added to that result, it MUST 60 | * fit into a 64-bit signed integer, for both positive and negative signs. 61 | * Also, it must be capable of being round-tripped to double without any 62 | * loss of precision. 63 | */ 64 | #define CASS_TIMESTAMP_UPPER_LIMIT (4294967295LL) 65 | #define CASS_TIMESTAMP_LOWER_LIMIT (-CASS_TIMESTAMP_UPPER_LIMIT) 66 | 67 | extern Tcl_ObjType casstcl_cassTypeTclType; 68 | extern Tcl_Obj *casstcl_loggingCallbackObj; 69 | extern Tcl_ThreadId casstcl_loggingCallbackThreadId; 70 | /* 71 | ** NOTE: The types in this section were "borrowed" from version 1.0 of the 72 | ** cpp-driver. 73 | */ 74 | #if CASS_VERSION_MAJOR > 1 75 | typedef size_t cass_size_t; 76 | 77 | typedef struct CassBytes_ { 78 | const cass_byte_t* data; 79 | cass_size_t size; 80 | } CassBytes; 81 | 82 | typedef struct CassString_ { 83 | const char* data; 84 | cass_size_t length; 85 | } CassString; 86 | 87 | typedef struct CassDecimal_ { 88 | cass_int32_t scale; 89 | CassBytes varint; 90 | } CassDecimal; 91 | #endif 92 | 93 | typedef struct casstcl_cassTypeInfo { 94 | CassValueType cassValueType; 95 | CassValueType valueSubType1; 96 | CassValueType valueSubType2; 97 | } casstcl_cassTypeInfo; 98 | 99 | typedef struct casstcl_sessionClientData 100 | { 101 | int cass_session_magic; 102 | Tcl_Interp *interp; 103 | CassCluster *cluster; 104 | CassSession* session; 105 | CassSsl *ssl; 106 | Tcl_Command cmdToken; 107 | Tcl_ThreadId threadId; 108 | Tcl_Obj *loggingCallbackObj; 109 | } casstcl_sessionClientData; 110 | 111 | typedef struct casstcl_futureClientData 112 | { 113 | int cass_future_magic; 114 | int flags; 115 | casstcl_sessionClientData *ct; 116 | CassFuture *future; 117 | Tcl_Command cmdToken; 118 | Tcl_Obj *callbackObj; 119 | } casstcl_futureClientData; 120 | 121 | typedef struct casstcl_batchClientData 122 | { 123 | int cass_batch_magic; 124 | casstcl_sessionClientData *ct; 125 | CassBatch *batch; 126 | CassBatchType batchType; 127 | Tcl_Command cmdToken; 128 | CassConsistency consistency; 129 | int count; 130 | } casstcl_batchClientData; 131 | 132 | typedef struct casstcl_preparedClientData 133 | { 134 | int cass_prepared_magic; 135 | casstcl_sessionClientData *ct; 136 | const CassPrepared *prepared; 137 | char *string; 138 | Tcl_Obj *tableNameObj; 139 | Tcl_Command cmdToken; 140 | } casstcl_preparedClientData; 141 | 142 | typedef struct casstcl_loggingEvent 143 | { 144 | Tcl_Event event; 145 | Tcl_Interp *interp; 146 | CassLogMessage message; 147 | } casstcl_loggingEvent; 148 | 149 | typedef struct casstcl_futureEvent 150 | { 151 | Tcl_Event event; 152 | casstcl_futureClientData *fcd; 153 | } casstcl_futureEvent; 154 | 155 | #ifdef __cplusplus 156 | extern "C" { 157 | #endif 158 | 159 | extern int 160 | casstcl_cassObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objvp[]); 161 | 162 | extern casstcl_futureClientData * 163 | casstcl_future_command_to_futureClientData (Tcl_Interp *interp, char *futureCommandName); 164 | 165 | extern int 166 | casstcl_future_error_to_tcl (casstcl_sessionClientData *ct, CassError cassError, CassFuture *future); 167 | 168 | extern int 169 | casstcl_cass_value_to_tcl_obj (casstcl_sessionClientData *ct, const CassValue *cassValue, Tcl_Obj **tclObj); 170 | 171 | #ifdef __cplusplus 172 | } 173 | #endif 174 | 175 | 176 | /* vim: set ts=4 sw=4 sts=4 noet : */ 177 | -------------------------------------------------------------------------------- /generic/casstcl_batch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_batcj - Functions used to create, delete, and handle batches 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_batch.h" 13 | #include "casstcl_cassandra.h" 14 | #include "casstcl_error.h" 15 | #include "casstcl_consistency.h" 16 | 17 | #include 18 | 19 | /* 20 | *-------------------------------------------------------------- 21 | * 22 | * casstcl_batchObjectDelete -- command deletion callback routine. 23 | * 24 | * Results: 25 | * ...destroys the batch object. 26 | * ...frees memory. 27 | * 28 | * Side effects: 29 | * None. 30 | * 31 | *-------------------------------------------------------------- 32 | */ 33 | void 34 | casstcl_batchObjectDelete (ClientData clientData) 35 | { 36 | casstcl_batchClientData *bcd = (casstcl_batchClientData *)clientData; 37 | 38 | assert (bcd->cass_batch_magic == CASS_BATCH_MAGIC); 39 | 40 | cass_batch_free (bcd->batch); 41 | ckfree((char *)clientData); 42 | } 43 | 44 | /* 45 | *-------------------------------------------------------------- 46 | * 47 | * casstcl_obj_to_cass_batch_type -- lookup a string in a Tcl object 48 | * to be one of the batch_type strings for CassBatchType and set 49 | * a pointer to a passed-in CassBatchType value to the corresponding 50 | * CassBatchType such as CASS_BATCH_TYPE_LOGGED, etc 51 | * 52 | * Results: 53 | * ...cass batch type gets set 54 | * ...a standard Tcl result is returned 55 | * 56 | * Side effects: 57 | * None. 58 | * 59 | *-------------------------------------------------------------- 60 | */ 61 | int 62 | casstcl_obj_to_cass_batch_type (Tcl_Interp *interp, Tcl_Obj *tclObj, CassBatchType *cassBatchType) { 63 | int batchTypeIndex; 64 | 65 | static CONST char *batchTypes[] = { 66 | "logged", 67 | "unlogged", 68 | "counter", 69 | NULL 70 | }; 71 | 72 | enum batchTypes { 73 | OPT_LOGGED, 74 | OPT_UNLOGGED, 75 | OPT_COUNTER 76 | }; 77 | 78 | // argument must be one of the subOptions defined above 79 | if (Tcl_GetIndexFromObj (interp, tclObj, batchTypes, "batch_type", 80 | TCL_EXACT, &batchTypeIndex) != TCL_OK) { 81 | return TCL_ERROR; 82 | } 83 | 84 | switch ((enum batchTypes) batchTypeIndex) { 85 | case OPT_LOGGED: { 86 | *cassBatchType = CASS_BATCH_TYPE_LOGGED; 87 | break; 88 | } 89 | 90 | case OPT_UNLOGGED: { 91 | *cassBatchType = CASS_BATCH_TYPE_UNLOGGED; 92 | break; 93 | } 94 | 95 | case OPT_COUNTER: { 96 | *cassBatchType = CASS_BATCH_TYPE_COUNTER; 97 | break; 98 | } 99 | } 100 | 101 | return TCL_OK; 102 | } 103 | 104 | /* 105 | *-------------------------------------------------------------- 106 | * 107 | * casstcl_batch_type_to_batch_type_string -- given a CassBatchType 108 | * code return a string corresponding to the CassBatchType constant 109 | * 110 | * Results: 111 | * returns a pointer to a const char * 112 | * 113 | * Side effects: 114 | * None. 115 | * 116 | *-------------------------------------------------------------- 117 | */ 118 | const char *casstcl_batch_type_to_batch_type_string (CassBatchType cassBatchType) 119 | { 120 | switch (cassBatchType) { 121 | case CASS_BATCH_TYPE_LOGGED: 122 | return "logged"; 123 | 124 | case CASS_BATCH_TYPE_UNLOGGED: 125 | return "unlogged"; 126 | 127 | case CASS_BATCH_TYPE_COUNTER: 128 | return "counter"; 129 | 130 | default: 131 | return "unknown"; 132 | } 133 | } 134 | 135 | /* 136 | *-------------------------------------------------------------- 137 | * 138 | * casstcl_batch_command_to_batchClientData -- given a batch command name, 139 | * find it in the interpreter and return a pointer to its batch client 140 | * data or NULL 141 | * 142 | * Results: 143 | * 144 | * Side effects: 145 | * None. 146 | * 147 | *-------------------------------------------------------------- 148 | */ 149 | casstcl_batchClientData * 150 | casstcl_batch_command_to_batchClientData (Tcl_Interp *interp, char *batchCommandName) 151 | { 152 | Tcl_CmdInfo batchCmdInfo; 153 | 154 | if (!Tcl_GetCommandInfo (interp, batchCommandName, &batchCmdInfo)) { 155 | return NULL; 156 | } 157 | 158 | casstcl_batchClientData *bcd = (casstcl_batchClientData *)batchCmdInfo.objClientData; 159 | if (bcd->cass_batch_magic != CASS_BATCH_MAGIC) { 160 | return NULL; 161 | } 162 | 163 | return bcd; 164 | } 165 | 166 | 167 | /* 168 | *---------------------------------------------------------------------- 169 | * 170 | * casstcl_createBatchObjectCommand -- 171 | * 172 | * given a casstcl_sessionClientData pointer, an object name (or "#auto"), 173 | * and a CASS_BATCH_TYPE, create a corresponding batch object command 174 | * 175 | * Results: 176 | * A standard Tcl result 177 | * 178 | *---------------------------------------------------------------------- 179 | */ 180 | int 181 | casstcl_createBatchObjectCommand (casstcl_sessionClientData *ct, char *commandName, CassBatchType cassBatchType) 182 | { 183 | // allocate one of our cass client data objects for Tcl and configure it 184 | casstcl_batchClientData *bcd = (casstcl_batchClientData *)ckalloc (sizeof (casstcl_batchClientData)); 185 | Tcl_Interp *interp = ct->interp; 186 | 187 | bcd->cass_batch_magic = CASS_BATCH_MAGIC; 188 | bcd->ct = ct; 189 | bcd->batch = cass_batch_new (cassBatchType); 190 | bcd->batchType = cassBatchType; 191 | bcd->consistency = CASS_CONSISTENCY_ONE; 192 | bcd->count = 0; 193 | 194 | #define BATCH_STRING_FORMAT "batch%lu" 195 | // if commandName is #auto, generate a unique name for the object 196 | int autoGeneratedName = 0; 197 | if (strcmp (commandName, "#auto") == 0) { 198 | static unsigned long nextAutoCounter = 0; 199 | int baseNameLength = snprintf (NULL, 0, BATCH_STRING_FORMAT, nextAutoCounter) + 1; 200 | commandName = ckalloc (baseNameLength); 201 | snprintf (commandName, baseNameLength, BATCH_STRING_FORMAT, nextAutoCounter++); 202 | autoGeneratedName = 1; 203 | } 204 | 205 | // create a Tcl command to interface to the batch object 206 | bcd->cmdToken = Tcl_CreateObjCommand (interp, commandName, casstcl_batchObjectObjCmd, bcd, casstcl_batchObjectDelete); 207 | // set the full name to the command in the interpreter result 208 | Tcl_GetCommandFullName(interp, bcd->cmdToken, Tcl_GetObjResult (interp)); 209 | if (autoGeneratedName == 1) { 210 | ckfree(commandName); 211 | } 212 | 213 | return TCL_OK; 214 | } 215 | 216 | 217 | /* 218 | *---------------------------------------------------------------------- 219 | * 220 | * casstcl_batchObjectObjCmd -- 221 | * 222 | * dispatches the subcommands of a casstcl batch-handling command 223 | * 224 | * Results: 225 | * stuff 226 | * 227 | *---------------------------------------------------------------------- 228 | */ 229 | int 230 | casstcl_batchObjectObjCmd(ClientData cData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 231 | { 232 | int optIndex; 233 | casstcl_batchClientData *bcd = (casstcl_batchClientData *)cData; 234 | int resultCode = TCL_OK; 235 | 236 | static CONST char *options[] = { 237 | "add", 238 | "upsert", 239 | "count", 240 | "consistency", 241 | "reset", 242 | "delete", 243 | NULL 244 | }; 245 | 246 | enum options { 247 | OPT_ADD, 248 | OPT_UPSERT, 249 | OPT_COUNT, 250 | OPT_CONSISTENCY, 251 | OPT_RESET, 252 | OPT_DELETE 253 | }; 254 | 255 | /* basic validation of command line arguments */ 256 | if (objc < 2) { 257 | Tcl_WrongNumArgs (interp, 1, objv, "subcommand ?args?"); 258 | return TCL_ERROR; 259 | } 260 | 261 | if (Tcl_GetIndexFromObj (interp, objv[1], options, "option", TCL_EXACT, &optIndex) != TCL_OK) { 262 | return TCL_ERROR; 263 | } 264 | 265 | switch ((enum options) optIndex) { 266 | case OPT_ADD: { 267 | CassStatement* statement = NULL; 268 | if (casstcl_make_statement_from_objv (bcd->ct, objc, objv, 2, &statement) == TCL_ERROR) { 269 | return TCL_ERROR; 270 | } 271 | 272 | CassError cassError = cass_batch_add_statement (bcd->batch, statement); 273 | cass_statement_free (statement); 274 | 275 | if (cassError == CASS_OK) { 276 | bcd->count++; 277 | } else { 278 | Tcl_AppendResult (interp, " while adding statement to batch", NULL); 279 | return casstcl_cass_error_to_tcl (bcd->ct, cassError); 280 | } 281 | 282 | break; 283 | } 284 | 285 | case OPT_UPSERT: { 286 | CassStatement* statement = NULL; 287 | 288 | resultCode = casstcl_make_upsert_statement_from_objv (bcd->ct, objc - 2, &objv[2], NULL, &statement); 289 | 290 | if (resultCode != TCL_ERROR) { 291 | //printf("calling cass_batch_add_statement\n"); 292 | CassError cassError; 293 | cassError = cass_batch_add_statement (bcd->batch, statement); 294 | cass_statement_free (statement); 295 | //printf("returned from cass_batch_add_statement, cassError %d\n", cassError); 296 | 297 | if (cassError == CASS_OK) { 298 | bcd->count++; 299 | } else { 300 | return casstcl_cass_error_to_tcl (bcd->ct, cassError); 301 | } 302 | } 303 | 304 | break; 305 | } 306 | 307 | // count - return a count of rows in the batch 308 | case OPT_COUNT: { 309 | if (objc != 2) { 310 | Tcl_WrongNumArgs (interp, 2, objv, ""); 311 | return TCL_ERROR; 312 | } 313 | 314 | Tcl_SetObjResult (interp, Tcl_NewIntObj (bcd->count)); 315 | break; 316 | } 317 | 318 | 319 | case OPT_CONSISTENCY: { 320 | CassConsistency cassConsistency; 321 | 322 | if ((objc < 2) || (objc > 3)) { 323 | Tcl_WrongNumArgs (interp, 2, objv, "?consistency?"); 324 | return TCL_ERROR; 325 | } 326 | 327 | if (objc == 2) { 328 | Tcl_SetObjResult (interp, Tcl_NewStringObj (casstcl_cass_consistency_to_string (bcd->consistency), -1)); 329 | return TCL_OK; 330 | } 331 | 332 | if (casstcl_obj_to_cass_consistency(bcd->ct, objv[2], &cassConsistency) == TCL_ERROR) { 333 | return TCL_ERROR; 334 | } 335 | 336 | CassError cassError = cass_batch_set_consistency (bcd->batch, cassConsistency); 337 | bcd->consistency = cassConsistency; 338 | if (cassError != CASS_OK) { 339 | return casstcl_cass_error_to_tcl (bcd->ct, cassError); 340 | } 341 | break; 342 | } 343 | 344 | case OPT_RESET: { 345 | if (objc != 2) { 346 | Tcl_WrongNumArgs (interp, 2, objv, ""); 347 | return TCL_ERROR; 348 | } 349 | 350 | cass_batch_free (bcd->batch); 351 | bcd->batch = cass_batch_new (bcd->batchType); 352 | bcd->count = 0; 353 | CassError cassError = cass_batch_set_consistency (bcd->batch, bcd->consistency); 354 | if (cassError != CASS_OK) { 355 | return casstcl_cass_error_to_tcl (bcd->ct, cassError); 356 | } 357 | 358 | break; 359 | } 360 | 361 | case OPT_DELETE: { 362 | if (objc != 2) { 363 | Tcl_WrongNumArgs (interp, 2, objv, ""); 364 | return TCL_ERROR; 365 | } 366 | 367 | if (Tcl_DeleteCommandFromToken (bcd->ct->interp, bcd->cmdToken) == TCL_ERROR) { 368 | resultCode = TCL_ERROR; 369 | } 370 | break; 371 | } 372 | } 373 | return resultCode; 374 | } 375 | 376 | 377 | 378 | /* vim: set ts=4 sw=4 sts=4 noet : */ 379 | -------------------------------------------------------------------------------- /generic/casstcl_batch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_batch 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *-------------------------------------------------------------- 13 | * 14 | * casstcl_obj_to_cass_batch_type -- lookup a string in a Tcl object 15 | * to be one of the batch_type strings for CassBatchType and set 16 | * a pointer to a passed-in CassBatchType value to the corresponding 17 | * CassBatchType such as CASS_BATCH_TYPE_LOGGED, etc 18 | * 19 | * Results: 20 | * ...cass batch type gets set 21 | * ...a standard Tcl result is returned 22 | * 23 | * Side effects: 24 | * None. 25 | * 26 | *-------------------------------------------------------------- 27 | */ 28 | int casstcl_obj_to_cass_batch_type (Tcl_Interp *interp, Tcl_Obj *tclObj, CassBatchType *cassBatchType); 29 | /* 30 | *-------------------------------------------------------------- 31 | * 32 | * casstcl_batch_type_to_batch_type_string -- given a CassBatchType 33 | * code return a string corresponding to the CassBatchType constant 34 | * 35 | * Results: 36 | * returns a pointer to a const char * 37 | * 38 | * Side effects: 39 | * None. 40 | * 41 | *-------------------------------------------------------------- 42 | */ 43 | const char *casstcl_batch_type_to_batch_type_string (CassBatchType cassBatchType); 44 | 45 | /* 46 | *-------------------------------------------------------------- 47 | * 48 | * casstcl_batch_command_to_batchClientData -- given a batch command name, 49 | * find it in the interpreter and return a pointer to its batch client 50 | * data or NULL 51 | * 52 | * Results: 53 | * 54 | * Side effects: 55 | * None. 56 | * 57 | *-------------------------------------------------------------- 58 | */ 59 | casstcl_batchClientData * casstcl_batch_command_to_batchClientData (Tcl_Interp *interp, char *batchCommandName); 60 | 61 | /* 62 | *---------------------------------------------------------------------- 63 | * 64 | * casstcl_createBatchObjectCommand -- 65 | * 66 | * given a casstcl_sessionClientData pointer, an object name (or "#auto"), 67 | * and a CASS_BATCH_TYPE, create a corresponding batch object command 68 | * 69 | * Results: 70 | * A standard Tcl result 71 | * 72 | *---------------------------------------------------------------------- 73 | */ 74 | int casstcl_createBatchObjectCommand (casstcl_sessionClientData *ct, char *commandName, CassBatchType cassBatchType); 75 | 76 | /* 77 | *---------------------------------------------------------------------- 78 | * 79 | * casstcl_batchObjectObjCmd -- 80 | * 81 | * dispatches the subcommands of a casstcl batch-handling command 82 | * 83 | * Results: 84 | * stuff 85 | * 86 | *---------------------------------------------------------------------- 87 | */ 88 | int casstcl_batchObjectObjCmd(ClientData cData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); 89 | 90 | /* 91 | *-------------------------------------------------------------- 92 | * 93 | * casstcl_batchObjectDelete -- command deletion callback routine. 94 | * 95 | * Results: 96 | * ...destroys the batch object. 97 | * ...frees memory. 98 | * 99 | * Side effects: 100 | * None. 101 | * 102 | *-------------------------------------------------------------- 103 | */ 104 | void casstcl_batchObjectDelete (ClientData clientData); 105 | 106 | /* vim: set ts=4 sw=4 sts=4 noet : */ 107 | -------------------------------------------------------------------------------- /generic/casstcl_cassandra.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_cassandra 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *-------------------------------------------------------------- 13 | * 14 | * casstcl_cassObjectDelete -- command deletion callback routine. 15 | * 16 | * Results: 17 | * ...destroys the cass connection handle. 18 | * ...frees memory. 19 | * 20 | * Side effects: 21 | * None. 22 | * 23 | *-------------------------------------------------------------- 24 | */ 25 | void casstcl_cassObjectDelete (ClientData clientData); 26 | 27 | 28 | /* 29 | *-------------------------------------------------------------- 30 | * 31 | * casstcl_invoke_callback_with_argument -- 32 | * 33 | * The twist here is that a callback object might be a list, not 34 | * just a command name, like the argument to -callback might be 35 | * more than just a function name, like it could be an object name 36 | * and a method name and an argument or whatever. 37 | * 38 | * This code splits out that list and generates up an eval thingie 39 | * and invokes it with the additional argument tacked onto the end, 40 | * a future object or the like. 41 | * 42 | * Results: 43 | * 44 | * Side effects: 45 | * None. 46 | * 47 | *-------------------------------------------------------------- 48 | */ 49 | int casstcl_invoke_callback_with_argument (Tcl_Interp *interp, Tcl_Obj *callbackObj, Tcl_Obj *argumentObj); 50 | 51 | 52 | /* 53 | *---------------------------------------------------------------------- 54 | * 55 | * casstcl_make_upsert_statement_from_objv -- 56 | * 57 | * This takes an objv and objc containing possible arguments such 58 | * as -mapunknown, -nocomplain and -ifnotexists and in the objv 59 | * always a fully qualified table name and a list of key-value pairs 60 | * 61 | * It creates a cass statement and if successful sets the caller's 62 | * pointer to a pointer to a cass statement to that statement 63 | * 64 | * It returns a standard Tcl result, TCL_ERROR if something went 65 | * wrong and you don't get a statement 66 | * 67 | * It returns TCL_OK if all went well 68 | * 69 | * This uses casstcl_make_upsert_statement to make the statement after 70 | * it figures the arguments thereto 71 | * 72 | * Results: 73 | * A standard Tcl result. 74 | * 75 | *---------------------------------------------------------------------- 76 | */ 77 | int casstcl_make_upsert_statement_from_objv ( 78 | casstcl_sessionClientData *ct, 79 | int objc, 80 | Tcl_Obj *CONST objv[], 81 | CassConsistency *consistencyPtr, 82 | CassStatement **statementPtr); 83 | 84 | /* 85 | *---------------------------------------------------------------------- 86 | * 87 | * casstcl_cassObjCmd -- 88 | * 89 | * Create a cass object... 90 | * 91 | * cass create my_cass 92 | * cass create #auto 93 | * 94 | * The created object is invoked to do things with a CassDB 95 | * 96 | * Results: 97 | * A standard Tcl result. 98 | * 99 | * 100 | *---------------------------------------------------------------------- 101 | */ 102 | int casstcl_cassObjCmd( 103 | ClientData clientData, 104 | Tcl_Interp *interp, 105 | int objc, 106 | Tcl_Obj *CONST objv[]); 107 | 108 | 109 | /* 110 | *---------------------------------------------------------------------- 111 | * 112 | * casstcl_make_upsert_statement -- 113 | * 114 | * given a session client data, fully qualified table name, Tcl list 115 | * obj and a pointer to a statement pointer, 116 | * 117 | * this baby... 118 | * 119 | * ...generates an upsert statement (in the form of insert but that's 120 | * how cassandra rolls) 121 | * 122 | * ...uses casstcl_bind_names_from_list to bind the data elements 123 | * to the statement using the right data types 124 | * 125 | * It creates a cassandra statement and sets your pointer to it 126 | * 127 | * Results: 128 | * A standard Tcl result. 129 | * 130 | *---------------------------------------------------------------------- 131 | */ 132 | int casstcl_make_upsert_statement ( 133 | casstcl_sessionClientData *ct, 134 | char *tableName, Tcl_Obj *listObj, 135 | CassConsistency *consistencyPtr, 136 | CassStatement **statementPtr, 137 | char *mapUnknown, 138 | int dropUnknown, 139 | int ifNotExists); 140 | 141 | /* 142 | *---------------------------------------------------------------------- 143 | * 144 | * casstcl_make_statement_from_objv -- 145 | * 146 | * This is a beautiful thing because it will work from the like four 147 | * places where we generate statements: exec, select, async, and 148 | * batch. 149 | * 150 | * This is like what would be in the option-handling case statements. 151 | * 152 | * We will look at the objc and objv we are given with what's in front 153 | * of the command that got invoked stripped off, that is for example 154 | * if the command was 155 | * 156 | * $batch add -array row $query column column column 157 | * 158 | * $batch add $query $value $type $value $type 159 | * 160 | * ...we expect to get it from "-array" on, that is, they'll pass us 161 | * the address of the objv starting from there and the objc properly 162 | * discounting whatever preceded the stuff we handle 163 | * 164 | * We then figure it out and invoke the underlying stuff. 165 | * 166 | * Results: 167 | * A standard Tcl result. 168 | * 169 | *---------------------------------------------------------------------- 170 | */ 171 | int casstcl_make_statement_from_objv ( 172 | casstcl_sessionClientData *ct, 173 | int objc, Tcl_Obj *CONST objv[], 174 | int argOffset, 175 | CassStatement **statementPtr); 176 | 177 | /* vim: set ts=4 sw=4 sts=4 noet : */ 178 | -------------------------------------------------------------------------------- /generic/casstcl_consistency.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_consistency - Functions for setting and managing cassandra consistency 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_error.h" 13 | #include "casstcl_consistency.h" 14 | 15 | /* 16 | *-------------------------------------------------------------- 17 | * 18 | * casstcl_setStatementConsistency -- Setup the consistency 19 | * level for the specified statement, if necessary. Special 20 | * handling is automatically used for serial consistency 21 | * levels. 22 | * 23 | * Results: 24 | * A standard Tcl result. 25 | * 26 | * Side effects: 27 | * The statement will be freed upon error. 28 | * 29 | *-------------------------------------------------------------- 30 | */ 31 | int 32 | casstcl_setStatementConsistency (casstcl_sessionClientData *ct, CassStatement *statementPtr, CassConsistency *consistencyPtr) 33 | { 34 | if (consistencyPtr != NULL) { 35 | CassConsistency consistency = *consistencyPtr; 36 | CassError consistencyErr; 37 | if (consistency == CASS_CONSISTENCY_SERIAL || 38 | consistency == CASS_CONSISTENCY_LOCAL_SERIAL) { 39 | consistencyErr = cass_statement_set_serial_consistency(statementPtr, consistency); 40 | } else { 41 | consistencyErr = cass_statement_set_consistency(statementPtr, consistency); 42 | } 43 | if (consistencyErr != CASS_OK) { 44 | cass_statement_free(statementPtr); 45 | return casstcl_cass_error_to_tcl (ct, consistencyErr); 46 | } 47 | } 48 | return TCL_OK; 49 | } 50 | 51 | 52 | /* 53 | *-------------------------------------------------------------- 54 | * 55 | * casstcl_obj_to_cass_consistency -- lookup a string in a Tcl object 56 | * to be one of the consistency strings for CassConsistency and set 57 | * a pointer to a passed-in CassConsistency value to the corresponding 58 | * CassConsistency such as CASS_CONSISTENCY_ANY, etc 59 | * 60 | * Results: 61 | * ...cass constinency gets set 62 | * ...a standard Tcl result is returned 63 | * 64 | * Side effects: 65 | * None. 66 | * 67 | *-------------------------------------------------------------- 68 | */ 69 | int 70 | casstcl_obj_to_cass_consistency(casstcl_sessionClientData *ct, Tcl_Obj *tclObj, CassConsistency *cassConsistency) { 71 | int conIndex; 72 | 73 | static CONST char *consistencies[] = { 74 | "any", 75 | "one", 76 | "two", 77 | "three", 78 | "quorum", 79 | "all", 80 | "local_quorum", 81 | "each_quorum", 82 | "serial", 83 | "local_serial", 84 | "local_one", 85 | NULL 86 | }; 87 | 88 | enum consistencies { 89 | OPT_ANY, 90 | OPT_ONE, 91 | OPT_TWO, 92 | OPT_THREE, 93 | OPT_QUORUM, 94 | OPT_ALL, 95 | OPT_LOCAL_QUORUM, 96 | OPT_EACH_QUORUM, 97 | OPT_SERIAL, 98 | OPT_LOCAL_SERIAL, 99 | OPT_LOCAL_ONE 100 | }; 101 | 102 | // argument must be one of the subOptions defined above 103 | if (Tcl_GetIndexFromObj (ct->interp, tclObj, consistencies, "consistency", 104 | TCL_EXACT, &conIndex) != TCL_OK) { 105 | return TCL_ERROR; 106 | } 107 | 108 | switch ((enum consistencies) conIndex) { 109 | case OPT_ANY: { 110 | *cassConsistency = CASS_CONSISTENCY_ANY; 111 | break; 112 | } 113 | 114 | case OPT_ONE: { 115 | *cassConsistency = CASS_CONSISTENCY_ONE; 116 | break; 117 | } 118 | 119 | case OPT_TWO: { 120 | *cassConsistency = CASS_CONSISTENCY_TWO; 121 | break; 122 | } 123 | 124 | case OPT_THREE: { 125 | *cassConsistency = CASS_CONSISTENCY_THREE; 126 | break; 127 | } 128 | 129 | case OPT_QUORUM: { 130 | *cassConsistency = CASS_CONSISTENCY_QUORUM; 131 | break; 132 | } 133 | 134 | case OPT_ALL: { 135 | *cassConsistency = CASS_CONSISTENCY_ALL; 136 | break; 137 | } 138 | 139 | case OPT_LOCAL_QUORUM: { 140 | *cassConsistency = CASS_CONSISTENCY_LOCAL_QUORUM; 141 | break; 142 | } 143 | 144 | case OPT_EACH_QUORUM: { 145 | *cassConsistency = CASS_CONSISTENCY_EACH_QUORUM; 146 | break; 147 | } 148 | 149 | case OPT_SERIAL: { 150 | *cassConsistency = CASS_CONSISTENCY_SERIAL; 151 | break; 152 | } 153 | 154 | case OPT_LOCAL_SERIAL: { 155 | *cassConsistency = CASS_CONSISTENCY_LOCAL_SERIAL; 156 | break; 157 | } 158 | 159 | case OPT_LOCAL_ONE: { 160 | *cassConsistency = CASS_CONSISTENCY_LOCAL_ONE; 161 | break; 162 | } 163 | } 164 | 165 | return TCL_OK; 166 | } 167 | 168 | /* 169 | *-------------------------------------------------------------- 170 | * 171 | * casstcl_cass_consistency_to_string -- given a CassConsistency, 172 | * return a const char * to a character string of equivalent 173 | * meaning 174 | * 175 | * Results: 176 | * a string gets returned 177 | * 178 | * Side effects: 179 | * None. 180 | * 181 | *-------------------------------------------------------------- 182 | */ 183 | const char * 184 | casstcl_cass_consistency_to_string (CassConsistency consistency) { 185 | switch (consistency) { 186 | case CASS_CONSISTENCY_ANY: { 187 | return "any"; 188 | } 189 | 190 | case CASS_CONSISTENCY_ONE: { 191 | return "one"; 192 | } 193 | 194 | case CASS_CONSISTENCY_TWO: { 195 | return "two"; 196 | } 197 | 198 | case CASS_CONSISTENCY_THREE: { 199 | return "three"; 200 | } 201 | 202 | case CASS_CONSISTENCY_QUORUM: { 203 | return "quorum"; 204 | } 205 | 206 | case CASS_CONSISTENCY_ALL: { 207 | return "all"; 208 | } 209 | 210 | case CASS_CONSISTENCY_LOCAL_QUORUM: { 211 | return "local_quorum"; 212 | } 213 | 214 | case CASS_CONSISTENCY_EACH_QUORUM: { 215 | return "each_quorum"; 216 | } 217 | 218 | case CASS_CONSISTENCY_SERIAL: { 219 | return "serial"; 220 | } 221 | 222 | case CASS_CONSISTENCY_LOCAL_SERIAL: { 223 | return "local_serial"; 224 | } 225 | 226 | case CASS_CONSISTENCY_LOCAL_ONE: { 227 | return "local_one"; 228 | } 229 | 230 | default: 231 | return "unknown"; 232 | } 233 | } 234 | 235 | /* vim: set ts=4 sw=4 sts=4 noet : */ 236 | -------------------------------------------------------------------------------- /generic/casstcl_consistency.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_consistency 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *-------------------------------------------------------------- 13 | * 14 | * casstcl_setStatementConsistency -- Setup the consistency 15 | * level for the specified statement, if necessary. Special 16 | * handling is automatically used for serial consistency 17 | * levels. 18 | * 19 | * Results: 20 | * A standard Tcl result. 21 | * 22 | * Side effects: 23 | * The statement will be freed upon error. 24 | * 25 | *-------------------------------------------------------------- 26 | */ 27 | int casstcl_setStatementConsistency (casstcl_sessionClientData *ct, CassStatement *statementPtr, CassConsistency *consistencyPtr); 28 | 29 | 30 | /* 31 | *-------------------------------------------------------------- 32 | * 33 | * casstcl_obj_to_cass_consistency -- lookup a string in a Tcl object 34 | * to be one of the consistency strings for CassConsistency and set 35 | * a pointer to a passed-in CassConsistency value to the corresponding 36 | * CassConsistency such as CASS_CONSISTENCY_ANY, etc 37 | * 38 | * Results: 39 | * ...cass constinency gets set 40 | * ...a standard Tcl result is returned 41 | * 42 | * Side effects: 43 | * None. 44 | * 45 | *-------------------------------------------------------------- 46 | */ 47 | int casstcl_obj_to_cass_consistency(casstcl_sessionClientData *ct, Tcl_Obj *tclObj, CassConsistency *cassConsistency); 48 | 49 | /* 50 | *-------------------------------------------------------------- 51 | * 52 | * casstcl_cass_consistency_to_string -- given a CassConsistency, 53 | * return a const char * to a character string of equivalent 54 | * meaning 55 | * 56 | * Results: 57 | * a string gets returned 58 | * 59 | * Side effects: 60 | * None. 61 | * 62 | *-------------------------------------------------------------- 63 | */ 64 | const char * casstcl_cass_consistency_to_string (CassConsistency consistency); 65 | 66 | 67 | /* vim: set ts=4 sw=4 sts=4 noet : */ 68 | -------------------------------------------------------------------------------- /generic/casstcl_error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_error - Functions used to interpret errors 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_error.h" 13 | 14 | /* 15 | *-------------------------------------------------------------- 16 | * 17 | * casstcl_cass_error_to_errorcode_string -- given a CassError 18 | * code return a string corresponding to the CassError constant 19 | * 20 | * Results: 21 | * returns a pointer to a const char * 22 | * 23 | * Side effects: 24 | * None. 25 | * 26 | *-------------------------------------------------------------- 27 | */ 28 | const char *casstcl_cass_error_to_errorcode_string (CassError cassError) 29 | { 30 | switch (cassError) { 31 | case CASS_OK: 32 | return "CASS_OK"; 33 | 34 | case CASS_ERROR_LIB_BAD_PARAMS: 35 | return "CASS_ERROR_LIB_BAD_PARAMS"; 36 | 37 | case CASS_ERROR_LIB_NO_STREAMS: 38 | return "CASS_ERROR_LIB_NO_STREAMS"; 39 | 40 | case CASS_ERROR_LIB_UNABLE_TO_INIT: 41 | return "CASS_ERROR_LIB_UNABLE_TO_INIT"; 42 | 43 | case CASS_ERROR_LIB_MESSAGE_ENCODE: 44 | return "CASS_ERROR_LIB_MESSAGE_ENCODE"; 45 | 46 | case CASS_ERROR_LIB_HOST_RESOLUTION: 47 | return "CASS_ERROR_LIB_HOST_RESOLUTION"; 48 | 49 | case CASS_ERROR_LIB_UNEXPECTED_RESPONSE: 50 | return "CASS_ERROR_LIB_UNEXPECTED_RESPONSE"; 51 | 52 | case CASS_ERROR_LIB_REQUEST_QUEUE_FULL: 53 | return "CASS_ERROR_LIB_REQUEST_QUEUE_FULL"; 54 | 55 | case CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD: 56 | return "CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD"; 57 | 58 | case CASS_ERROR_LIB_WRITE_ERROR: 59 | return "CASS_ERROR_LIB_WRITE_ERROR"; 60 | 61 | case CASS_ERROR_LIB_NO_HOSTS_AVAILABLE: 62 | return "CASS_ERROR_LIB_NO_HOSTS_AVAILABLE"; 63 | 64 | case CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS: 65 | return "CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS"; 66 | 67 | case CASS_ERROR_LIB_INVALID_ITEM_COUNT: 68 | return "CASS_ERROR_LIB_INVALID_ITEM_COUNT"; 69 | 70 | case CASS_ERROR_LIB_INVALID_VALUE_TYPE: 71 | return "CASS_ERROR_LIB_INVALID_VALUE_TYPE"; 72 | 73 | case CASS_ERROR_LIB_REQUEST_TIMED_OUT: 74 | return "CASS_ERROR_LIB_REQUEST_TIMED_OUT"; 75 | 76 | case CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE: 77 | return "CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE"; 78 | 79 | case CASS_ERROR_LIB_CALLBACK_ALREADY_SET: 80 | return "CASS_ERROR_LIB_CALLBACK_ALREADY_SET"; 81 | 82 | case CASS_ERROR_LIB_INVALID_STATEMENT_TYPE: 83 | return "CASS_ERROR_LIB_INVALID_STATEMENT_TYPE"; 84 | 85 | case CASS_ERROR_LIB_NAME_DOES_NOT_EXIST: 86 | return "CASS_ERROR_LIB_NAME_DOES_NOT_EXIST"; 87 | 88 | case CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL: 89 | return "CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL"; 90 | 91 | case CASS_ERROR_LIB_NULL_VALUE: 92 | return "CASS_ERROR_LIB_NULL_VALUE"; 93 | 94 | case CASS_ERROR_LIB_NOT_IMPLEMENTED: 95 | return "CASS_ERROR_LIB_NOT_IMPLEMENTED"; 96 | 97 | case CASS_ERROR_LIB_UNABLE_TO_CONNECT: 98 | return "CASS_ERROR_LIB_UNABLE_TO_CONNECT"; 99 | 100 | case CASS_ERROR_LIB_UNABLE_TO_CLOSE: 101 | return "CASS_ERROR_LIB_UNABLE_TO_CLOSE"; 102 | 103 | case CASS_ERROR_SERVER_SERVER_ERROR: 104 | return "CASS_ERROR_SERVER_SERVER_ERROR"; 105 | 106 | case CASS_ERROR_SERVER_PROTOCOL_ERROR: 107 | return "CASS_ERROR_SERVER_PROTOCOL_ERROR"; 108 | 109 | case CASS_ERROR_SERVER_BAD_CREDENTIALS: 110 | return "CASS_ERROR_SERVER_BAD_CREDENTIALS"; 111 | 112 | case CASS_ERROR_SERVER_UNAVAILABLE: 113 | return "CASS_ERROR_SERVER_UNAVAILABLE"; 114 | 115 | case CASS_ERROR_SERVER_OVERLOADED: 116 | return "CASS_ERROR_SERVER_OVERLOADED"; 117 | 118 | case CASS_ERROR_SERVER_IS_BOOTSTRAPPING: 119 | return "CASS_ERROR_SERVER_IS_BOOTSTRAPPING"; 120 | 121 | case CASS_ERROR_SERVER_TRUNCATE_ERROR: 122 | return "CASS_ERROR_SERVER_TRUNCATE_ERROR"; 123 | 124 | case CASS_ERROR_SERVER_WRITE_TIMEOUT: 125 | return "CASS_ERROR_SERVER_WRITE_TIMEOUT"; 126 | 127 | case CASS_ERROR_SERVER_READ_TIMEOUT: 128 | return "CASS_ERROR_SERVER_READ_TIMEOUT"; 129 | 130 | case CASS_ERROR_SERVER_SYNTAX_ERROR: 131 | return "CASS_ERROR_SERVER_SYNTAX_ERROR"; 132 | 133 | case CASS_ERROR_SERVER_UNAUTHORIZED: 134 | return "CASS_ERROR_SERVER_UNAUTHORIZED"; 135 | 136 | case CASS_ERROR_SERVER_INVALID_QUERY: 137 | return "CASS_ERROR_SERVER_INVALID_QUERY"; 138 | 139 | case CASS_ERROR_SERVER_CONFIG_ERROR: 140 | return "CASS_ERROR_SERVER_CONFIG_ERROR"; 141 | 142 | case CASS_ERROR_SERVER_ALREADY_EXISTS: 143 | return "CASS_ERROR_SERVER_ALREADY_EXISTS"; 144 | 145 | case CASS_ERROR_SERVER_UNPREPARED: 146 | return "CASS_ERROR_SERVER_UNPREPARED"; 147 | 148 | case CASS_ERROR_SSL_INVALID_CERT: 149 | return "CASS_ERROR_SSL_INVALID_CERT"; 150 | 151 | case CASS_ERROR_SSL_INVALID_PRIVATE_KEY: 152 | return "CASS_ERROR_SSL_INVALID_PRIVATE_KEY"; 153 | 154 | case CASS_ERROR_SSL_NO_PEER_CERT: 155 | return "CASS_ERROR_SSL_NO_PEER_CERT"; 156 | 157 | case CASS_ERROR_SSL_INVALID_PEER_CERT: 158 | return "CASS_ERROR_SSL_INVALID_PEER_CERT"; 159 | 160 | case CASS_ERROR_SSL_IDENTITY_MISMATCH: 161 | return "CASS_ERROR_SSL_IDENTITY_MISMATCH"; 162 | 163 | case CASS_ERROR_LAST_ENTRY: 164 | return "CASS_ERROR_LAST_ENTRY"; 165 | 166 | default: 167 | return "CASS_ERROR_UNRECOGNIZED_ERROR"; 168 | } 169 | return NULL; 170 | } 171 | 172 | 173 | /* 174 | *-------------------------------------------------------------- 175 | * 176 | * casstcl_cass_error_to_tcl -- given a CassError code and a field 177 | * name, if the error code is CASS_OK return TCL_OK but if it's anything 178 | * else, set the interpreter result to the corresponding error string 179 | * and set the error code to CASSANDRA and the e-code like 180 | * CASS_ERROR_LIB_BAD_PARAMS 181 | * 182 | * Results: 183 | * A standard Tcl result 184 | * 185 | * Side effects: 186 | * None. 187 | * 188 | *-------------------------------------------------------------- 189 | */ 190 | int casstcl_cass_error_to_tcl (casstcl_sessionClientData *ct, CassError cassError) { 191 | 192 | if (cassError == CASS_OK) { 193 | return TCL_OK; 194 | } 195 | 196 | const char *cassErrorCodeString = casstcl_cass_error_to_errorcode_string (cassError); 197 | const char *cassErrorDesc = cass_error_desc (cassError); 198 | Tcl_ResetResult (ct->interp); 199 | Tcl_SetErrorCode (ct->interp, "CASSANDRA", cassErrorCodeString, cassErrorDesc, NULL); 200 | Tcl_AppendResult (ct->interp, "cassandra error: ", cassErrorDesc, NULL); 201 | return TCL_ERROR; 202 | } 203 | 204 | 205 | /* 206 | *-------------------------------------------------------------- 207 | * 208 | * casstcl_future_error_to_tcl -- given a CassError code and a future, 209 | * if the error code is CASS_OK return TCL_OK but if it's anything 210 | * else, set the interpreter result to the corresponding error message 211 | * from the future object and set the error code to CASSANDRA and the 212 | * e-code like CASS_ERROR_LIB_BAD_PARAMS 213 | * 214 | * Results: 215 | * A standard Tcl result 216 | * 217 | * Side effects: 218 | * None. 219 | * 220 | *-------------------------------------------------------------- 221 | */ 222 | int casstcl_future_error_to_tcl (casstcl_sessionClientData *ct, CassError cassError, CassFuture *future) { 223 | 224 | if (cassError == CASS_OK) { 225 | return TCL_OK; 226 | } 227 | 228 | const char *cassErrorCodeString = casstcl_cass_error_to_errorcode_string (cassError); 229 | const char *cassErrorDesc = cass_error_desc (cassError); 230 | CassString message; 231 | cass_future_error_message (future, &message.data, &message.length); 232 | 233 | Tcl_ResetResult (ct->interp); 234 | Tcl_SetErrorCode (ct->interp, "CASSANDRA", cassErrorCodeString, cassErrorDesc, message.data, NULL); 235 | Tcl_AppendResult (ct->interp, "cassandra error: ", cassErrorDesc, ": ", message.data, NULL); 236 | return TCL_ERROR; 237 | } 238 | 239 | /* vim: set ts=4 sw=4 sts=4 noet : */ 240 | -------------------------------------------------------------------------------- /generic/casstcl_error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_error 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *-------------------------------------------------------------- 13 | * 14 | * casstcl_cass_error_to_errorcode_string -- given a CassError 15 | * code return a string corresponding to the CassError constant 16 | * 17 | * Results: 18 | * returns a pointer to a const char * 19 | * 20 | * Side effects: 21 | * None. 22 | * 23 | *-------------------------------------------------------------- 24 | */ 25 | const char *casstcl_cass_error_to_errorcode_string (CassError cassError); 26 | 27 | 28 | /* 29 | *-------------------------------------------------------------- 30 | * 31 | * casstcl_cass_error_to_tcl -- given a CassError code and a field 32 | * name, if the error code is CASS_OK return TCL_OK but if it's anything 33 | * else, set the interpreter result to the corresponding error string 34 | * and set the error code to CASSANDRA and the e-code like 35 | * CASS_ERROR_LIB_BAD_PARAMS 36 | * 37 | * Results: 38 | * A standard Tcl result 39 | * 40 | * Side effects: 41 | * None. 42 | * 43 | *-------------------------------------------------------------- 44 | */ 45 | int casstcl_cass_error_to_tcl (casstcl_sessionClientData *ct, CassError cassError); 46 | 47 | /* 48 | *-------------------------------------------------------------- 49 | * 50 | * casstcl_future_error_to_tcl -- given a CassError code and a future, 51 | * if the error code is CASS_OK return TCL_OK but if it's anything 52 | * else, set the interpreter result to the corresponding error message 53 | * from the future object and set the error code to CASSANDRA and the 54 | * e-code like CASS_ERROR_LIB_BAD_PARAMS 55 | * 56 | * Results: 57 | * A standard Tcl result 58 | * 59 | * Side effects: 60 | * None. 61 | * 62 | *-------------------------------------------------------------- 63 | */ 64 | int casstcl_future_error_to_tcl (casstcl_sessionClientData *ct, CassError cassError, CassFuture *future); 65 | 66 | /* vim: set ts=4 sw=4 sts=4 noet : */ 67 | -------------------------------------------------------------------------------- /generic/casstcl_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_event - Functions used to log and handle events 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_log.h" 13 | #include "casstcl_event.h" 14 | #include "casstcl_cassandra.h" 15 | /* 16 | *---------------------------------------------------------------------- 17 | * 18 | * casstcl_logging_eventProc -- 19 | * 20 | * this routine is called by the Tcl event handler to process callbacks 21 | * we have set up from logging callbacks we've gotten from Cassandra 22 | * loop is 23 | * 24 | * Results: 25 | * returns 1 to say we handled the event and the dispatcher can delete it 26 | * 27 | *---------------------------------------------------------------------- 28 | */ 29 | int 30 | casstcl_logging_eventProc (Tcl_Event *tevPtr, int flags) { 31 | 32 | // we got called with a Tcl_Event pointer but really it's a pointer to 33 | // our casstcl_loggingEvent structure that has the Tcl_Event plus a pointer 34 | // to casstcl_sessionClientData, which is our key to the kindgdom. 35 | // Go get that. 36 | 37 | casstcl_loggingEvent *evPtr = (casstcl_loggingEvent *)tevPtr; 38 | Tcl_Interp *interp = evPtr->interp; 39 | 40 | #define CASSTCL_LOG_CALLBACK_LISTCOUNT 12 41 | 42 | Tcl_Obj *listObjv[CASSTCL_LOG_CALLBACK_LISTCOUNT]; 43 | 44 | // probably won't happen but if we get a logging callback and have 45 | // no callback object, return 1 saying we handled it and let the 46 | // dispatcher delete the message NB this isn't exactly cool 47 | if (casstcl_loggingCallbackObj == NULL) { 48 | return 1; 49 | } 50 | 51 | // construct a list of key-value pairs representing the log message 52 | 53 | listObjv[0] = Tcl_NewStringObj ("clock", -1); 54 | listObjv[1] = Tcl_NewDoubleObj (evPtr->message.time_ms / 1000.0); 55 | 56 | listObjv[2] = Tcl_NewStringObj ("severity", -1); 57 | listObjv[3] = Tcl_NewStringObj (casstcl_cass_log_level_to_string (evPtr->message.severity), -1); 58 | 59 | listObjv[4] = Tcl_NewStringObj ("file", -1); 60 | listObjv[5] = Tcl_NewStringObj (evPtr->message.file, -1); 61 | 62 | listObjv[6] = Tcl_NewStringObj ("line", -1); 63 | listObjv[7] = Tcl_NewIntObj (evPtr->message.line); 64 | 65 | listObjv[8] = Tcl_NewStringObj ("function", -1); 66 | listObjv[9] = Tcl_NewStringObj (evPtr->message.function, -1); 67 | 68 | listObjv[10] = Tcl_NewStringObj ("message", -1); 69 | int messageLength = strnlen (evPtr->message.message, CASS_LOG_MAX_MESSAGE_SIZE); 70 | listObjv[11] = Tcl_NewStringObj (evPtr->message.message, messageLength); 71 | 72 | Tcl_Obj *listObj = Tcl_NewListObj (CASSTCL_LOG_CALLBACK_LISTCOUNT, listObjv); 73 | 74 | // even if this fails we still want the event taken off the queue 75 | // this function will do the background error thing if there is a tcl 76 | // error running the callback 77 | casstcl_invoke_callback_with_argument (interp, casstcl_loggingCallbackObj, listObj); 78 | 79 | // tell the dispatcher we handled it. 0 would mean we didn't deal with 80 | // it and don't want it removed from the queue 81 | return 1; 82 | } 83 | 84 | /* 85 | *---------------------------------------------------------------------- 86 | * 87 | * casstcl_logging_callback -- 88 | * 89 | * this routine is called by the cassandra cpp-driver as a callback 90 | * when a log message has been received and cass_log_set_callback 91 | * has been done to register this callback 92 | * 93 | * Results: 94 | * an event is queued to the thread that started our conversation with 95 | * cassandra 96 | * 97 | *---------------------------------------------------------------------- 98 | */ 99 | void casstcl_logging_callback (const CassLogMessage *message, void *data) { 100 | casstcl_loggingEvent *evPtr; 101 | 102 | Tcl_Interp *interp = data; 103 | evPtr = (casstcl_loggingEvent *)ckalloc (sizeof(casstcl_loggingEvent)); 104 | evPtr->event.proc = casstcl_logging_eventProc; 105 | evPtr->interp = interp; 106 | evPtr->message = *message; /* structure copy */ 107 | Tcl_ThreadQueueEvent(casstcl_loggingCallbackThreadId, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL); 108 | } 109 | 110 | 111 | /* 112 | *---------------------------------------------------------------------- 113 | * 114 | * casstcl_EventSetupProc -- 115 | * This routine is a required argument to Tcl_CreateEventSource 116 | * 117 | * Normally here an extension that generates events does something 118 | * to make sure the application wakes up when events of the desired 119 | * type occur. 120 | * 121 | * We don't need to do anything here because we generate Tcl events 122 | * onto the originating thread via the callbacks invoked from the 123 | * Cassandra cpp-driver library and that's (apparently) all Tcl 124 | * needs to do its thing. 125 | * 126 | * Results: 127 | * The program compiles. 128 | * 129 | *---------------------------------------------------------------------- 130 | */ 131 | void 132 | casstcl_EventSetupProc (ClientData data, int flags) 133 | { 134 | } 135 | 136 | /* 137 | *---------------------------------------------------------------------- 138 | * 139 | * casstcl_EventCheckProc -- 140 | * 141 | * Normally here an extension that generates events would look at its 142 | * tables or whatnot to see what needs to be generated as an event. 143 | * 144 | * We don't need to do that because we generate Tcl events 145 | * onto the originating thread via the callbacks invoked from the 146 | * Cassandra cpp-driver library, so we handle it that way. 147 | * 148 | * Results: 149 | * The program compiles. 150 | * 151 | *---------------------------------------------------------------------- 152 | */ 153 | void 154 | casstcl_EventCheckProc (ClientData data, int flags) 155 | { 156 | } 157 | 158 | /* vim: set ts=4 sw=4 sts=4 noet : */ 159 | 160 | 161 | -------------------------------------------------------------------------------- /generic/casstcl_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_event 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *---------------------------------------------------------------------- 13 | * 14 | * casstcl_logging_eventProc -- 15 | * 16 | * this routine is called by the Tcl event handler to process callbacks 17 | * we have set up from logging callbacks we've gotten from Cassandra 18 | * loop is 19 | * 20 | * Results: 21 | * returns 1 to say we handled the event and the dispatcher can delete it 22 | * 23 | *---------------------------------------------------------------------- 24 | */ 25 | int 26 | casstcl_logging_eventProc (Tcl_Event *tevPtr, int flags); 27 | 28 | /* 29 | *---------------------------------------------------------------------- 30 | * 31 | * casstcl_logging_callback -- 32 | * 33 | * this routine is called by the cassandra cpp-driver as a callback 34 | * when a log message has been received and cass_log_set_callback 35 | * has been done to register this callback 36 | * 37 | * Results: 38 | * an event is queued to the thread that started our conversation with 39 | * cassandra 40 | * 41 | *---------------------------------------------------------------------- 42 | */ 43 | void casstcl_logging_callback (const CassLogMessage *message, void *data); 44 | 45 | 46 | /* 47 | *---------------------------------------------------------------------- 48 | * 49 | * casstcl_EventSetupProc -- 50 | * This routine is a required argument to Tcl_CreateEventSource 51 | * 52 | * Normally here an extension that generates events does something 53 | * to make sure the application wakes up when events of the desired 54 | * type occur. 55 | * 56 | * We don't need to do anything here because we generate Tcl events 57 | * onto the originating thread via the callbacks invoked from the 58 | * Cassandra cpp-driver library and that's (apparently) all Tcl 59 | * needs to do its thing. 60 | * 61 | * Results: 62 | * The program compiles. 63 | * 64 | *---------------------------------------------------------------------- 65 | */ 66 | void casstcl_EventSetupProc (ClientData data, int flags); 67 | 68 | /* 69 | *---------------------------------------------------------------------- 70 | * 71 | * casstcl_EventCheckProc -- 72 | * 73 | * Normally here an extension that generates events would look at its 74 | * tables or whatnot to see what needs to be generated as an event. 75 | * 76 | * We don't need to do that because we generate Tcl events 77 | * onto the originating thread via the callbacks invoked from the 78 | * Cassandra cpp-driver library, so we handle it that way. 79 | * 80 | * Results: 81 | * The program compiles. 82 | * 83 | *---------------------------------------------------------------------- 84 | */ 85 | void casstcl_EventCheckProc (ClientData data, int flags); 86 | 87 | /* vim: set ts=4 sw=4 sts=4 noet : */ 88 | -------------------------------------------------------------------------------- /generic/casstcl_future.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_futures - Functions used to create, delete, and handle futures 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_cassandra.h" 13 | #include "casstcl_future.h" 14 | #include "casstcl_error.h" 15 | #include "casstcl_event.h" 16 | 17 | #include 18 | 19 | /* 20 | *---------------------------------------------------------------------- 21 | * 22 | * casstcl_future_eventProc -- 23 | * 24 | * this routine is called by the Tcl event handler to process callbacks 25 | * we have set up from future (result objects) we've gotten from Cassandra 26 | * 27 | * Results: 28 | * The callback routine set when the async method was invoked is 29 | * invoked in the Tcl interpreter with one argument, that being the 30 | * future object that was also created when the async method was 31 | * invoked 32 | * 33 | * If an uncaught error occurs when evaluating the command, a Tcl 34 | * background exception is invoked 35 | * 36 | *---------------------------------------------------------------------- 37 | */ 38 | int 39 | casstcl_future_eventProc (Tcl_Event *tevPtr, int flags) { 40 | 41 | // we got called with a Tcl_Event pointer but really it's a pointer to 42 | // our casstcl_futureEvent structure that has the Tcl_Event plus a pointer 43 | // to casstcl_futureClientData, which is our key to the kindgdom. 44 | // Go get that. 45 | 46 | casstcl_futureEvent *evPtr = (casstcl_futureEvent *)tevPtr; 47 | casstcl_futureClientData *fcd = evPtr->fcd; 48 | assert (fcd->cass_future_magic == CASS_FUTURE_MAGIC); 49 | Tcl_Interp *interp = fcd->ct->interp; 50 | 51 | // eval the command. it should be the callback we were told as the 52 | // first argument and the future object we created, like future0, as 53 | // the second. 54 | 55 | CassError rc = cass_future_error_code(fcd->future); 56 | 57 | // Callback if we have an error OR if CASSTCL_FUTURE_CALLBACK_ON_ERROR_ONLY not set 58 | if ( ((fcd->flags & CASSTCL_FUTURE_CALLBACK_ON_ERROR_ONLY) != CASSTCL_FUTURE_CALLBACK_ON_ERROR_ONLY ) || 59 | (casstcl_future_error_to_tcl(fcd->ct, rc, fcd->future) == TCL_ERROR ) ) { 60 | // get the name of the future object this callback is related to 61 | // into an object so that we can pass it as an argument 62 | 63 | Tcl_Obj *futureObj = Tcl_NewObj(); 64 | Tcl_GetCommandFullName(interp, fcd->cmdToken, futureObj); 65 | 66 | casstcl_invoke_callback_with_argument (interp, fcd->callbackObj, futureObj); 67 | 68 | } else { 69 | 70 | Tcl_DeleteCommandFromToken (interp, fcd->cmdToken); 71 | } 72 | 73 | // tell the dispatcher we handled it. 0 would mean we didn't deal with 74 | // it and don't want it removed from the queue 75 | return 1; 76 | } 77 | 78 | /* 79 | *---------------------------------------------------------------------- 80 | * 81 | * casstcl_future_callback -- 82 | * 83 | * this routine is called by the cassandra cpp-driver as a callback 84 | * when a callback was set up by us using cass_future_set_callback 85 | * 86 | * this occurs when the request has completed or errored 87 | * 88 | * this generates a Tcl event and queues it to the thread that issued 89 | * the command to do an asynchronous cassandra command with callback 90 | * in the first place. 91 | * 92 | * when Tcl processes the event, casstcl_future_eventProc will be invoked. 93 | * that guy will do a Tcl eval to invoke the callback 94 | * 95 | * Results: 96 | * stuff 97 | * 98 | *---------------------------------------------------------------------- 99 | */ 100 | void casstcl_future_callback (CassFuture* future, void* data) { 101 | casstcl_futureEvent *evPtr; 102 | 103 | casstcl_futureClientData *fcd = data; 104 | evPtr = (casstcl_futureEvent *) ckalloc (sizeof (casstcl_futureEvent)); 105 | evPtr->event.proc = casstcl_future_eventProc; 106 | evPtr->fcd = fcd; 107 | int queueEnd = ((fcd->flags & CASSTCL_FUTURE_QUEUE_HEAD_FLAG) == CASSTCL_FUTURE_QUEUE_HEAD_FLAG) ? 108 | TCL_QUEUE_HEAD : TCL_QUEUE_TAIL; 109 | Tcl_ThreadQueueEvent(fcd->ct->threadId, (Tcl_Event *)evPtr, queueEnd); 110 | } 111 | 112 | /* 113 | *---------------------------------------------------------------------- 114 | * 115 | * casstcl_createFutureObjectCommand -- 116 | * 117 | * given a casstcl_sessionClientData pointer, a pointer to a 118 | * CassFuture structure and a Tcl callback object (containing a 119 | * function name), this routine creates a Tcl command like 120 | * "future17" that can be invoked with method arguments to access, 121 | * manipulate and destroy cassandra future objects. 122 | * 123 | * Results: 124 | * A standard Tcl result 125 | * 126 | *---------------------------------------------------------------------- 127 | */ 128 | int 129 | casstcl_createFutureObjectCommand (casstcl_sessionClientData *ct, CassFuture *future, Tcl_Obj *callbackObj, int flags) 130 | { 131 | // allocate one of our cass future objects for Tcl and configure it 132 | casstcl_futureClientData *fcd; 133 | 134 | CassError rc = cass_future_error_code (future); 135 | if (rc != CASS_OK) { 136 | casstcl_future_error_to_tcl (ct, rc, future); 137 | cass_future_free (future); 138 | return TCL_ERROR; 139 | } 140 | 141 | fcd = (casstcl_futureClientData *)ckalloc (sizeof (casstcl_futureClientData)); 142 | fcd->cass_future_magic = CASS_FUTURE_MAGIC; 143 | fcd->ct = ct; 144 | fcd->future = future; 145 | fcd->flags = flags; 146 | Tcl_Interp *interp = ct->interp; 147 | 148 | if (callbackObj != NULL) { 149 | Tcl_IncrRefCount(callbackObj); 150 | } 151 | fcd->callbackObj = callbackObj; 152 | 153 | if (callbackObj != NULL) { 154 | cass_future_set_callback (future, casstcl_future_callback, fcd); 155 | } 156 | 157 | static unsigned long nextAutoCounter = 0; 158 | char *commandName; 159 | int baseNameLength; 160 | 161 | #define FUTURESTRING "future" 162 | baseNameLength = sizeof(FUTURESTRING) + 8 * sizeof(nextAutoCounter) + 1; // Allocate way more space than needed 163 | commandName = ckalloc (baseNameLength); 164 | snprintf (commandName, baseNameLength, "%s%lu", FUTURESTRING, nextAutoCounter++); 165 | 166 | // create a Tcl command to interface to cass 167 | fcd->cmdToken = Tcl_CreateObjCommand (interp, commandName, casstcl_futureObjectObjCmd, fcd, casstcl_futureObjectDelete); 168 | Tcl_SetObjResult (interp, Tcl_NewStringObj (commandName, -1)); 169 | ckfree(commandName); 170 | return TCL_OK; 171 | } 172 | 173 | 174 | /* 175 | *---------------------------------------------------------------------- 176 | * 177 | * casstcl_futureObjectObjCmd -- 178 | * 179 | * dispatches the subcommands of a casstcl future-handling command 180 | * 181 | * Results: 182 | * stuff 183 | * 184 | *---------------------------------------------------------------------- 185 | */ 186 | int 187 | casstcl_futureObjectObjCmd(ClientData cData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 188 | { 189 | int optIndex; 190 | casstcl_futureClientData *fcd = (casstcl_futureClientData *)cData; 191 | int resultCode = TCL_OK; 192 | 193 | static CONST char *options[] = { 194 | "isready", 195 | "wait", 196 | "foreach", 197 | "status", 198 | "error_message", 199 | "delete", 200 | NULL 201 | }; 202 | 203 | enum options { 204 | OPT_ISREADY, 205 | OPT_WAIT, 206 | OPT_FOREACH, 207 | OPT_STATUS, 208 | OPT_ERRORMESSAGE, 209 | OPT_DELETE 210 | }; 211 | 212 | /* basic validation of command line arguments */ 213 | if (objc < 2) { 214 | Tcl_WrongNumArgs (interp, 1, objv, "subcommand ?args?"); 215 | return TCL_ERROR; 216 | } 217 | 218 | if (Tcl_GetIndexFromObj (interp, objv[1], options, "option", TCL_EXACT, &optIndex) != TCL_OK) { 219 | return TCL_ERROR; 220 | } 221 | 222 | switch ((enum options) optIndex) { 223 | case OPT_ISREADY: { 224 | Tcl_SetBooleanObj (Tcl_GetObjResult(interp), cass_future_ready (fcd->future)); 225 | break; 226 | } 227 | 228 | case OPT_WAIT: { 229 | int microSeconds = 0; 230 | 231 | if (objc > 3) { 232 | Tcl_WrongNumArgs (interp, 2, objv, "?us?"); 233 | return TCL_ERROR; 234 | } 235 | 236 | if (objc == 3) { 237 | if (Tcl_GetIntFromObj (interp, objv[2], µSeconds) == TCL_ERROR) { 238 | Tcl_AppendResult (interp, " while converting microseconds element", NULL); 239 | return TCL_ERROR; 240 | } 241 | Tcl_SetBooleanObj (Tcl_GetObjResult(interp), cass_future_wait_timed (fcd->future, microSeconds)); 242 | } else { 243 | cass_future_wait (fcd->future); 244 | } 245 | break; 246 | } 247 | 248 | case OPT_FOREACH: { 249 | if (objc != 4) { 250 | Tcl_WrongNumArgs (interp, 2, objv, "rowArray codeBody"); 251 | return TCL_ERROR; 252 | } 253 | 254 | char *arrayName = Tcl_GetString (objv[2]); 255 | Tcl_Obj *codeObj = objv[3]; 256 | 257 | resultCode = casstcl_iterate_over_future (fcd->ct, fcd->future, arrayName, codeObj); 258 | break; 259 | } 260 | 261 | case OPT_DELETE: { 262 | if (objc != 2) { 263 | Tcl_WrongNumArgs (interp, 2, objv, ""); 264 | return TCL_ERROR; 265 | } 266 | 267 | if (Tcl_DeleteCommandFromToken (interp, fcd->cmdToken) == TCL_ERROR) { 268 | resultCode = TCL_ERROR; 269 | } 270 | break; 271 | } 272 | 273 | case OPT_STATUS: { 274 | const char *cassErrorCodeString = casstcl_cass_error_to_errorcode_string (cass_future_error_code (fcd->future)); 275 | 276 | Tcl_SetObjResult (interp, Tcl_NewStringObj (cassErrorCodeString, -1)); 277 | break; 278 | } 279 | 280 | case OPT_ERRORMESSAGE: { 281 | CassString cassErrorDesc; 282 | cass_future_error_message (fcd->future, &cassErrorDesc.data, &cassErrorDesc.length); 283 | Tcl_SetStringObj (Tcl_GetObjResult(interp), cassErrorDesc.data, cassErrorDesc.length); 284 | break; 285 | } 286 | } 287 | return resultCode; 288 | } 289 | 290 | 291 | /* 292 | *---------------------------------------------------------------------- 293 | * 294 | * casstcl_iterate_over_future -- 295 | * 296 | * Given a casstcl client data structure, a cassandra cpp-driver 297 | * CassFuture object, the name of an array and a Tcl object 298 | * containing a code body, populate the array with each row in 299 | * the result in turn and execute the code body 300 | * 301 | * Results: 302 | * A standard Tcl result. 303 | * 304 | * Note that it is up to the caller to free the future. 305 | * 306 | * Also note that this code bears way too much in common with casstcl_select 307 | * but it doesn't quite go to call this from that because that uses paging 308 | * through the results 309 | * 310 | *---------------------------------------------------------------------- 311 | */ 312 | int 313 | casstcl_iterate_over_future (casstcl_sessionClientData *ct, CassFuture *future, char *arrayName, Tcl_Obj *codeObj) 314 | { 315 | int tclReturn = TCL_OK; 316 | const CassResult* result = NULL; 317 | CassIterator* iterator; 318 | int rc = cass_future_error_code(future); 319 | 320 | if (rc != CASS_OK) { 321 | casstcl_future_error_to_tcl (ct, rc, future); 322 | cass_future_free (future); 323 | return TCL_ERROR; 324 | } 325 | 326 | /* 327 | * NOTE: Apparently, an asynchronous "connect" has no result 328 | * even when it succeeds. 329 | */ 330 | result = cass_future_get_result(future); 331 | Tcl_Interp *interp = ct->interp; 332 | 333 | if (result == NULL) { 334 | Tcl_ResetResult(interp); 335 | return TCL_OK; 336 | } 337 | iterator = cass_iterator_from_result(result); 338 | 339 | int columnCount = cass_result_column_count (result); 340 | 341 | while (cass_iterator_next(iterator)) { 342 | CassString cassNameString; 343 | int i; 344 | 345 | const CassRow* row = cass_iterator_get_row(iterator); 346 | 347 | // process all the columns into the tcl array 348 | for (i = 0; i < columnCount; i++) { 349 | Tcl_Obj *newObj = NULL; 350 | const char *columnName; 351 | const CassValue *columnValue; 352 | 353 | cass_result_column_name (result, i, &cassNameString.data, &cassNameString.length); 354 | columnName = cassNameString.data; 355 | 356 | columnValue = cass_row_get_column (row, i); 357 | 358 | if (cass_value_is_null (columnValue)) { 359 | Tcl_UnsetVar2 (interp, arrayName, columnName, 0); 360 | continue; 361 | } 362 | 363 | if (casstcl_cass_value_to_tcl_obj (ct, columnValue, &newObj) == TCL_ERROR) { 364 | tclReturn = TCL_ERROR; 365 | break; 366 | } 367 | 368 | if (newObj == NULL) { 369 | Tcl_UnsetVar2 (interp, arrayName, columnName, 0); 370 | } else { 371 | if (Tcl_SetVar2Ex (interp, arrayName, columnName, newObj, (TCL_LEAVE_ERR_MSG)) == NULL) { 372 | tclReturn = TCL_ERROR; 373 | break; 374 | } 375 | } 376 | } 377 | 378 | // now execute the code body (this version of eval does not 379 | // require any reference count management of the object). 380 | // 381 | // as with the other place where we do this, TCL_BREAK and TCL_CONTINUE 382 | // are for us, we want TCL_BREAK to break us out but return TCL_OK 383 | // because our caller isn't to get a break. TCL_RETURN, on the 384 | // other hand, means a return even past our caller, so we pass that 385 | // through. 386 | // 387 | int evalReturnCode = Tcl_EvalObjEx(interp, codeObj, 0); 388 | if ((evalReturnCode != TCL_OK) && (evalReturnCode != TCL_CONTINUE)) { 389 | if (evalReturnCode == TCL_RETURN) { 390 | tclReturn = TCL_RETURN; 391 | } else if (evalReturnCode == TCL_ERROR) { 392 | char msg[60]; 393 | tclReturn = TCL_ERROR; 394 | 395 | sprintf(msg, "\n (\"select\" body line %d)", 396 | Tcl_GetErrorLine(interp)); 397 | Tcl_AddErrorInfo(interp, msg); 398 | } 399 | 400 | break; 401 | } 402 | } 403 | cass_iterator_free(iterator); 404 | return tclReturn; 405 | } 406 | 407 | /* 408 | *-------------------------------------------------------------- 409 | * 410 | * casstcl_futureObjectDelete -- command deletion callback routine. 411 | * 412 | * Results: 413 | * ...destroys the future object. 414 | * ...frees memory. 415 | * 416 | * Side effects: 417 | * None. 418 | * 419 | *-------------------------------------------------------------- 420 | */ 421 | void 422 | casstcl_futureObjectDelete (ClientData clientData) 423 | { 424 | casstcl_futureClientData *fcd = (casstcl_futureClientData *)clientData; 425 | 426 | assert (fcd->cass_future_magic == CASS_FUTURE_MAGIC); 427 | 428 | cass_future_free (fcd->future); 429 | 430 | if (fcd->callbackObj != NULL) { 431 | Tcl_DecrRefCount(fcd->callbackObj); 432 | } 433 | 434 | ckfree((char *)clientData); 435 | } 436 | 437 | /* 438 | *-------------------------------------------------------------- 439 | * 440 | * casstcl_future_command_to_futureClientData -- given a "future" 441 | * command name like future0, find it 442 | * in the interpreter and return a pointer to its future client 443 | * data or NULL 444 | * 445 | * Results: 446 | * 447 | * Side effects: 448 | * None. 449 | * 450 | *-------------------------------------------------------------- 451 | */ 452 | casstcl_futureClientData * 453 | casstcl_future_command_to_futureClientData (Tcl_Interp *interp, char *futureCommandName) 454 | { 455 | Tcl_CmdInfo futureCmdInfo; 456 | 457 | if (!Tcl_GetCommandInfo (interp, futureCommandName, &futureCmdInfo)) { 458 | return NULL; 459 | } 460 | 461 | casstcl_futureClientData *fcd = (casstcl_futureClientData *)futureCmdInfo.objClientData; 462 | if (fcd->cass_future_magic != CASS_FUTURE_MAGIC) { 463 | return NULL; 464 | } 465 | 466 | return fcd; 467 | } 468 | 469 | /* vim: set ts=4 sw=4 sts=4 noet : */ 470 | -------------------------------------------------------------------------------- /generic/casstcl_future.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_future 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *---------------------------------------------------------------------- 13 | * 14 | * casstcl_future_eventProc -- 15 | * 16 | * this routine is called by the Tcl event handler to process callbacks 17 | * we have set up from future (result objects) we've gotten from Cassandra 18 | * 19 | * Results: 20 | * The callback routine set when the async method was invoked is 21 | * invoked in the Tcl interpreter with one argument, that being the 22 | * future object that was also created when the async method was 23 | * invoked 24 | * 25 | * If an uncaught error occurs when evaluating the command, a Tcl 26 | * background exception is invoked 27 | * 28 | *---------------------------------------------------------------------- 29 | */ 30 | int casstcl_future_eventProc (Tcl_Event *tevPtr, int flags); 31 | 32 | /* 33 | *---------------------------------------------------------------------- 34 | * 35 | * casstcl_future_callback -- 36 | * 37 | * this routine is called by the cassandra cpp-driver as a callback 38 | * when a callback was set up by us using cass_future_set_callback 39 | * 40 | * this occurs when the request has completed or errored 41 | * 42 | * this generates a Tcl event and queues it to the thread that issued 43 | * the command to do an asynchronous cassandra command with callback 44 | * in the first place. 45 | * 46 | * when Tcl processes the event, casstcl_future_eventProc will be invoked. 47 | * that guy will do a Tcl eval to invoke the callback 48 | * 49 | * Results: 50 | * stuff 51 | * 52 | *---------------------------------------------------------------------- 53 | */ 54 | void casstcl_future_callback (CassFuture* future, void* data); 55 | 56 | /* 57 | *---------------------------------------------------------------------- 58 | * 59 | * casstcl_createFutureObjectCommand -- 60 | * 61 | * given a casstcl_sessionClientData pointer, a pointer to a 62 | * CassFuture structure and a Tcl callback object (containing a 63 | * function name), this routine creates a Tcl command like 64 | * "future17" that can be invoked with method arguments to access, 65 | * manipulate and destroy cassandra future objects. 66 | * 67 | * Results: 68 | * A standard Tcl result 69 | * 70 | *---------------------------------------------------------------------- 71 | */ 72 | int casstcl_createFutureObjectCommand ( 73 | casstcl_sessionClientData *ct, 74 | CassFuture *future, 75 | Tcl_Obj *callbackObj, 76 | int flags); 77 | 78 | 79 | /* 80 | *---------------------------------------------------------------------- 81 | * 82 | * casstcl_futureObjectObjCmd -- 83 | * 84 | * dispatches the subcommands of a casstcl future-handling command 85 | * 86 | * Results: 87 | * stuff 88 | * 89 | *---------------------------------------------------------------------- 90 | */ 91 | int casstcl_futureObjectObjCmd( 92 | ClientData cData, 93 | Tcl_Interp *interp, 94 | int objc, 95 | Tcl_Obj *CONST objv[]); 96 | 97 | 98 | /* 99 | *---------------------------------------------------------------------- 100 | * 101 | * casstcl_iterate_over_future -- 102 | * 103 | * Given a casstcl client data structure, a cassandra cpp-driver 104 | * CassFuture object, the name of an array and a Tcl object 105 | * containing a code body, populate the array with each row in 106 | * the result in turn and execute the code body 107 | * 108 | * Results: 109 | * A standard Tcl result. 110 | * 111 | * Note that it is up to the caller to free the future. 112 | * 113 | * Also note that this code bears way too much in common with casstcl_select 114 | * but it doesn't quite go to call this from that because that uses paging 115 | * through the results 116 | * 117 | *---------------------------------------------------------------------- 118 | */ 119 | int casstcl_iterate_over_future ( 120 | casstcl_sessionClientData *ct, 121 | CassFuture *future, char *arrayName, 122 | Tcl_Obj *codeObj); 123 | 124 | /* 125 | *-------------------------------------------------------------- 126 | * 127 | * casstcl_futureObjectDelete -- command deletion callback routine. 128 | * 129 | * Results: 130 | * ...destroys the future object. 131 | * ...frees memory. 132 | * 133 | * Side effects: 134 | * None. 135 | * 136 | *-------------------------------------------------------------- 137 | */ 138 | void casstcl_futureObjectDelete (ClientData clientData); 139 | 140 | /* 141 | *-------------------------------------------------------------- 142 | * 143 | * casstcl_future_command_to_futureClientData -- given a "future" 144 | * command name like future0, find it 145 | * in the interpreter and return a pointer to its future client 146 | * data or NULL 147 | * 148 | * Results: 149 | * 150 | * Side effects: 151 | * None. 152 | * 153 | *-------------------------------------------------------------- 154 | */ 155 | casstcl_futureClientData * casstcl_future_command_to_futureClientData (Tcl_Interp *interp, char *futureCommandName); 156 | 157 | /* vim: set ts=4 sw=4 sts=4 noet : */ 158 | -------------------------------------------------------------------------------- /generic/casstcl_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_log - Functions to support custom logging 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_log.h" 13 | 14 | /* 15 | *-------------------------------------------------------------- 16 | * 17 | * casstcl_obj_to_cass_log_level -- lookup a string in a Tcl object 18 | * to be one of the log level strings for CassLogLevel and set 19 | * a pointer to a passed-in CassLogLevel value to the corresponding 20 | * CassLogLevel such as CASS_LOG_CRITICAL, etc 21 | * 22 | * Results: 23 | * ...cass log level gets set 24 | * ...a standard Tcl result is returned 25 | * 26 | * Side effects: 27 | * None. 28 | * 29 | *-------------------------------------------------------------- 30 | */ 31 | int 32 | casstcl_obj_to_cass_log_level (Tcl_Interp *interp, Tcl_Obj *tclObj, CassLogLevel *cassLogLevel) { 33 | int logIndex; 34 | 35 | static CONST char *logLevels[] = { 36 | "disabled", 37 | "critical", 38 | "error", 39 | "warn", 40 | "info", 41 | "debug", 42 | "trace", 43 | NULL 44 | }; 45 | 46 | enum loglevels { 47 | OPT_DISABLED, 48 | OPT_CRITICAL, 49 | OPT_ERROR, 50 | OPT_WARN, 51 | OPT_INFO, 52 | OPT_DEBUG, 53 | OPT_TRACE 54 | }; 55 | 56 | // argument must be one of the options defined above 57 | if (Tcl_GetIndexFromObj (interp, tclObj, logLevels, "logLevel", 58 | TCL_EXACT, &logIndex) != TCL_OK) { 59 | return TCL_ERROR; 60 | } 61 | 62 | switch ((enum loglevels) logIndex) { 63 | case OPT_DISABLED: { 64 | *cassLogLevel = CASS_LOG_DISABLED; 65 | break; 66 | } 67 | 68 | case OPT_CRITICAL: { 69 | *cassLogLevel = CASS_LOG_CRITICAL; 70 | break; 71 | } 72 | 73 | case OPT_ERROR: { 74 | *cassLogLevel = CASS_LOG_ERROR; 75 | break; 76 | } 77 | 78 | case OPT_WARN: { 79 | *cassLogLevel = CASS_LOG_WARN; 80 | break; 81 | } 82 | 83 | case OPT_INFO: { 84 | *cassLogLevel = CASS_LOG_INFO; 85 | break; 86 | } 87 | 88 | case OPT_DEBUG: { 89 | *cassLogLevel = CASS_LOG_DEBUG; 90 | break; 91 | } 92 | 93 | case OPT_TRACE: { 94 | *cassLogLevel = CASS_LOG_TRACE; 95 | break; 96 | } 97 | } 98 | 99 | return TCL_OK; 100 | } 101 | 102 | /* 103 | *-------------------------------------------------------------- 104 | * 105 | * casstcl_cass_log_level_to_string -- given a CassLogLevel value, 106 | * return a const char * to a character string of equivalent 107 | * meaning 108 | * 109 | * Results: 110 | * a string gets returned 111 | * 112 | * Side effects: 113 | * None. 114 | * 115 | *-------------------------------------------------------------- 116 | */ 117 | const char * 118 | casstcl_cass_log_level_to_string (CassLogLevel severity) { 119 | switch (severity) { 120 | case CASS_LOG_DISABLED: 121 | return "disabled"; 122 | 123 | case CASS_LOG_CRITICAL: 124 | return "critical"; 125 | 126 | case CASS_LOG_ERROR: 127 | return "error"; 128 | 129 | case CASS_LOG_WARN: 130 | return "warn"; 131 | 132 | case CASS_LOG_INFO: 133 | return "info"; 134 | 135 | case CASS_LOG_DEBUG: 136 | return "debug"; 137 | 138 | case CASS_LOG_TRACE: 139 | return "trace"; 140 | 141 | default: 142 | return "unknown"; 143 | } 144 | } 145 | 146 | /* vim: set ts=4 sw=4 sts=4 noet : */ 147 | -------------------------------------------------------------------------------- /generic/casstcl_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_log 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | 12 | /* 13 | *-------------------------------------------------------------- 14 | * 15 | * casstcl_obj_to_cass_log_level -- lookup a string in a Tcl object 16 | * to be one of the log level strings for CassLogLevel and set 17 | * a pointer to a passed-in CassLogLevel value to the corresponding 18 | * CassLogLevel such as CASS_LOG_CRITICAL, etc 19 | * 20 | * Results: 21 | * ...cass log level gets set 22 | * ...a standard Tcl result is returned 23 | * 24 | * Side effects: 25 | * None. 26 | * 27 | *-------------------------------------------------------------- 28 | */ 29 | int casstcl_obj_to_cass_log_level (Tcl_Interp *interp, Tcl_Obj *tclObj, CassLogLevel *cassLogLevel); 30 | 31 | 32 | /* 33 | *-------------------------------------------------------------- 34 | * 35 | * casstcl_cass_log_level_to_string -- given a CassLogLevel value, 36 | * return a const char * to a character string of equivalent 37 | * meaning 38 | * 39 | * Results: 40 | * a string gets returned 41 | * 42 | * Side effects: 43 | * None. 44 | * 45 | *-------------------------------------------------------------- 46 | */ 47 | const char * casstcl_cass_log_level_to_string (CassLogLevel severity); 48 | 49 | /* vim: set ts=4 sw=4 sts=4 noet : */ 50 | 51 | -------------------------------------------------------------------------------- /generic/casstcl_prepared.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_prepared - Functions used to handle, make, and delete prepared objects 3 | * 4 | * casstcl - Tcl interface to CassDB 5 | * 6 | * Copyright (C) 2014 FlightAware LLC 7 | * 8 | * freely redistributable under the Berkeley license 9 | */ 10 | 11 | #include "casstcl.h" 12 | #include "casstcl_prepared.h" 13 | #include "casstcl_types.h" 14 | #include "casstcl_consistency.h" 15 | 16 | #include 17 | 18 | /* 19 | *-------------------------------------------------------------- 20 | * 21 | * casstcl_preparedObjectDelete -- command deletion callback routine. 22 | * 23 | * Results: 24 | * ...destroys the prepared object. 25 | * ...frees memory. 26 | * 27 | * Side effects: 28 | * None. 29 | * 30 | *-------------------------------------------------------------- 31 | */ 32 | void 33 | casstcl_preparedObjectDelete (ClientData clientData) 34 | { 35 | casstcl_preparedClientData *pcd = (casstcl_preparedClientData *)clientData; 36 | 37 | assert (pcd->cass_prepared_magic == CASS_PREPARED_MAGIC); 38 | 39 | cass_prepared_free (pcd->prepared); 40 | Tcl_DecrRefCount (pcd->tableNameObj); 41 | ckfree (pcd->string); 42 | ckfree((char *)clientData); 43 | } 44 | 45 | /* 46 | *-------------------------------------------------------------- 47 | * 48 | * casstcl_prepared_command_to_preparedClientData -- given a "prepared" 49 | * command name like prepared0, find it 50 | * in the interpreter and return a pointer to its prepared client 51 | * data or NULL 52 | * 53 | * Results: 54 | * 55 | * Side effects: 56 | * None. 57 | * 58 | *-------------------------------------------------------------- 59 | */ 60 | casstcl_preparedClientData * 61 | casstcl_prepared_command_to_preparedClientData (Tcl_Interp *interp, char *preparedCommandName) 62 | { 63 | Tcl_CmdInfo preparedCmdInfo; 64 | 65 | if (!Tcl_GetCommandInfo (interp, preparedCommandName, &preparedCmdInfo)) { 66 | return NULL; 67 | } 68 | 69 | casstcl_preparedClientData *pcd = (casstcl_preparedClientData *)preparedCmdInfo.objClientData; 70 | if (pcd->cass_prepared_magic != CASS_PREPARED_MAGIC) { 71 | return NULL; 72 | } 73 | 74 | return pcd; 75 | } 76 | 77 | 78 | /* 79 | *---------------------------------------------------------------------- 80 | * 81 | * casstcl_bind_names_from_prepared -- 82 | * 83 | * binds names into a prepared statement 84 | * 85 | * takes a prepared statement client data, a Tcl list of key-value 86 | * pairs and a pointer to a pointer to a cassandra statement 87 | * 88 | * It creates a cassandra statement 89 | * 90 | * Similar to casstcl_bind_names_from_array and casstcl_bind_names_from_list 91 | * 92 | * Works a little differently because it binds the parameters by name 93 | * as specified in the key-value pairs. This capability is only available 94 | * to prepared statements, and makes things a little easier on the 95 | * developer. 96 | * 97 | * Results: 98 | * A standard Tcl result. 99 | * 100 | *---------------------------------------------------------------------- 101 | */ 102 | int 103 | casstcl_bind_names_from_prepared (casstcl_preparedClientData *pcd, int objc, Tcl_Obj *CONST objv[], CassConsistency *consistencyPtr, CassStatement **statementPtr) 104 | { 105 | Tcl_Interp *interp = pcd->ct->interp; 106 | CassStatement *statement = cass_prepared_bind (pcd->prepared); 107 | casstcl_sessionClientData *ct = pcd->ct; 108 | int i; 109 | int masterReturn = TCL_OK; 110 | int tclReturn = TCL_OK; 111 | char *table = Tcl_GetString (pcd->tableNameObj); 112 | 113 | casstcl_cassTypeInfo typeInfo; 114 | 115 | *statementPtr = NULL; 116 | 117 | if (casstcl_setStatementConsistency(ct, statement, consistencyPtr) != TCL_OK) { 118 | return TCL_ERROR; 119 | } 120 | 121 | //printf("objc = %d\n", objc); 122 | for (i = 0; i < objc; i += 2) { 123 | // printf("i = %d, objv[i] = '%s', objc = %d\n", i, Tcl_GetString(objv[i]), objc); 124 | 125 | tclReturn = casstcl_typename_obj_to_cass_value_types (interp, table, objv[i], &typeInfo); 126 | 127 | if (tclReturn == TCL_ERROR) { 128 | //printf ("error from casstcl_bind_names_from_prepared\n"); 129 | masterReturn = TCL_ERROR; 130 | break; 131 | } 132 | 133 | // failed to find it? in this case it's an error 134 | if (tclReturn == TCL_CONTINUE) { 135 | // Tcl_ResetResult (interp); 136 | // Tcl_AppendResult (interp, "couldn't look up data type for column '", Tcl_GetString (objv[i]), "' from table '", table, "'", NULL); 137 | // masterReturn = TCL_ERROR; 138 | // break; 139 | continue; 140 | } 141 | 142 | // get the value out of the list 143 | Tcl_Obj *valueObj = objv[i+1]; 144 | int name_length = 0; 145 | char *name = Tcl_GetStringFromObj (objv[i], &name_length); 146 | 147 | // printf("requesting bind by name for '%s', valueType %d\n", name, typeInfo.cassValueTYpe); 148 | tclReturn = casstcl_bind_tcl_obj (ct, statement, name, name_length, 0, &typeInfo, valueObj); 149 | // printf ("tried to bind arg '%s' as type %d %d %d value '%s'\n", name, typeInfo.cassValueType, typeInfo.valueSubType1, typeInfo.valueSubType2, Tcl_GetString(valueObj)); 150 | if (tclReturn == TCL_ERROR) { 151 | //printf ("error from casstcl_bind_tcl_obj\n"); 152 | Tcl_AppendResult (interp, " while attempting to bind field name of '", name, "' of type '", casstcl_cass_value_type_to_string(typeInfo.cassValueType), "' referencing table '", table, "'", NULL); 153 | masterReturn = TCL_ERROR; 154 | break; 155 | } 156 | } 157 | 158 | //printf("finished the loop, i = %d, objc = %d\n", i, objc); 159 | if (masterReturn == TCL_OK) { 160 | //printf("theoretically got a good statement\n"); 161 | *statementPtr = statement; 162 | } 163 | 164 | //printf("return code is %d\n", masterReturn); 165 | return masterReturn; 166 | } 167 | 168 | /* 169 | *---------------------------------------------------------------------- 170 | * 171 | * casstcl_preparedObjectObjCmd -- 172 | * 173 | * dispatches the subcommands of a casstcl prepared statement-handling 174 | * command 175 | * 176 | * Results: 177 | * stuff 178 | * 179 | *---------------------------------------------------------------------- 180 | */ 181 | int 182 | casstcl_preparedObjectObjCmd(ClientData cData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 183 | { 184 | int optIndex; 185 | casstcl_preparedClientData *pcd = (casstcl_preparedClientData *)cData; 186 | int resultCode = TCL_OK; 187 | 188 | static CONST char *options[] = { 189 | "statement", 190 | "delete", 191 | NULL 192 | }; 193 | 194 | enum options { 195 | OPT_STATEMENT, 196 | OPT_DELETE 197 | }; 198 | 199 | /* basic validation of command line arguments */ 200 | if (objc < 2) { 201 | Tcl_WrongNumArgs (interp, 1, objv, "subcommand ?args?"); 202 | return TCL_ERROR; 203 | } 204 | 205 | if (Tcl_GetIndexFromObj (interp, objv[1], options, "option", TCL_EXACT, &optIndex) != TCL_OK) { 206 | return TCL_ERROR; 207 | } 208 | 209 | switch ((enum options) optIndex) { 210 | case OPT_STATEMENT: { 211 | if (objc != 2) { 212 | Tcl_WrongNumArgs (interp, 2, objv, ""); 213 | return TCL_ERROR; 214 | } 215 | Tcl_SetObjResult (interp, Tcl_NewStringObj (pcd->string, -1)); 216 | 217 | break; 218 | } 219 | case OPT_DELETE: { 220 | if (objc != 2) { 221 | Tcl_WrongNumArgs (interp, 2, objv, ""); 222 | return TCL_ERROR; 223 | } 224 | 225 | if (Tcl_DeleteCommandFromToken (pcd->ct->interp, pcd->cmdToken) == TCL_ERROR) { 226 | resultCode = TCL_ERROR; 227 | } 228 | break; 229 | } 230 | } 231 | return resultCode; 232 | } 233 | 234 | /* vim: set ts=4 sw=4 sts=4 noet : */ 235 | -------------------------------------------------------------------------------- /generic/casstcl_prepared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_prepared 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | /* 12 | *-------------------------------------------------------------- 13 | * 14 | * casstcl_prepared_command_to_preparedClientData -- given a "prepared" 15 | * command name like prepared0, find it 16 | * in the interpreter and return a pointer to its prepared client 17 | * data or NULL 18 | * 19 | * Results: 20 | * 21 | * Side effects: 22 | * None. 23 | * 24 | *-------------------------------------------------------------- 25 | */ 26 | casstcl_preparedClientData * casstcl_prepared_command_to_preparedClientData (Tcl_Interp *interp, char *preparedCommandName); 27 | 28 | /* 29 | *-------------------------------------------------------------- 30 | * 31 | * casstcl_preparedObjectDelete -- command deletion callback routine. 32 | * 33 | * Results: 34 | * ...destroys the prepared object. 35 | * ...frees memory. 36 | * 37 | * Side effects: 38 | * None. 39 | * 40 | *-------------------------------------------------------------- 41 | */ 42 | void casstcl_preparedObjectDelete (ClientData clientData); 43 | 44 | 45 | /* 46 | *---------------------------------------------------------------------- 47 | * 48 | * casstcl_bind_names_from_prepared -- 49 | * 50 | * binds names into a prepared statement 51 | * 52 | * takes a prepared statement client data, a Tcl list of key-value 53 | * pairs and a pointer to a pointer to a cassandra statement 54 | * 55 | * It creates a cassandra statement 56 | * 57 | * Similar to casstcl_bind_names_from_array and casstcl_bind_names_from_list 58 | * 59 | * Works a little differently because it binds the parameters by name 60 | * as specified in the key-value pairs. This capability is only available 61 | * to prepared statements, and makes things a little easier on the 62 | * developer. 63 | * 64 | * Results: 65 | * A standard Tcl result. 66 | * 67 | *---------------------------------------------------------------------- 68 | */ 69 | int casstcl_bind_names_from_prepared ( 70 | casstcl_preparedClientData *pcd, 71 | int objc, 72 | Tcl_Obj *CONST objv[], 73 | CassConsistency *consistencyPtr, 74 | CassStatement **statementPtr); 75 | /* 76 | *---------------------------------------------------------------------- 77 | * 78 | * casstcl_preparedObjectObjCmd -- 79 | * 80 | * dispatches the subcommands of a casstcl prepared statement-handling 81 | * command 82 | * 83 | * Results: 84 | * stuff 85 | * 86 | *---------------------------------------------------------------------- 87 | */ 88 | int casstcl_preparedObjectObjCmd(ClientData cData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); 89 | 90 | /* vim: set ts=4 sw=4 sts=4 noet : */ 91 | -------------------------------------------------------------------------------- /generic/casstcl_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Include file for casstcl_types 4 | * 5 | * Copyright (C) 2015 by FlightAware, All Rights Reserved 6 | * 7 | * Freely redistributable under the Berkeley copyright, see license.terms 8 | * for details. 9 | */ 10 | 11 | 12 | /* 13 | *-------------------------------------------------------------- 14 | * 15 | * casstcl_cass_value_type_to_string -- given a CassValueType, 16 | * return a const char * to a character string of equivalent 17 | * meaning 18 | * 19 | * Results: 20 | * a string gets returned 21 | * 22 | * Side effects: 23 | * None. 24 | * 25 | *-------------------------------------------------------------- 26 | */ 27 | const char * 28 | casstcl_cass_value_type_to_string (CassValueType valueType); 29 | 30 | 31 | /* 32 | *-------------------------------------------------------------- 33 | * 34 | * casstcl_string_to_cass_value_type -- lookup a string 35 | * to be one of the cass value type strings for CassValueType and set 36 | * a pointer to a passed-in CassValueType value to the corresponding 37 | * type such as CASS_VALUE_TYPE_DOUBLE, etc 38 | * 39 | * Results: 40 | * ...cass value type gets returned 41 | * CASS_VALUE_TYPE_UNKNOWN is returned if nothing matches 42 | * 43 | * Side effects: 44 | * None. 45 | * 46 | *-------------------------------------------------------------- 47 | */ 48 | CassValueType 49 | casstcl_string_to_cass_value_type (char *string); 50 | 51 | /* 52 | *-------------------------------------------------------------- 53 | * 54 | * casstcl_obj_to_compound_cass_value_types 55 | * 56 | * Lookup a string from a Tcl object and identify it as one of the cass 57 | * value type strings for CassValueType (int, text uuid, etc.) and set 58 | * a pointer to a passed-in CassValueType value to the corresponding 59 | * type such as CASS_VALUE_TYPE_DOUBLE, etc 60 | * 61 | * Also if it is a list of "map type type" or "set type" or "list type" 62 | * then set the valueSubType1 to type defined by the set or list and 63 | * in the case of a map set valueSubType1 for the key datatype and 64 | * valueSubType2 for the value datatype 65 | * 66 | * Results: 67 | * ...cass value types gets set 68 | * ...a standard Tcl result is returned 69 | * 70 | * Side effects: 71 | * None. 72 | * 73 | *-------------------------------------------------------------- 74 | */ 75 | int 76 | casstcl_obj_to_compound_cass_value_types (Tcl_Interp *interp, Tcl_Obj *tclObj, casstcl_cassTypeInfo *typeInfo); 77 | 78 | /* 79 | *---------------------------------------------------------------------- 80 | * 81 | * casstcl_InitCassBytesFromBignum -- 82 | * 83 | * Allocate and initialize a CassBytes from a 'bignum'. 84 | * 85 | * Results: 86 | * A standard Tcl result. 87 | * 88 | * Side effects: 89 | * None. 90 | * 91 | *---------------------------------------------------------------------- 92 | */ 93 | 94 | int 95 | casstcl_InitCassBytesFromBignum( 96 | Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 97 | CassBytes *v, /* CassBytes to initialize */ 98 | mp_int *a); /* Initial value */ 99 | 100 | /* 101 | *---------------------------------------------------------------------- 102 | * 103 | * casstcl_GetTimestampFromObj -- 104 | * 105 | * Accepts a Tcl object value that specifies a whole number of 106 | * seconds and optionally a fractional number of seconds, and 107 | * converts the value to the whole number of milliseconds. 108 | * 109 | * Results: 110 | * A standard Tcl result. 111 | * 112 | * Side effects: 113 | * None. 114 | * 115 | *---------------------------------------------------------------------- 116 | */ 117 | 118 | int 119 | casstcl_GetTimestampFromObj( 120 | Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 121 | Tcl_Obj *objPtr, /* Object from which to get milliseconds. */ 122 | cass_int64_t *milliseconds); /* Place to store whole milliseconds. */ 123 | 124 | 125 | /* 126 | *---------------------------------------------------------------------- 127 | * 128 | * casstcl_NewTimestampObj -- 129 | * 130 | * Accepts a Cassandra 'timestamp' value, in milliseconds, and 131 | * creates a Tcl object based on it. If the milliseconds is 132 | * evenly divisible by 1000, a Tcl wide integer object will be 133 | * returned, containing the exact number of seconds. Otherwise, 134 | * a Tcl double object will be returned with an approximate value, 135 | * where the fractional portion of the double will represent the 136 | * milliseconds and the whole portion will represent the number 137 | * of seconds. 138 | * 139 | * Results: 140 | * The newly created Tcl object, having a reference count of zero. 141 | * 142 | * Side effects: 143 | * None. 144 | * 145 | *---------------------------------------------------------------------- 146 | */ 147 | 148 | Tcl_Obj *casstcl_NewTimestampObj(cass_int64_t milliseconds); 149 | 150 | /* 151 | *---------------------------------------------------------------------- 152 | * 153 | * mp_read_unsigned_bin -- 154 | * 155 | * Read a binary encoded 'bignum' from the specified buffer. It 156 | * must have been initialized first. This routine was borrowed 157 | * directly from the Tcl 8.6 source code (i.e. because we needed 158 | * it and it was not available as an export). 159 | * 160 | * Results: 161 | * A standard LibTomMath result. 162 | * 163 | * Side effects: 164 | * None. 165 | * 166 | *---------------------------------------------------------------------- 167 | */ 168 | 169 | int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c); 170 | 171 | /* 172 | *---------------------------------------------------------------------- 173 | * 174 | * casstcl_InitBignumFromCassBytes -- 175 | * 176 | * Allocate and initialize a 'bignum' from a CassBytes. 177 | * 178 | * Results: 179 | * A standard Tcl result. 180 | * 181 | * Side effects: 182 | * None. 183 | * 184 | *---------------------------------------------------------------------- 185 | */ 186 | 187 | int 188 | casstcl_InitBignumFromCassBytes( 189 | Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 190 | mp_int *a, /* Bignum to initialize */ 191 | CassBytes *v); /* Initial value */ 192 | 193 | /* 194 | *---------------------------------------------------------------------- 195 | * 196 | * casstcl_bind_names_from_array -- 197 | * 198 | * Now this little ditty takes an array name and a query and an objv 199 | * and a pointer to a pointer to a cassandra statement 200 | * 201 | * It creates a cassandra statement 202 | * 203 | * It then iterates through the objv as a list of column names 204 | * 205 | * It fetches the data type of the column from the column-datatype cache 206 | * 207 | * It fetches the value from the array and converts it and binds it 208 | * to the statement 209 | * 210 | * This requires that the table name and keyspace be known 211 | * 212 | * If the data type is a list or set then the corresponding Tcl object 213 | * is converted to a list of that type. 214 | * 215 | * If the data type is a map then the corresponding Tcl object is converted 216 | * to a map of alternating key-value pairs of the two specified types. 217 | * 218 | * If the objv is empty the statement is created with nothing bound. 219 | * It's probably fine if that happens. 220 | * 221 | * Results: 222 | * A standard Tcl result. 223 | * 224 | *---------------------------------------------------------------------- 225 | */ 226 | int casstcl_bind_names_from_array ( 227 | casstcl_sessionClientData *ct, 228 | char *table, 229 | char *query, 230 | char *tclArray, 231 | int objc, 232 | Tcl_Obj *CONST objv[], 233 | CassConsistency *consistencyPtr, 234 | CassStatement **statementPtr); 235 | 236 | /* 237 | *---------------------------------------------------------------------- 238 | * 239 | * casstcl_bind_names_from_list -- 240 | * 241 | * fully qualified table name 242 | * name of the table 243 | * and a pointer to a pointer to a cassandra statement 244 | * 245 | * It creates a cassandra statement 246 | * 247 | * Similar to casstcl_bind_names_from_array 248 | * 249 | * Results: 250 | * A standard Tcl result. 251 | * 252 | *---------------------------------------------------------------------- 253 | */ 254 | int 255 | casstcl_bind_names_from_list ( 256 | casstcl_sessionClientData *ct, 257 | char *table, 258 | char *query, 259 | int objc, 260 | Tcl_Obj *CONST objv[], 261 | CassConsistency *consistencyPtr, 262 | CassStatement **statementPtr); 263 | 264 | 265 | /* 266 | *---------------------------------------------------------------------- 267 | * 268 | * casstcl_typename_obj_to_cass_value_types -- 269 | * 270 | * Look up the validator in the column type map and decode it into 271 | * three CassValueType entries, with extremely fast caching 272 | * 273 | * Results: 274 | * A standard Tcl result. 275 | * 276 | * TCL_CONTINUE is returned if the type index name isn't found. 277 | * Also in that case, the value types are set to CASS_VALUE_TYPE_UNKNOWN. 278 | * 279 | *---------------------------------------------------------------------- 280 | */ 281 | int casstcl_typename_obj_to_cass_value_types ( 282 | Tcl_Interp *interp, 283 | char *table, 284 | Tcl_Obj *typenameObj, 285 | casstcl_cassTypeInfo *typeInfoPtr); 286 | 287 | 288 | /* 289 | *---------------------------------------------------------------------- 290 | * 291 | * casstcl_cass_value_to_tcl_obj -- 292 | * 293 | * Given a cassandra CassValue, generate a Tcl_Obj of a corresponding 294 | * type 295 | * 296 | * This is a vital routine to the entire edifice. 297 | * 298 | * Results: 299 | * A standard Tcl result. 300 | * 301 | * 302 | *---------------------------------------------------------------------- 303 | */ 304 | 305 | int casstcl_cass_value_to_tcl_obj ( 306 | casstcl_sessionClientData *ct, 307 | const CassValue *cassValue, 308 | Tcl_Obj **tclObj); 309 | 310 | /* 311 | *---------------------------------------------------------------------- 312 | * 313 | * casstcl_append_tcl_obj_to_collection 314 | * 315 | * Convert a Tcl object to a cassandra value of the specified type and 316 | * append it to the specified collection 317 | * 318 | * This is used for constructing cassandra maps, sets and lists. 319 | * 320 | * You create a set or a list by appending elements to it. 321 | * 322 | * You create a map by appending successions of key elements and value 323 | * elements to it. 324 | * 325 | * They have a specified datatype for sets and lists; for keys there is 326 | * one for the key and one for the value so for instance the keys can 327 | * be integers and the values can be strings or whatever. 328 | * 329 | * Results: 330 | * A standard Tcl result. 331 | * 332 | * 333 | *---------------------------------------------------------------------- 334 | */ 335 | int casstcl_append_tcl_obj_to_collection ( 336 | casstcl_sessionClientData *ct, 337 | CassCollection *collection, 338 | CassValueType valueType, 339 | Tcl_Obj *obj); 340 | /* 341 | *---------------------------------------------------------------------- 342 | * 343 | * casstcl_bind_tcl_obj -- 344 | * 345 | * This routine binds Tcl objects to ?-substitution parameters in nascent 346 | * cassandra statements. 347 | * 348 | * It takes a statement, an index (which parameter to substitute left to 349 | * right from 0 to n-1), the cassandra data type (and subtype(s) if it is 350 | * a list, set or map), and it will convert the Tcl object to the specified 351 | * data type and bind it to the statement. 352 | * 353 | * This is a really important routine. 354 | * 355 | * If type conversion fails, like Cassandra wants floating point and the 356 | * Tcl object won't convert to floating point then it's a Tcl error. 357 | * 358 | * If everything works then TCL_OK is returned. 359 | * 360 | * valueSubType1 is only used for maps, sets and lists 361 | * valueSubType2 is only used for maps 362 | * 363 | * Results: 364 | * A standard Tcl result. 365 | * 366 | * 367 | *---------------------------------------------------------------------- 368 | */ 369 | 370 | int casstcl_bind_tcl_obj ( 371 | casstcl_sessionClientData *ct, 372 | CassStatement *statement, 373 | char *name, 374 | int name_length, 375 | cass_size_t index, 376 | casstcl_cassTypeInfo *typeInfo, 377 | Tcl_Obj *obj); 378 | 379 | /* 380 | *---------------------------------------------------------------------- 381 | * 382 | * casstcl_bind_values_and_types -- 383 | * 384 | * Now this little ditty takes a query and an objv and a pointer to 385 | * a pointer to a cassandra statement 386 | * 387 | * It creates a cassandra statement 388 | * 389 | * It then iterates through the objv as a list pairs where the first element 390 | * is a value and the second element is a cassandra data type 391 | * 392 | * For each pair it interprets the data type, converts the element to 393 | * that type, and binds it to that position in the statement. 394 | * 395 | * If the data type is a list or set then the corresponding Tcl object 396 | * is converted to a list of that type. 397 | * 398 | * If the data type is a map then the corresponding Tcl object is converted 399 | * to a map of alternating key-value pairs of the two specified types. 400 | * 401 | * If the objv is empty the statement is created with nothing bound. 402 | * It's probably fine if that happens. 403 | * 404 | * Results: 405 | * A standard Tcl result. 406 | * 407 | *---------------------------------------------------------------------- 408 | */ 409 | int 410 | casstcl_bind_values_and_types ( 411 | casstcl_sessionClientData *ct, 412 | char *query, 413 | int objc, 414 | Tcl_Obj *CONST objv[], 415 | CassConsistency *consistencyPtr, 416 | CassStatement **statementPtr); 417 | 418 | 419 | /* 420 | *---------------------------------------------------------------------- 421 | * 422 | * casstcl_GetInetFromObj -- 423 | * 424 | * Attempt to return an Inet from the Tcl object "objPtr". 425 | * 426 | * Results: 427 | * A standard Tcl result. 428 | * 429 | * Side effects: 430 | * None. 431 | * 432 | *---------------------------------------------------------------------- 433 | */ 434 | 435 | int 436 | casstcl_GetInetFromObj( 437 | Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 438 | Tcl_Obj *objPtr, /* The object from which to get an Inet. */ 439 | CassInet *inetPtr); /* Place to store resulting Inet. */ 440 | 441 | /* 442 | *---------------------------------------------------------------------- 443 | * 444 | * casstcl_GetDurationFromObj -- 445 | * 446 | * Attempt to convert a string in one of the formats Cassandra expects 447 | * as a duration into months, days, and nanoseconds 448 | * 449 | * Results: 450 | * A standard Tcl result. 451 | * 452 | * Side Effects: 453 | * 454 | * None. 455 | * 456 | *---------------------------------------------------------------------- 457 | */ 458 | int 459 | casstcl_GetDurationFromObj( 460 | Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 461 | Tcl_Obj *objPtr, /* The object from which to get an Inet. */ 462 | cass_int32_t *monthp, /* resulting months. */ 463 | cass_int32_t *dayp, /* resulting days. */ 464 | cass_int64_t *nanop); /* Resulting nanoseconds. */ 465 | 466 | /* vim: set ts=4 sw=4 sts=4 noet : */ 467 | -------------------------------------------------------------------------------- /generic/tclcasstcl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * casstcl_Init and casstcl_SafeInit 3 | * 4 | * Copyright (C) 2015 FlightAware 5 | * 6 | * Freely redistributable under the Berkeley copyright. See license.terms 7 | * for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include "casstcl.h" 13 | 14 | #undef TCL_STORAGE_CLASS 15 | #define TCL_STORAGE_CLASS DLLEXPORT 16 | 17 | 18 | /* 19 | *---------------------------------------------------------------------- 20 | * 21 | * Casstcl_Init -- 22 | * 23 | * Initialize the casstcl extension. The string "casstcl" 24 | * in the function name must match the PACKAGE declaration at the top of 25 | * configure.in. 26 | * 27 | * Results: 28 | * A standard Tcl result 29 | * 30 | * Side effects: 31 | * One new command "casstcl" is added to the Tcl interpreter. 32 | * 33 | *---------------------------------------------------------------------- 34 | */ 35 | 36 | EXTERN int 37 | Casstcl_Init(Tcl_Interp *interp) 38 | { 39 | Tcl_Namespace *namespace; 40 | /* 41 | * This may work with 8.0, but we are using strictly stubs here, 42 | * which requires 8.1. 43 | */ 44 | if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { 45 | return TCL_ERROR; 46 | } 47 | 48 | if (Tcl_TomMath_InitStubs(interp, "8.5") == NULL) { 49 | return TCL_ERROR; 50 | } 51 | 52 | if (Tcl_PkgRequire(interp, "Tcl", "8.1", 0) == NULL) { 53 | return TCL_ERROR; 54 | } 55 | 56 | if (Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION) != TCL_OK) { 57 | return TCL_ERROR; 58 | } 59 | 60 | Tcl_RegisterObjType(&casstcl_cassTypeTclType); 61 | 62 | namespace = Tcl_CreateNamespace (interp, "::casstcl", NULL, NULL); 63 | 64 | /* Create the create command */ 65 | Tcl_CreateObjCommand(interp, "::casstcl::cass", (Tcl_ObjCmdProc *) casstcl_cassObjCmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); 66 | 67 | Tcl_Export (interp, namespace, "*", 0); 68 | 69 | return TCL_OK; 70 | } 71 | 72 | 73 | /* 74 | *---------------------------------------------------------------------- 75 | * 76 | * casstcl_SafeInit -- 77 | * 78 | * Initialize the casstcl in a safe interpreter. 79 | * 80 | * Results: 81 | * A standard Tcl result 82 | * 83 | * Side effects: 84 | * One new command "casstcl" is added to the Tcl interpreter. 85 | * 86 | *---------------------------------------------------------------------- 87 | */ 88 | 89 | EXTERN int 90 | Casstcl_SafeInit(Tcl_Interp *interp) 91 | { 92 | /* 93 | * can this work safely? seems too dangerous... 94 | */ 95 | return TCL_ERROR; 96 | } 97 | 98 | /* vim: set ts=4 sw=4 sts=4 noet : */ 99 | -------------------------------------------------------------------------------- /pkgIndex.tcl.in: -------------------------------------------------------------------------------- 1 | # 2 | # Tcl package index file for casstcl @PACKAGE_VERSION@ 3 | # 4 | package ifneeded @PACKAGE_NAME@ @PACKAGE_VERSION@ \ 5 | [list load [file join $dir @PKG_LIB_FILE@]]\n[list source [file join $dir casstcl.tcl]] 6 | -------------------------------------------------------------------------------- /tclconfig/README.txt: -------------------------------------------------------------------------------- 1 | These files comprise the basic building blocks for a Tcl Extension 2 | Architecture (TEA) extension. For more information on TEA see: 3 | 4 | http://www.tcl.tk/doc/tea/ 5 | 6 | This package is part of the Tcl project at SourceForge, and latest 7 | sources should be available there: 8 | 9 | http://tcl.sourceforge.net/ 10 | 11 | This package is a freely available open source package. You can do 12 | virtually anything you like with it, such as modifying it, redistributing 13 | it, and selling it either in whole or in part. 14 | 15 | CONTENTS 16 | ======== 17 | The following is a short description of the files you will find in 18 | the sample extension. 19 | 20 | README.txt This file 21 | 22 | install-sh Program used for copying binaries and script files 23 | to their install locations. 24 | 25 | tcl.m4 Collection of Tcl autoconf macros. Included by a package's 26 | aclocal.m4 to define TEA_* macros. 27 | -------------------------------------------------------------------------------- /tclconfig/install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # install - install a program, script, or datafile 5 | # This comes from X11R5; it is not part of GNU. 6 | # 7 | # $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ 8 | # 9 | # This script is compatible with the BSD install script, but was written 10 | # from scratch. 11 | # 12 | 13 | 14 | # set DOITPROG to echo to test this script 15 | 16 | # Don't use :- since 4.3BSD and earlier shells don't like it. 17 | doit="${DOITPROG-}" 18 | 19 | 20 | # put in absolute paths if you don't have them in your path; or use env. vars. 21 | 22 | mvprog="${MVPROG-mv}" 23 | cpprog="${CPPROG-cp}" 24 | chmodprog="${CHMODPROG-chmod}" 25 | chownprog="${CHOWNPROG-chown}" 26 | chgrpprog="${CHGRPPROG-chgrp}" 27 | stripprog="${STRIPPROG-strip}" 28 | rmprog="${RMPROG-rm}" 29 | 30 | instcmd="$mvprog" 31 | chmodcmd="" 32 | chowncmd="" 33 | chgrpcmd="" 34 | stripcmd="" 35 | rmcmd="$rmprog -f" 36 | mvcmd="$mvprog" 37 | src="" 38 | dst="" 39 | 40 | while [ x"$1" != x ]; do 41 | case $1 in 42 | -c) instcmd="$cpprog" 43 | shift 44 | continue;; 45 | 46 | -m) chmodcmd="$chmodprog $2" 47 | shift 48 | shift 49 | continue;; 50 | 51 | -o) chowncmd="$chownprog $2" 52 | shift 53 | shift 54 | continue;; 55 | 56 | -g) chgrpcmd="$chgrpprog $2" 57 | shift 58 | shift 59 | continue;; 60 | 61 | -s) stripcmd="$stripprog" 62 | shift 63 | continue;; 64 | 65 | *) if [ x"$src" = x ] 66 | then 67 | src=$1 68 | else 69 | dst=$1 70 | fi 71 | shift 72 | continue;; 73 | esac 74 | done 75 | 76 | if [ x"$src" = x ] 77 | then 78 | echo "install: no input file specified" 79 | exit 1 80 | fi 81 | 82 | if [ x"$dst" = x ] 83 | then 84 | echo "install: no destination specified" 85 | exit 1 86 | fi 87 | 88 | 89 | # If destination is a directory, append the input filename; if your system 90 | # does not like double slashes in filenames, you may need to add some logic 91 | 92 | if [ -d $dst ] 93 | then 94 | dst="$dst"/`basename $src` 95 | fi 96 | 97 | # Make a temp file name in the proper directory. 98 | 99 | dstdir=`dirname $dst` 100 | dsttmp=$dstdir/#inst.$$# 101 | 102 | # Move or copy the file name to the temp name 103 | 104 | $doit $instcmd $src $dsttmp 105 | 106 | # and set any options; do chmod last to preserve setuid bits 107 | 108 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi 109 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi 110 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi 111 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi 112 | 113 | # Now rename the file to the real destination. 114 | 115 | $doit $rmcmd $dst 116 | $doit $mvcmd $dsttmp $dst 117 | 118 | 119 | exit 0 120 | -------------------------------------------------------------------------------- /tests/all.tcl: -------------------------------------------------------------------------------- 1 | # all.tcl -- 2 | # 3 | # This file contains a top-level script to run all of the Tcl 4 | # tests. Execute it by invoking "source all.test" when running tcltest 5 | # in this directory. 6 | # 7 | # Copyright (c) 1998-1999 by Scriptics Corporation. 8 | # Copyright (c) 2000 by Ajuba Solutions 9 | # 10 | # See the file "license.terms" for information on usage and redistribution 11 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 | 13 | set tcltestVersion [package require tcltest] 14 | namespace import -force tcltest::* 15 | 16 | # tcltest::configure -verbose {body pass skip error} 17 | 18 | tcltest::testsDirectory [file dir [info script]] 19 | tcltest::runAllTests 20 | 21 | return 22 | -------------------------------------------------------------------------------- /update_ver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script simplifies the process of incrementing all version numbers for a new release. 4 | 5 | # make distclean *before* updating the version to make sure all the old version built files are gone. 6 | make distclean 7 | 8 | NEWVER="2.14.0" 9 | 10 | perl -p -i -e "s/^(AC_INIT\\(\\[[a-z_]+\\],) \\[[0-9.]+\\]/\\1 \\[$NEWVER\\]/" configure.in 11 | 12 | perl -p -i -e "s/^(\*Version) [0-9.]+(\*)/\\1 $NEWVER\\2/" README.md 13 | 14 | --------------------------------------------------------------------------------